diff --git a/.github/workflows/safety.yml b/.github/workflows/safety.yml index 465c5d8..2340bb2 100644 --- a/.github/workflows/safety.yml +++ b/.github/workflows/safety.yml @@ -68,4 +68,4 @@ jobs: - name: cargo miri test run: cargo miri test env: - MIRIFLAGS: "-Zmiri-tag-raw-pointers -Zmiri-disable-isolation" + MIRIFLAGS: "-Zmiri-disable-isolation" diff --git a/Cargo.lock b/Cargo.lock index 9fff9c1..5082cdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,13 +534,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "seize" -version = "0.2.5" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e5739de653b129b0a59da381599cf17caf24bc586f6a797c52d3d6147c5b85a" -dependencies = [ - "num_cpus", - "once_cell", -] +checksum = "689224d06523904ebcc9b482c6a3f4f7fb396096645c4cd10c0d2ff7371a34d3" [[package]] name = "serde" diff --git a/Cargo.toml b/Cargo.toml index 497b7ec..fed6e93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ parking_lot = "0.12" num_cpus = "1.12.0" rayon = {version = "1.3", optional = true} serde = {version = "1.0.105", optional = true} -seize = "0.2.1" +seize = "0.3.3" [dependencies.ahash] version = "0.8.4" diff --git a/src/iter/mod.rs b/src/iter/mod.rs index 56d8b4c..4b83d2f 100644 --- a/src/iter/mod.rs +++ b/src/iter/mod.rs @@ -109,7 +109,7 @@ mod tests { let mut guard = map.guard(); map.insert(1, 42, &guard); map.insert(2, 84, &guard); - guard.flush(); + guard.refresh(); assert_eq!( map.values(&guard).collect::>(), diff --git a/src/map.rs b/src/map.rs index 170e301..febdd6a 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1020,15 +1020,11 @@ where // the new bin will also be a tree bin. if both the high // bin and the low bin are non-empty, we have to // allocate a new TreeBin. - Shared::boxed( - BinEntry::Tree(TreeBin::new( - // safety: we have just created `low` and its `next` - // nodes and have never shared them - unsafe { low.into_box() }, - guard, - )), - &self.collector, - ) + // safety: we have just created `low` and its `next` nodes using `Shared::boxed` + // and have never shared them + let low_bin = unsafe { BinEntry::Tree(TreeBin::new(low, guard)) }; + + Shared::boxed(low_bin, &self.collector) } else { // if not, we can re-use the old bin here, since it will // be swapped for a Moved entry while we are still @@ -1048,15 +1044,11 @@ where unsafe { TreeBin::drop_tree_nodes(high, false, guard) }; high_linear } else if low_count != 0 { - Shared::boxed( - BinEntry::Tree(TreeBin::new( - // safety: we have just created `high` and its `next` - // nodes and have never shared them - unsafe { high.into_box() }, - guard, - )), - &self.collector, - ) + // safety: we have just created `high` and its `next` nodes using `Shared::boxed` + // and have never shared them + let high_bin = unsafe { BinEntry::Tree(TreeBin::new(high, guard)) }; + + Shared::boxed(high_bin, &self.collector) } else { reused_bin = true; // since we also don't use the created low nodes here, @@ -1664,7 +1656,7 @@ where not_inserted, } => Err(TryInsertError { current, - not_inserted: Linked::into_inner(*not_inserted), + not_inserted: not_inserted.value, }), PutResult::Inserted { new } => Ok(new), PutResult::Replaced { .. } => { @@ -1731,9 +1723,7 @@ where Err(changed) => { assert!(!changed.current.is_null()); bin = changed.current; - if let BinEntry::Node(node) = - Linked::into_inner(*unsafe { changed.new.into_box() }) - { + if let BinEntry::Node(node) = unsafe { changed.new.into_box() }.value { key = node.key; } else { unreachable!("we declared node and it is a BinEntry::Node"); @@ -2795,18 +2785,11 @@ where tail = new_tree_node; e = e_deref.next.load(Ordering::SeqCst, guard); } - tab.store_bin( - index, - Shared::boxed( - BinEntry::Tree(TreeBin::new( - // safety: we have just created `head` and its `next` - // nodes and have never shared them - unsafe { head.into_box() }, - guard, - )), - &self.collector, - ), - ); + + // safety: we have just created `head` and its `next` nodes using `Shared::boxed` + // and have never shared them + let head_bin = unsafe { BinEntry::Tree(TreeBin::new(head, guard)) }; + tab.store_bin(index, Shared::boxed(head_bin, &self.collector)); drop(lock); // make sure the old bin entries get dropped e = bin; @@ -3317,7 +3300,7 @@ fn no_replacement_return_val() { map.put(42, String::from("world"), true, &guard), PutResult::Exists { current: &String::from("hello"), - not_inserted: Box::new(map.collector.link(String::from("world"))), + not_inserted: Box::new(map.collector.link_value(String::from("world"))), } ); } diff --git a/src/node.rs b/src/node.rs index 36ed822..f8ad720 100644 --- a/src/node.rs +++ b/src/node.rs @@ -2,7 +2,7 @@ use crate::raw::Table; use crate::reclaim::{Atomic, Collector, Guard, RetireShared, Shared}; use core::sync::atomic::{AtomicBool, AtomicI64, Ordering}; use parking_lot::Mutex; -use seize::Linked; +use seize::{Link, Linked}; use std::borrow::Borrow; use std::thread::{current, park, Thread}; @@ -241,9 +241,12 @@ where /// Constructs a new bin from the given nodes. /// /// Nodes are arranged into an ordered red-black tree. - pub(crate) fn new(bin: Box>>, guard: &Guard<'_>) -> Self { + /// + /// # Safety + /// + /// The `bin` pointer and its successors were created with `Shared::boxed` and never shared. + pub(crate) unsafe fn new(bin: Shared<'_, BinEntry>, guard: &Guard<'_>) -> Self { let mut root = Shared::null(); - let bin = Shared::from(Box::into_raw(bin)); // safety: We own the nodes for creating this new TreeBin, so they are // not shared with another thread and cannot get invalidated. @@ -937,16 +940,16 @@ impl TreeBin { bin: Shared<'g, BinEntry>, guard: &'g Guard<'_>, ) { - guard.retire(bin.as_ptr(), |mut link| { + guard.defer_retire(bin.as_ptr(), |link| { let bin = unsafe { - // SAFETY: `bin` is a `BinEntry` - let ptr = link.cast::>(); + // SAFETY: `bin` is a `Linked>` + let ptr: *mut Linked> = Link::cast(link); // SAFETY: `retire` guarantees that we // have unique access to `bin` at this point - *Box::from_raw(ptr) + Box::from_raw(ptr).value }; - if let BinEntry::Tree(mut tree_bin) = Linked::into_inner(bin) { + if let BinEntry::Tree(mut tree_bin) = bin { tree_bin.drop_fields(false); } else { unreachable!("bin is a tree bin"); @@ -985,7 +988,7 @@ impl TreeBin { ) { let mut p = from; while !p.is_null() { - if let BinEntry::TreeNode(tree_node) = Linked::into_inner(*p.into_box()) { + if let BinEntry::TreeNode(tree_node) = p.into_box().value { // if specified, drop the value in this node if drop_values { let _ = tree_node.node.value.into_box(); @@ -1544,7 +1547,7 @@ mod tests { let entry = table.get_moved(table2, &guard); table.store_bin(0, entry); assert!(table - .find(&collector.link(BinEntry::Moved), 1, &2, &guard) + .find(&collector.link_value(BinEntry::Moved), 1, &2, &guard) .is_null()); table.drop_bins(); // safety: table2 is still valid and not accessed by different threads @@ -1562,7 +1565,7 @@ mod tests { let entry = table.get_moved(table2, &guard); table.store_bin(0, entry); assert!(table - .find(&collector.link(BinEntry::Moved), 1, &2, &guard) + .find(&collector.link_value(BinEntry::Moved), 1, &2, &guard) .is_null()); table.drop_bins(); // safety: table2 is still valid and not accessed by different threads @@ -1584,7 +1587,7 @@ mod tests { let entry = table.get_moved(table2, &guard); table.store_bin(0, entry); assert!(table - .find(&collector.link(BinEntry::Moved), 0, &1, &guard) + .find(&collector.link_value(BinEntry::Moved), 0, &1, &guard) .is_null()); table.drop_bins(); // safety: table2 is still valid and not accessed by different threads @@ -1611,7 +1614,7 @@ mod tests { // entry was not removed unsafe { table - .find(&collector.link(BinEntry::Moved), 1, &2, &guard) + .find(&collector.link_value(BinEntry::Moved), 1, &2, &guard) .deref() } .as_node() diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 59d734e..7427ad2 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -209,7 +209,7 @@ impl Table { // we replaced the bin with a NULL, so there's no future way to access it // either; we own all the nodes in the list. - let node = if let BinEntry::Node(node) = Linked::into_inner(*p) { + let node = if let BinEntry::Node(node) = p.value { node } else { unreachable!(); @@ -228,7 +228,7 @@ impl Table { BinEntry::Tree(_) => { // safety: same as for BinEntry::Node let p = unsafe { bin.into_box() }; - let bin = if let BinEntry::Tree(bin) = Linked::into_inner(*p) { + let bin = if let BinEntry::Tree(bin) = p.value { bin } else { unreachable!(); diff --git a/src/reclaim.rs b/src/reclaim.rs index b65dc3f..e994387 100644 --- a/src/reclaim.rs +++ b/src/reclaim.rs @@ -2,14 +2,14 @@ pub(crate) use seize::{Collector, Guard, Linked}; use std::marker::PhantomData; use std::ops::Deref; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicPtr, Ordering}; use std::{fmt, ptr}; -pub(crate) struct Atomic(seize::AtomicPtr); +pub(crate) struct Atomic(AtomicPtr>); impl Atomic { pub(crate) fn null() -> Self { - Self(seize::AtomicPtr::default()) + Self(AtomicPtr::default()) } pub(crate) fn load<'g>(&self, ordering: Ordering, guard: &'g Guard<'_>) -> Shared<'g, T> { @@ -149,7 +149,7 @@ pub(crate) trait RetireShared { impl RetireShared for Guard<'_> { unsafe fn retire_shared(&self, shared: Shared<'_, T>) { - self.retire(shared.ptr, seize::reclaim::boxed::); + self.defer_retire(shared.ptr, seize::reclaim::boxed::>); } }