diff --git a/Cargo.toml b/Cargo.toml index db4dd2f..8632951 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ num_cpus = "1.12.0" rayon = {version = "1.3", optional = true} serde = {version = "1.0.105", optional = true} seize = "0.2.1" +fast-counter = "1.0.0" [dependencies.ahash] version = "0.7.6" diff --git a/src/map.rs b/src/map.rs index 0fb1ffc..3324cc2 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,4 +1,5 @@ use seize::Linked; +use fast_counter::ConcurrentCounter; use crate::iter::*; use crate::node::*; @@ -93,7 +94,7 @@ pub struct HashMap { /// The next table index (plus one) to split while resizing. transfer_index: AtomicIsize, - count: AtomicIsize, + counter: ConcurrentCounter, /// Table initialization and resizing control. When negative, the /// table is being initialized or resized: -1 for initialization, @@ -280,7 +281,7 @@ impl HashMap { table: Atomic::null(), next_table: Atomic::null(), transfer_index: AtomicIsize::new(0), - count: AtomicIsize::new(0), + counter: ConcurrentCounter::new(num_cpus()), size_ctl: AtomicIsize::new(0), build_hasher: hash_builder, collector: Collector::new(), @@ -349,6 +350,12 @@ impl HashMap { /// Returns the number of entries in the map. /// + /// Note that the returned value is _NOT_ an + /// atomic snapshot; invocation in the absence of concurrent + /// updates returns an accurate result, but concurrent updates that + /// occur while the sum is being calculated might not be + /// incorporated. + /// /// # Examples /// /// ``` @@ -361,7 +368,7 @@ impl HashMap { /// assert!(map.pin().len() == 2); /// ``` pub fn len(&self) -> usize { - let n = self.count.load(Ordering::Relaxed); + let n = self.counter.sum(); if n < 0 { 0 } else { @@ -1141,23 +1148,19 @@ where } fn add_count(&self, n: isize, resize_hint: Option, guard: &Guard<'_>) { - // TODO: implement the Java CounterCell business here - - use std::cmp; - let mut count = match n.cmp(&0) { - cmp::Ordering::Greater => self.count.fetch_add(n, Ordering::SeqCst) + n, - cmp::Ordering::Less => self.count.fetch_sub(n.abs(), Ordering::SeqCst) - n, - cmp::Ordering::Equal => self.count.load(Ordering::SeqCst), - }; + if n != 0 { + self.counter.add(n); + } // if resize_hint is None, it means the caller does not want us to consider a resize. // if it is Some(n), the caller saw n entries in a bin - if resize_hint.is_none() { - return; - } - // TODO: use the resize hint - let _saw_bin_length = resize_hint.unwrap(); + let _saw_bin_length = match resize_hint { + Some(hint) => hint, + None => return + }; + + let mut count = self.counter.sum(); loop { let sc = self.size_ctl.load(Ordering::SeqCst); @@ -1217,7 +1220,7 @@ where } // another resize may be needed! - count = self.count.load(Ordering::SeqCst); + count = self.counter.sum(); } } diff --git a/src/node.rs b/src/node.rs index 36ed822..ff9a957 100644 --- a/src/node.rs +++ b/src/node.rs @@ -938,7 +938,7 @@ impl TreeBin { guard: &'g Guard<'_>, ) { guard.retire(bin.as_ptr(), |mut link| { - let bin = unsafe { + let bin = { // SAFETY: `bin` is a `BinEntry` let ptr = link.cast::>(); // SAFETY: `retire` guarantees that we