use super::node::{FROZEN, Node, is_frozen, is_marked, ptr, with, word};
use super::table::TableRef;
use super::{HashMap, MIN_CAPACITY};
use crate::sync::spin_hint;
use alloc::boxed::Box;
use core::hash::{BuildHasher, Hash};
use core::sync::atomic::Ordering;
use kovan::{Atomic, Guard, Shared, pin, retire};
extern crate alloc;
impl<K, V, S> HashMap<K, V, S>
where
K: Hash + Eq + Clone + 'static,
V: Clone + 'static,
S: BuildHasher,
{
#[inline]
pub(super) fn wait_for_resize(&self) {
while self.resizing.load(Ordering::Acquire) {
spin_hint();
}
}
pub(super) fn try_resize(&self, new_capacity: usize) {
if self
.resizing
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
return;
}
let new_capacity = new_capacity
.next_power_of_two()
.max(MIN_CAPACITY)
.max(self.floor);
let guard = pin();
let old = self.table_ref(&guard);
if old.capacity() != new_capacity {
let new = TableRef::alloc(new_capacity);
let copied = Self::migrate(old, Some(new), &guard);
new.count().store(copied as isize, Ordering::Relaxed);
self.publish(old, new);
}
drop(guard);
self.resizing.store(false, Ordering::Release);
}
pub fn clear(&self) {
while self
.resizing
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
spin_hint();
}
let guard = pin();
let old = self.table_ref(&guard);
Self::migrate(old, None, &guard);
self.publish(old, TableRef::alloc(old.capacity()));
drop(guard);
self.resizing.store(false, Ordering::Release);
}
fn freeze(link: &Atomic<Node<K, V>>, guard: &Guard) -> *mut Node<K, V> {
loop {
let w = link.load(Ordering::Acquire, guard).as_raw();
if is_frozen(w) {
return w;
}
let frozen = with(w, FROZEN);
if link
.compare_exchange(
word(w),
word(frozen),
Ordering::AcqRel,
Ordering::Relaxed,
guard,
)
.is_ok()
{
return frozen;
}
}
}
fn migrate(old: TableRef<K, V>, into: Option<TableRef<K, V>>, guard: &Guard) -> usize {
let mut copied = 0;
for b in 0..old.capacity() {
let mut w = Self::freeze(old.bucket(b), guard);
loop {
let cur = ptr(w);
if cur.is_null() {
break;
}
let node = unsafe { &*cur };
w = Self::freeze(&node.next, guard);
if is_marked(w) {
continue;
}
if let Some(new) = into {
let dst = new.bucket(new.bucket_index(node.hash));
let copy = Node::new(node.hash, node.key.clone(), node.value.clone());
copy.next
.store(dst.load(Ordering::Relaxed, guard), Ordering::Relaxed);
dst.store(word(Box::into_raw(Box::new(copy))), Ordering::Relaxed);
copied += 1;
}
}
}
copied
}
fn publish(&self, old: TableRef<K, V>, new: TableRef<K, V>) {
self.table
.store(unsafe { Shared::from_raw(new.as_raw()) }, Ordering::Release);
unsafe { retire(old.take_proxy()) };
}
}