use super::node::{FROZEN, Node, is_frozen, is_held, is_marked, ptr, with, word};
use super::table::TableRef;
use super::{HashMap, MIN_CAPACITY};
use crate::MaybeSend;
use crate::sync::spin_hint;
use core::hash::{BuildHasher, Hash};
use core::sync::atomic::Ordering;
use kovan::{Atomic, Guard, Shared, alloc_node, pin, retire_node};
extern crate alloc;
impl<K, V, S> HashMap<K, V, S>
where
K: Hash + Eq + Clone + MaybeSend + 'static,
V: Clone + MaybeSend + '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 undo = Undo {
old,
new,
guard: &guard,
latch: &self.resizing,
};
let copied = Self::migrate(old, Some(new), &guard);
core::mem::forget(undo);
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;
}
if is_held(w) {
spin_hint();
continue;
}
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(alloc_node(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_node(old.take_proxy()) };
}
}
struct Undo<'a, K: 'static, V: 'static> {
old: TableRef<K, V>,
new: TableRef<K, V>,
guard: &'a Guard,
latch: &'a crate::sync::AtomicBool,
}
impl<K: 'static, V: 'static> Drop for Undo<'_, K, V> {
fn drop(&mut self) {
thaw_links(self.old, self.guard);
unsafe { self.new.free() };
self.latch.store(false, Ordering::Release);
}
}
fn thaw_links<K: 'static, V: 'static>(table: TableRef<K, V>, guard: &Guard) {
for b in 0..table.capacity() {
let mut link = table.bucket(b);
loop {
let w = link.load(Ordering::Acquire, guard).as_raw();
if !is_frozen(w) {
break;
}
let thawed = w.map_addr(|a| a & !FROZEN);
let _ = link.compare_exchange(
word(w),
word(thawed),
Ordering::AcqRel,
Ordering::Relaxed,
guard,
);
let cur = ptr(w);
if cur.is_null() {
break;
}
link = unsafe { &(*cur).next };
}
}
}