From 4192b6c99171b126ed609eaa36331a7e5fcd29cf Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Thu, 10 Jul 2025 11:11:00 +0930 Subject: [PATCH 01/10] Proof of concept for supporting no_std targets. Breaks basically all tests. --- .vscode/settings.json | 4 + Cargo.toml | 38 +++-- src/arcache/ll.rs | 13 +- src/arcache/mod.rs | 256 ++++++++++++++++++++++--------- src/bptree/asynch.rs | 2 +- src/bptree/impl.rs | 84 +++++----- src/bptree/mod.rs | 21 +-- src/cowcell/asynch.rs | 1 + src/cowcell/mod.rs | 43 ++++-- src/ebrcell/mod.rs | 19 ++- src/hashmap/impl.rs | 67 ++++---- src/hashmap/mod.rs | 28 ++-- src/hashtrie/impl.rs | 70 +++++---- src/hashtrie/mod.rs | 27 ++-- src/internals/bptree/cursor.rs | 45 ++++-- src/internals/bptree/iter.rs | 14 +- src/internals/bptree/mutiter.rs | 3 +- src/internals/bptree/node.rs | 31 ++-- src/internals/hashmap/cursor.rs | 53 ++++--- src/internals/hashmap/iter.rs | 12 +- src/internals/hashmap/macros.rs | 4 +- src/internals/hashmap/node.rs | 22 ++- src/internals/hashmap/simd.rs | 2 +- src/internals/hashtrie/cursor.rs | 46 +++--- src/internals/hashtrie/iter.rs | 12 +- src/internals/lincowcell/mod.rs | 53 ++++--- src/lib.rs | 18 ++- src/unsound3.rs | 2 +- src/utils.rs | 53 +++++-- 29 files changed, 675 insertions(+), 368 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..62ba47e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "rust-analyzer.cargo.allTargets": true, + "rust-analyzer.check.features": ["default"] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 08e39d2..7493413 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,15 +23,19 @@ name = "concread" path = "src/lib.rs" [features] -default = ["asynch", "ahash", "ebr", "maps", "arcache-is-hashtrie"] +default = ["std", "asynch","ahash", "ebr", "maps", "arcache-is-hashtrie"] # Features to add/remove contents. ahash = ["dep:ahash"] arcache = ["maps", "lru", "crossbeam-queue"] -asynch = ["tokio"] -ebr = ["crossbeam-epoch"] -maps = ["crossbeam-utils", "smallvec"] +asynch = ["dep:tokio", "std"] +ebr = ["std"] +maps = ["dep:crossbeam-utils", "smallvec"] tcache = [] +std = ["ahash/std", "ahash/runtime-rng", "crossbeam-epoch/std", "crossbeam-queue/std", "crossbeam-utils/std", "tracing/std", "dep:parking_lot", "smallvec/write"] +#serde = ["lock_api/serde"] + +no_std = ["crossbeam-epoch/alloc", "crossbeam-queue/alloc", "serde/alloc", "dep:spin", "ahash", "arc-swap/experimental-thread-local"] # Internal features for tweaking some align/perf behaviours. dhat-heap = ["dep:dhat"] @@ -43,21 +47,25 @@ arcache-is-hashtrie = ["arcache"] simd_support = [] [dependencies] -ahash = { version = "0.8", optional = true } -crossbeam-utils = { version = "0.8.21", optional = true } -crossbeam-epoch = { version = "0.9.11", optional = true } -crossbeam-queue = { version = "0.3.12", optional = true } +ahash = { version = "0.8", default-features = false, optional = true} +crossbeam-utils = { version = "0.8.21", optional = true, default-features = false, features = []} +crossbeam-epoch = { version = "0.9.11", optional = true, default-features = false, features = [] } +crossbeam-queue = { version = "0.3.12", optional = true, default-features = false, features = [] } dhat = { version = "0.3.3", optional = true } -lru = { version = "0.13", optional = true } -serde = { version = "1.0", optional = true } -smallvec = { version = "1.14", optional = true } -sptr = "0.3" -arc-swap = "1.0" +lru = { version = "0.16.0", optional = true } +serde = { version = "1.0", optional = true, default-features = false, features = []} +smallvec = { version = "1.14", optional = true, default-features = false} +arc-swap = {version = "1.0", default-features = false} tokio = { version = "1", features = ["sync"], optional = true } -tracing = "0.1" +tracing = {version = "0.1", default-features = false} +lock_api = "0.4" +parking_lot = {version = "0.12.3", optional = true } +hashbrown = {version = "0.15.2", default-features = false} +spin = {version = "0.10.0", optional = true, default-features = false, features = ["lock_api", "spin_mutex", "rwlock"]} +cfg-if = "1.0.0" [dev-dependencies] -criterion = { version = "0.5", features = ["html_reports"] } +criterion = { version = "0.6.0", features = ["html_reports"] } rand = "0.9" tracing-subscriber = { version = "0.3", features = [ "env-filter", diff --git a/src/arcache/ll.rs b/src/arcache/ll.rs index 825322f..13bbaf9 100644 --- a/src/arcache/ll.rs +++ b/src/arcache/ll.rs @@ -1,3 +1,10 @@ + + +#[cfg(feature = "std")] +use std::boxed::Box; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; + use std::fmt::Debug; use std::marker::PhantomData; use std::mem::MaybeUninit; @@ -309,10 +316,12 @@ where (*next).prev = prev; (*prev).next = next; // Null things for paranoia. - if cfg!(test) || cfg!(debug_assertions) { + + cfg_if::cfg_if! { if #[cfg(any(test, debug_assertions))] + { (*n.inner).prev = ptr::null_mut(); (*n.inner).next = ptr::null_mut(); - } + }} // (*n).tag = 0; } diff --git a/src/arcache/mod.rs b/src/arcache/mod.rs index 191e075..689b3ee 100644 --- a/src/arcache/mod.rs +++ b/src/arcache/mod.rs @@ -10,6 +10,12 @@ //! writers that are serialised. This formally means that this is an ACID //! compliant Cache. + +#[cfg(feature = "std")] +use std::{vec::Vec, borrow::ToOwned, sync::Arc}; +#[cfg(not(feature = "std"))] +use alloc::{vec::Vec, borrow::ToOwned, sync::Arc}; + mod ll; /// Stats collection for [ARCache] pub mod stats; @@ -26,12 +32,12 @@ use crate::hashmap::{ use crate::hashtrie::{ HashTrie as DataMap, HashTrieReadTxn as DataMapReadTxn, HashTrieWriteTxn as DataMapWriteTxn, }; +use crate::utils::{self, Monotonic}; use crossbeam_queue::ArrayQueue; -use std::collections::HashMap as Map; +use hashbrown::HashMap as Map; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::sync::{Mutex, RwLock}; +use lock_api::{Mutex, RawMutex, RawRwLock, RwLock}; use std::borrow::Borrow; use std::cell::UnsafeCell; @@ -41,25 +47,79 @@ use std::mem; use std::num::NonZeroUsize; use std::ops::Deref; use std::ops::DerefMut; -use std::time::Instant; use tracing::trace; // const READ_THREAD_MIN: usize = 8; const READ_THREAD_RATIO: usize = 16; +#[cfg(feature = "std")] +mod monotonic_timer { + pub struct MonotonicTimer; + + unsafe impl crate::utils::Monotonic for MonotonicTimer{ + type Output = std::time::Instant; + + fn new() -> Self { + MonotonicTimer + } + + fn current(&self) -> Self::Output { + self.next() + } + + fn next(&self) -> Self::Output { + std::time::Instant::now() + } + } +} + +#[cfg(not(feature = "std"))] +mod monotonic_timer { + use std::sync::atomic::{AtomicUsize, Ordering}; + /// This provides a mnonotonic generation counter, with the bit width equal to the pointer width of the platform. + /// + /// # SAFETY + /// + /// This wraps around on overflow, so the result becomes invalid if you call it more than `usize::MAX`. + /// Overflow will panic on debug, and continue on release mode. + pub struct MonotonicTimer(AtomicUsize); + + unsafe impl crate::utils::Monotonic for MonotonicTimer { + type Output = usize; + + fn new() -> Self { + Self(AtomicUsize::new(0)) + } + + fn current(&self) -> Self::Output { + // If you are calling this function, you probably want more guarantees about the relative ordering. + // Therefore, we use Acquire semantics to get the best ordering for loads withour requiring SeqSct for everything + self.0.load(Ordering::Acquire) + } + + fn next(&self) -> Self::Output { + // we can use relaxed ordering here as it still guarantees that each value will only be observed once. + // the downside is that close calls may have re-ordered insertion do the relaxed ordering on the read. + let counter = self.0.fetch_add(1, Ordering::Relaxed); + debug_assert!(counter != usize::MAX, "The default monotonic counter reached the maximum number of valid calls"); + counter + } + } +} + enum ThreadCacheItem { Present(V, bool, usize), Removed(bool), } -struct CacheHitEvent { - t: Instant, +struct CacheHitEvent { + t: M::Output, k_hash: u64, } -struct CacheIncludeEvent { - t: Instant, +struct CacheIncludeEvent { + t: M::Output, k: K, v: V, txid: u64, @@ -136,10 +196,11 @@ pub(crate) struct CStat { p: usize, } -struct ArcInner +struct ArcInner where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, + M: Monotonic { /// Weight of items between the two caches. p: usize, @@ -148,22 +209,23 @@ where ghost_freq: LL>, ghost_rec: LL>, haunted: LL>, - hit_queue: Arc>, - inc_queue: Arc>>, + hit_queue: Arc>>, + inc_queue: Arc>>, min_txid: u64, } -struct ArcShared +struct ArcShared where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, + M: Monotonic { // Max number of elements to cache. max: usize, // Max number of elements for a reader per thread. read_max: usize, - hit_queue: Arc>, - inc_queue: Arc>>, + hit_queue: Arc>>, + inc_queue: Arc>>, /// The number of items that are present in the cache before we start to process /// the arc sets/lists. watermark: usize, @@ -173,34 +235,44 @@ where /// A concurrently readable adaptive replacement cache. Operations are performed on the /// cache via read and write operations. -pub struct ARCache +pub struct ARCache where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, + M: Monotonic + 'static, + RawMutexImpl: RawMutex + 'static, + RawRwLockImpl: RawRwLock + 'static { // Use a unified tree, allows simpler movement of items between the // cache types. - cache: DataMap>, + cache: DataMap, RawMutexImpl>, // This is normally only ever taken in "read" mode, so it's effectively // an uncontended barrier. - shared: RwLock>, + shared: RwLock>, // These are only taken during a quiesce - inner: Mutex>, + inner: Mutex>, // stats: CowCell, above_watermark: AtomicBool, look_back_limit: u64, + monotonic: M } unsafe impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, - > Send for ARCache + M: Monotonic + Send + Send + 'static, + Mutex: RawMutex + Sync + Send + 'static, + RwLock: RawRwLock + Sync + Send + 'static + > Send for ARCache { } unsafe impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, - > Sync for ARCache + M: Monotonic + Send + 'static, + Mutex: RawMutex + Sync + 'static, + RwLock: RawRwLock + Sync + 'static + > Sync for ARCache { } @@ -241,18 +313,21 @@ where /// An active read transaction over the cache. The data is this cache is guaranteed to be /// valid at the point in time the read is created. You may include items during a cache /// miss via the "insert" function. -pub struct ARCacheReadTxn<'a, K, V, S> +pub struct ARCacheReadTxn<'a, K, V, S, M, Mutex, RwLock> where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, S: ARCacheReadStat + Clone, + M: Monotonic + 'static, + Mutex: RawMutex + 'static, + RwLock: RawRwLock + 'static { - caller: &'a ARCache, + caller: &'a ARCache, // ro_txn to cache - cache: DataMapReadTxn<'a, K, CacheItem>, + cache: DataMapReadTxn<'a, K, CacheItem, Mutex>, tlocal: Option>, - hit_queue: Arc>, - inc_queue: Arc>>, + hit_queue: Arc>>, + inc_queue: Arc>>, above_watermark: bool, reader_quiesce: bool, stats: S, @@ -262,14 +337,20 @@ unsafe impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, S: ARCacheReadStat + Clone + Sync + Send + 'static, - > Send for ARCacheReadTxn<'_, K, V, S> + M: Monotonic + Sync + Send + 'static, + Mutex: RawMutex + Sync + Send + 'static, + RwLock: RawRwLock + Sync + Send + 'static + > Send for ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { } unsafe impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, S: ARCacheReadStat + Clone + Sync + Send + 'static, - > Sync for ARCacheReadTxn<'_, K, V, S> + M: Monotonic + Sync + 'static, + Mutex: RawMutex + Sync + 'static, + RwLock: RawRwLock + Sync+ 'static + > Sync for ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { } @@ -277,15 +358,18 @@ unsafe impl< /// from readers, and may be rolled-back if an error occurs. Changes only become /// globally visible once you call "commit". Items may be added to the cache on /// a miss via "insert", and you can explicitly remove items by calling "remove". -pub struct ARCacheWriteTxn<'a, K, V, S> +pub struct ARCacheWriteTxn<'a, K, V, S, M, Mutex, RwLock> where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, S: ARCacheWriteStat, + M: Monotonic + 'static, + Mutex: RawMutex + 'static, + RwLock: RawRwLock + 'static { - caller: &'a ARCache, + caller: &'a ARCache, // wr_txn to cache - cache: DataMapWriteTxn<'a, K, CacheItem>, + cache: DataMapWriteTxn<'a, K, CacheItem, Mutex>, // Cache of missed items (w_ dirty/clean) // On COMMIT we drain this to the main cache tlocal: Map>, @@ -331,15 +415,16 @@ impl< } /// A configurable builder to create new concurrent Adaptive Replacement Caches. -pub struct ARCacheBuilder { +pub struct ARCacheBuilder { max: Option, read_max: Option, watermark: Option, reader_quiesce: bool, look_back_limit: Option, + monotonic: Option } -impl Default for ARCacheBuilder { +impl Default for ARCacheBuilder { fn default() -> Self { ARCacheBuilder { max: None, @@ -347,11 +432,15 @@ impl Default for ARCacheBuilder { watermark: None, reader_quiesce: true, look_back_limit: None, + monotonic: None } } } -impl ARCacheBuilder { +impl ARCacheBuilder +where + M: Monotonic +{ /// Create a new ARCache builder that you can configure before creation. pub fn new() -> Self { Self::default() @@ -403,6 +492,7 @@ impl ARCacheBuilder { watermark: self.watermark, reader_quiesce: self.reader_quiesce, look_back_limit: self.look_back_limit, + ..self } } @@ -451,7 +541,7 @@ impl ARCacheBuilder { /// Consume this builder, returning a cache if successful. If configured parameters are /// missing or incorrect, a None will be returned. - pub fn build(self) -> Option> + pub fn build(self) -> Option> where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, @@ -463,6 +553,7 @@ impl ARCacheBuilder { watermark, reader_quiesce, look_back_limit, + monotonic } = self; let (max, read_max) = max.zip(read_max)?; @@ -488,7 +579,7 @@ impl ARCacheBuilder { let chan_size = 32; let inc_queue = Arc::new(ArrayQueue::new(chan_size)); - let shared = RwLock::new(ArcShared { + let shared = RwLock::>::new(ArcShared { max, read_max, // stat_tx, @@ -497,7 +588,7 @@ impl ARCacheBuilder { watermark, reader_quiesce, }); - let inner = Mutex::new(ArcInner { + let inner = Mutex::>::new(ArcInner { // We use p from the former stats. p: 0, freq: LL::new(), @@ -518,6 +609,7 @@ impl ARCacheBuilder { // stats: CowCell::new(stats), above_watermark: AtomicBool::new(init_watermark), look_back_limit, + monotonic: monotonic.unwrap_or(M::new()) }) } } @@ -525,7 +617,10 @@ impl ARCacheBuilder { impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, - > ARCache + M: Monotonic + 'static, + Mutex: RawMutex + 'static, + RwLock: RawRwLock + 'static + > ARCache { /// Use ARCacheBuilder instead #[deprecated(since = "0.2.20", note = "please use`ARCacheBuilder` instead")] @@ -564,11 +659,11 @@ impl< /// Begin a read operation on the cache. This reader has a thread-local cache for items /// that are localled included via `insert`, and can communicate back to the main cache /// to safely include items. - pub fn read_stats(&self, stats: S) -> ARCacheReadTxn + pub fn read_stats(&self, stats: S) -> ARCacheReadTxn where S: ARCacheReadStat + Clone, { - let rshared = self.shared.read().unwrap(); + let rshared = self.shared.read(); let tlocal = if rshared.read_max > 0 { Some(ReadCache { set: Map::new(), @@ -595,19 +690,19 @@ impl< /// Begin a read operation on the cache. This reader has a thread-local cache for items /// that are localled included via `insert`, and can communicate back to the main cache /// to safely include items. - pub fn read(&self) -> ARCacheReadTxn { + pub fn read(&self) -> ARCacheReadTxn { self.read_stats(()) } /// Begin a write operation on the cache. This writer has a thread-local store /// for all items that have been included or dirtied in the transactions, items /// may be removed from this cache (ie deleted, invalidated). - pub fn write(&self) -> ARCacheWriteTxn { + pub fn write(&self) -> ARCacheWriteTxn { self.write_stats(()) } /// _ - pub fn write_stats(&self, stats: S) -> ARCacheWriteTxn + pub fn write_stats(&self, stats: S) -> ARCacheWriteTxn where S: ARCacheWriteStat, { @@ -624,7 +719,7 @@ impl< } } - fn try_write_stats(&self, stats: S) -> Result, S> + fn try_write_stats(&self, stats: S) -> Result, S> where S: ARCacheWriteStat, { @@ -705,9 +800,9 @@ impl< fn drain_tlocal_inc( &self, - cache: &mut DataMapWriteTxn>, - inner: &mut ArcInner, - shared: &ArcShared, + cache: &mut DataMapWriteTxn, Mutex>, + inner: &mut ArcInner, + shared: &ArcShared, tlocal: Map>, commit_txid: u64, stats: &mut S, @@ -851,9 +946,9 @@ impl< fn drain_hit_rx( &self, - cache: &mut DataMapWriteTxn>, - inner: &mut ArcInner, - commit_ts: Instant, + cache: &mut DataMapWriteTxn, Mutex>, + inner: &mut ArcInner, + commit_ts: M::Output, ) { // * for each item // while let Ok(ce) = inner.rx.try_recv() { @@ -903,10 +998,10 @@ impl< fn drain_inc_rx( &self, - cache: &mut DataMapWriteTxn>, - inner: &mut ArcInner, - shared: &ArcShared, - commit_ts: Instant, + cache: &mut DataMapWriteTxn, Mutex>, + inner: &mut ArcInner, + shared: &ArcShared, + commit_ts: M::Output, stats: &mut S, ) where S: ARCacheWriteStat, @@ -1051,8 +1146,8 @@ impl< fn drain_tlocal_hits( &self, - cache: &mut DataMapWriteTxn>, - inner: &mut ArcInner, + cache: &mut DataMapWriteTxn, Mutex>, + inner: &mut ArcInner, // shared: &ArcShared, commit_txid: u64, hit: Vec, @@ -1118,7 +1213,7 @@ impl< } fn evict_to_haunted_len( - cache: &mut DataMapWriteTxn>, + cache: &mut DataMapWriteTxn, Mutex>, ll: &mut LL>, to_ll: &mut LL>, size: usize, @@ -1149,7 +1244,7 @@ impl< } fn evict_to_len( - cache: &mut DataMapWriteTxn>, + cache: &mut DataMapWriteTxn, Mutex>, ll: &mut LL>, to_ll: &mut LL>, size: usize, @@ -1204,9 +1299,9 @@ impl< #[allow(clippy::cognitive_complexity)] fn evict( &self, - cache: &mut DataMapWriteTxn>, - inner: &mut ArcInner, - shared: &ArcShared, + cache: &mut DataMapWriteTxn, Mutex>, + inner: &mut ArcInner, + shared: &ArcShared, commit_txid: u64, stats: &mut S, ) where @@ -1337,7 +1432,7 @@ impl< } fn drain_ll_to_ghost( - cache: &mut DataMapWriteTxn>, + cache: &mut DataMapWriteTxn, Mutex>, ll: &mut LL>, gf: &mut LL>, gr: &mut LL>, @@ -1386,7 +1481,7 @@ impl< } fn drain_ll_min_txid( - cache: &mut DataMapWriteTxn>, + cache: &mut DataMapWriteTxn, Mutex>, ll: &mut LL>, min_txid: u64, ) { @@ -1408,7 +1503,7 @@ impl< #[allow(clippy::unnecessary_mut_passed)] fn commit( &self, - mut cache: DataMapWriteTxn>, + mut cache: DataMapWriteTxn, Mutex>, tlocal: Map>, hit: Vec, clear: bool, @@ -1420,11 +1515,11 @@ impl< S: ARCacheWriteStat, { // What is the time? - let commit_ts = Instant::now(); + let commit_generation = self.monotonic.next(); let commit_txid = cache.get_txid(); // Copy p + init cache sizes for adjustment. - let mut inner = self.inner.lock().unwrap(); - let shared = self.shared.read().unwrap(); + let mut inner = self.inner.lock(); + let shared = self.shared.read(); // Did we request to be cleared? If so, we move everything to a ghost set // that was live. @@ -1482,11 +1577,11 @@ impl< &mut cache, inner.deref_mut(), shared.deref(), - commit_ts, + commit_generation, &mut stats, ); - self.drain_hit_rx(&mut cache, inner.deref_mut(), commit_ts); + self.drain_hit_rx(&mut cache, inner.deref_mut(), commit_generation); // drain the tlocal hits into the main cache. @@ -1550,7 +1645,10 @@ impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, S: ARCacheWriteStat, - > ARCacheWriteTxn<'_, K, V, S> + M: Monotonic + 'static, + Mutex: RawMutex + 'static, + RwLock: RawRwLock + 'static + > ARCacheWriteTxn<'_, K, V, S, M, Mutex, RwLock> { /// Commit the changes of this writer, making them globally visible. This causes /// all items written to this thread's local store to become visible in the main @@ -1920,8 +2018,8 @@ impl< #[cfg(test)] pub(crate) fn peek_stat(&self) -> CStat { - let inner = self.caller.inner.lock().unwrap(); - let shared = self.caller.shared.read().unwrap(); + let inner = self.caller.inner.lock(); + let shared = self.caller.shared.read(); CStat { max: shared.max, cache: self.cache.len(), @@ -1942,7 +2040,10 @@ impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, S: ARCacheReadStat + Clone, - > ARCacheReadTxn<'_, K, V, S> + M: Monotonic + 'static, + Mutex: RawMutex, + RwLock: RawRwLock + > ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { /// Attempt to retrieve a k-v pair from the cache. If it is present in the main cache OR /// the thread local cache, a `Some` is returned, else you will receive a `None`. On a @@ -1971,7 +2072,7 @@ impl< if self.above_watermark { let _ = self.hit_queue.push(CacheHitEvent { - t: Instant::now(), + t: self.caller.monotonic.next(), k_hash, }); } @@ -1990,7 +2091,7 @@ impl< if self.above_watermark { let _ = self.hit_queue.push(CacheHitEvent { - t: Instant::now(), + t: self.caller.monotonic.next(), k_hash, }); } @@ -2030,7 +2131,7 @@ impl< if self .inc_queue .push(CacheIncludeEvent { - t: Instant::now(), + t: self.caller.monotonic.next(), k: k.clone(), v: v.clone(), txid: self.cache.get_txid(), @@ -2097,7 +2198,10 @@ impl< K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, S: ARCacheReadStat + Clone, - > Drop for ARCacheReadTxn<'_, K, V, S> + M: Monotonic + 'static, + Mutex: RawMutex, + RwLock: RawRwLock + > Drop for ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { fn drop(&mut self) { // We could make this check the queue sizes rather than blindly quiescing diff --git a/src/bptree/asynch.rs b/src/bptree/asynch.rs index 6405423..3dfb7a7 100644 --- a/src/bptree/asynch.rs +++ b/src/bptree/asynch.rs @@ -271,7 +271,7 @@ mod tests { let map = BptreeMap::new(); // add values { - let mut w = map.write().await; + let mut w: crate::bptree::asynch::BptreeMapWriteTxn<'_, usize, usize> = map.write().await; w.extend((0..(L_CAPACITY * 2)).map(|v| (v * 2, v * 2))); w.commit(); } diff --git a/src/bptree/impl.rs b/src/bptree/impl.rs index e763c28..53dc39c 100644 --- a/src/bptree/impl.rs +++ b/src/bptree/impl.rs @@ -1,3 +1,5 @@ +use lock_api::RawMutex; + use crate::internals::bptree::cursor::CursorReadOps; use crate::internals::bptree::cursor::{CursorRead, CursorWrite, SuperBlock}; use crate::internals::bptree::iter::{Iter, KeyIter, RangeIter, ValueIter}; @@ -29,40 +31,42 @@ use std::ops::RangeBounds; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `BptreeMapWriteTxn` without calling `commit()`. -pub struct BptreeMap +pub struct BptreeMap where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCell, CursorRead, CursorWrite>, + inner: LinCowCell, CursorRead, CursorWrite, M>, } -unsafe impl Send - for BptreeMap +unsafe impl Send + for BptreeMap { } -unsafe impl Sync - for BptreeMap +unsafe impl Sync + for BptreeMap { } /// An active read transaction over a [BptreeMap]. The data in this tree /// is guaranteed to not change and will remain consistent for the life /// of this transaction. -pub struct BptreeMapReadTxn<'a, K, V> +pub struct BptreeMapReadTxn<'a, K, V, M> where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCellReadTxn<'a, SuperBlock, CursorRead, CursorWrite>, + inner: LinCowCellReadTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } -unsafe impl Send - for BptreeMapReadTxn<'_, K, V> +unsafe impl Send + for BptreeMapReadTxn<'_, K, V, R> { } -unsafe impl Sync - for BptreeMapReadTxn<'_, K, V> +unsafe impl Sync + for BptreeMapReadTxn<'_, K, V, R> { } @@ -71,20 +75,22 @@ unsafe impl +pub struct BptreeMapWriteTxn<'a, K, V, M> where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite>, + inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } -enum SnapshotType<'a, K, V> +enum SnapshotType<'a, K, V, M> where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - R(&'a CursorRead), + R(&'a CursorRead), W(&'a CursorWrite), } @@ -96,24 +102,25 @@ where /// This snapshot IS safe within the read thread due to the nature of the /// implementation borrowing the inner tree to prevent mutations within the /// same thread while the read snapshot is open. -pub struct BptreeMapReadSnapshot<'a, K, V> +pub struct BptreeMapReadSnapshot<'a, K, V, M> where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: SnapshotType<'a, K, V>, + inner: SnapshotType<'a, K, V, M>, } -impl Default - for BptreeMap +impl Default + for BptreeMap { fn default() -> Self { Self::new() } } -impl - BptreeMap +impl + BptreeMap { /// Construct a new concurrent tree pub fn new() -> Self { @@ -125,20 +132,23 @@ impl Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| BptreeMapWriteTxn { inner }) } } -impl - FromIterator<(K, V)> for BptreeMap +impl + FromIterator<(K, V)> for BptreeMap { fn from_iter>(iter: I) -> Self { let mut new_sblock = unsafe { SuperBlock::new() }; - let prev = new_sblock.create_reader(); - let mut cursor = new_sblock.create_writer(); + let prev: CursorRead = new_sblock.create_reader(); + + // TODO - fix this? + + let mut cursor = as LinCowCellCapable, CursorWrite>>::create_writer(&new_sblock); //new_sblock.create_writer(); cursor.extend(iter); @@ -150,16 +160,16 @@ impl - Extend<(K, V)> for BptreeMapWriteTxn<'_, K, V> +impl + Extend<(K, V)> for BptreeMapWriteTxn<'_, K, V, R> { fn extend>(&mut self, iter: I) { self.inner.as_mut().extend(iter); } } -impl - BptreeMapWriteTxn<'_, K, V> +impl + BptreeMapWriteTxn<'_, K, V, M> { // == RO methods @@ -305,15 +315,15 @@ impl BptreeMapReadSnapshot { + pub fn to_snapshot(&self) -> BptreeMapReadSnapshot { BptreeMapReadSnapshot { inner: SnapshotType::W(self.inner.as_ref()), } } } -impl - BptreeMapReadTxn<'_, K, V> +impl + BptreeMapReadTxn<'_, K, V, M> { /// Retrieve a value from the tree. If the value exists, a reference is returned /// as `Some(&V)`, otherwise if not present `None` is returned. @@ -386,7 +396,7 @@ impl BptreeMapReadSnapshot { + pub fn to_snapshot(&self) -> BptreeMapReadSnapshot { BptreeMapReadSnapshot { inner: SnapshotType::R(self.inner.as_ref()), } @@ -399,8 +409,8 @@ impl - BptreeMapReadSnapshot<'_, K, V> +impl + BptreeMapReadSnapshot<'_, K, V, M> { /// Retrieve a value from the tree. If the value exists, a reference is returned /// as `Some(&V)`, otherwise if not present `None` is returned. diff --git a/src/bptree/mod.rs b/src/bptree/mod.rs index a40fe6b..9b27796 100644 --- a/src/bptree/mod.rs +++ b/src/bptree/mod.rs @@ -16,26 +16,26 @@ use crate::internals::lincowcell::{LinCowCell, LinCowCellReadTxn, LinCowCellWrit include!("impl.rs"); -impl - BptreeMap +impl + BptreeMap { /// Initiate a read transaction for the tree, concurrent to any /// other readers or writers. - pub fn read(&self) -> BptreeMapReadTxn { + pub fn read(&self) -> BptreeMapReadTxn { let inner = self.inner.read(); BptreeMapReadTxn { inner } } /// Initiate a write transaction for the tree, exclusive to this /// writer, and concurrently to all existing reads. - pub fn write(&self) -> BptreeMapWriteTxn { + pub fn write(&self) -> BptreeMapWriteTxn { let inner = self.inner.write(); BptreeMapWriteTxn { inner } } } -impl - BptreeMapWriteTxn<'_, K, V> +impl + BptreeMapWriteTxn<'_, K, V, M> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceive these changes. @@ -47,10 +47,11 @@ impl Serialize for BptreeMapReadTxn<'_, K, V> +impl Serialize for BptreeMapReadTxn<'_, K, V, M> where K: Serialize + Clone + Ord + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn serialize(&self, serializer: S) -> Result where @@ -67,10 +68,11 @@ where } #[cfg(feature = "serde")] -impl Serialize for BptreeMap +impl Serialize for BptreeMap where K: Serialize + Clone + Ord + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn serialize(&self, serializer: S) -> Result where @@ -81,10 +83,11 @@ where } #[cfg(feature = "serde")] -impl<'de, K, V> Deserialize<'de> for BptreeMap +impl<'de, K, V, M> Deserialize<'de> for BptreeMap where K: Deserialize<'de> + Clone + Ord + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn deserialize(deserializer: D) -> Result where diff --git a/src/cowcell/asynch.rs b/src/cowcell/asynch.rs index 39dcae2..4525aaa 100644 --- a/src/cowcell/asynch.rs +++ b/src/cowcell/asynch.rs @@ -2,6 +2,7 @@ //! //! See `CowCell` for more details. +// We can use std here as the `asynch` feature requires the `std` feature use std::ops::{Deref, DerefMut}; use std::sync::Arc; use tokio::sync::{Mutex, MutexGuard}; diff --git a/src/cowcell/mod.rs b/src/cowcell/mod.rs index cbc6c28..359e2e6 100644 --- a/src/cowcell/mod.rs +++ b/src/cowcell/mod.rs @@ -12,9 +12,16 @@ #[cfg(feature = "asynch")] pub mod asynch; -use std::ops::{Deref, DerefMut}; + + +use core::ops::{Deref, DerefMut}; +use lock_api::{Mutex, MutexGuard, RawMutex}; + +#[cfg(not(feature = "std"))] +use alloc::sync::Arc; + +#[cfg(feature = "std")] use std::sync::Arc; -use std::sync::{Mutex, MutexGuard}; use arc_swap::ArcSwap; @@ -60,8 +67,8 @@ use arc_swap::ArcSwap; /// assert_eq!(*new_read_txn, 1); /// ``` #[derive(Debug, Default)] -pub struct CowCell { - write: Mutex<()>, +pub struct CowCell { + write: Mutex, active: ArcSwap, } @@ -74,13 +81,13 @@ pub struct CowCell { /// rollback a change, don't call commit and allow the write transaction to /// be dropped. This causes the `CowCell` to unlock allowing the next writer /// to proceed. -pub struct CowCellWriteTxn<'a, T> { +pub struct CowCellWriteTxn<'a, T, R: RawMutex> { // Hold open the guard, and initiate the copy to here. work: Option, read: Arc, // This way we know who to contact for updating our data .... - caller: &'a CowCell, - _guard: MutexGuard<'a, ()>, + caller: &'a CowCell, + _guard: MutexGuard<'a, R, ()>, } /// A `CowCell` Read Transaction handle. @@ -96,9 +103,10 @@ impl Clone for CowCellReadTxn { } } -impl CowCell +impl CowCell where T: Clone, + R: RawMutex { /// Create a new `CowCell` for storing type `T`. `T` must implement `Clone` /// to enable clone-on-write. @@ -121,9 +129,9 @@ where /// Begin a write transaction, returning a write guard. The content of the /// write is only visible to this thread, and is not visible to any reader /// until `commit()` is called. - pub fn write(&self) -> CowCellWriteTxn { + pub fn write(&self) -> CowCellWriteTxn<'_, T, R> { /* Take the exclusive write lock first */ - let mguard = self.write.lock().unwrap(); + let mguard = self.write.lock(); // We delay copying until the first get_mut. let read = self.active.load_full(); /* Now build the write struct */ @@ -138,9 +146,9 @@ where /// Attempt to create a write transaction. If it fails, and err /// is returned. On success the `Ok(guard)` is returned. See also /// `write(&self)` - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { /* Take the exclusive write lock first */ - self.write.try_lock().ok().map(|mguard| { + self.write.try_lock().map(|mguard| { // We delay copying until the first get_mut. let read = self.active.load_full(); /* Now build the write struct */ @@ -172,9 +180,10 @@ impl Deref for CowCellReadTxn { } } -impl CowCellWriteTxn<'_, T> +impl CowCellWriteTxn<'_, T, R> where T: Clone, + R: RawMutex { /// Access a mutable pointer of the data in the `CowCell`. This data is only /// visible to the write transaction object in this thread, until you call @@ -182,7 +191,7 @@ where pub fn get_mut(&mut self) -> &mut T { if self.work.is_none() { let mut data: Option = Some((*self.read).clone()); - std::mem::swap(&mut data, &mut self.work); + core::mem::swap(&mut data, &mut self.work); // Should be the none we previously had. debug_assert!(data.is_none()) } @@ -205,9 +214,10 @@ where } } -impl Deref for CowCellWriteTxn<'_, T> +impl Deref for CowCellWriteTxn<'_, T, R> where T: Clone, + R: RawMutex { type Target = T; @@ -220,9 +230,10 @@ where } } -impl DerefMut for CowCellWriteTxn<'_, T> +impl DerefMut for CowCellWriteTxn<'_, T, R> where T: Clone, + R: RawMutex { #[inline(always)] fn deref_mut(&mut self) -> &mut T { diff --git a/src/ebrcell/mod.rs b/src/ebrcell/mod.rs index d55b48d..a858b5f 100644 --- a/src/ebrcell/mod.rs +++ b/src/ebrcell/mod.rs @@ -15,12 +15,13 @@ //! or crossbeam library components. //! If you need accurate memory reclaim, use the Arc (`CowCell`) implementation. +use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crossbeam_epoch as epoch; use crossbeam_epoch::{Atomic, Guard, Owned}; -use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use std::mem; use std::ops::{Deref, DerefMut}; + use std::sync::{Mutex, MutexGuard}; /// An `EbrCell` Write Transaction handle. @@ -32,7 +33,10 @@ use std::sync::{Mutex, MutexGuard}; /// abort a change, don't call commit and allow the write transaction to /// go out of scope. This causes the `EbrCell` to unlock allowing other /// writes to proceed. -pub struct EbrCellWriteTxn<'a, T: 'static + Clone + Send + Sync> { +pub struct EbrCellWriteTxn< + 'a, + T: 'static + Clone + Send + Sync +> { data: Option, // This way we know who to contact for updating our data .... caller: &'a EbrCell, @@ -129,7 +133,10 @@ where /// assert_eq!(*new_read_txn, 1); /// ``` #[derive(Debug)] -pub struct EbrCell { +pub struct EbrCell +where + T: Clone + Sync + Send + 'static +{ write: Mutex<()>, active: Atomic, } @@ -150,7 +157,7 @@ where /// Create a new `EbrCell` storing type `T`. `T` must implement `Clone`. pub fn new(data: T) -> Self { EbrCell { - write: Mutex::new(()), + write: Mutex::<()>::new(()), active: Atomic::new(data), } } @@ -174,7 +181,7 @@ where /// Attempt to begin a write transaction. If it's already held, /// `None` is returned. pub fn try_write(&self) -> Option> { - self.write.try_lock().ok().map(|mguard| { + self.write.try_lock().map(|mguard| { let guard = epoch::pin(); let cur_shared = self.active.load(Acquire, &guard); /* Now build the write struct, we'll discard the pin shortly! */ @@ -184,7 +191,7 @@ where caller: self, _guard: mguard, } - }) + }).ok() } /// This is an internal component of the commit cycle. It takes ownership diff --git a/src/hashmap/impl.rs b/src/hashmap/impl.rs index 7d576a4..5e954d9 100644 --- a/src/hashmap/impl.rs +++ b/src/hashmap/impl.rs @@ -1,3 +1,5 @@ +use lock_api::RawMutex; + use crate::internals::hashmap::cursor::CursorReadOps; use crate::internals::hashmap::cursor::{CursorRead, CursorWrite, SuperBlock}; use crate::internals::hashmap::iter::*; @@ -27,32 +29,34 @@ use std::iter::FromIterator; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `HashMapWriteTxn` without calling `commit()`. -pub struct HashMap +pub struct HashMap where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCell, CursorRead, CursorWrite>, + inner: LinCowCell, CursorRead, CursorWrite, M>, } -unsafe impl - Send for HashMap +unsafe impl + Send for HashMap { } -unsafe impl - Sync for HashMap +unsafe impl + Sync for HashMap { } /// An active read transaction over a `HashMap`. The data in this tree /// is guaranteed to not change and will remain consistent for the life /// of this transaction. -pub struct HashMapReadTxn<'a, K, V> +pub struct HashMapReadTxn<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCellReadTxn<'a, SuperBlock, CursorRead, CursorWrite>, + inner: LinCowCellReadTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } /// An active write transaction for a `HashMap`. The data in this tree @@ -60,20 +64,22 @@ where /// readers. The write may be rolledback/aborted by dropping this guard /// without calling `commit()`. Once `commit()` is called, readers will be /// able to access and perceive changes in new transactions. -pub struct HashMapWriteTxn<'a, K, V> +pub struct HashMapWriteTxn<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite>, + inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } -enum SnapshotType<'a, K, V> +enum SnapshotType<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - R(&'a CursorRead), + R(&'a CursorRead), W(&'a CursorWrite), } @@ -85,29 +91,30 @@ where /// This snapshot IS safe within the read thread due to the nature of the /// implementation borrowing the inner tree to prevent mutations within the /// same thread while the read snapshot is open. -pub struct HashMapReadSnapshot<'a, K, V> +pub struct HashMapReadSnapshot<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: SnapshotType<'a, K, V>, + inner: SnapshotType<'a, K, V, M>, } -impl Default - for HashMap +impl Default + for HashMap { fn default() -> Self { Self::new() } } -impl - FromIterator<(K, V)> for HashMap +impl + FromIterator<(K, V)> for HashMap { fn from_iter>(iter: I) -> Self { let mut new_sblock = unsafe { SuperBlock::new() }; - let prev = new_sblock.create_reader(); - let mut cursor = new_sblock.create_writer(); + let prev: CursorRead = new_sblock.create_reader(); + let mut cursor = as LinCowCellCapable, CursorWrite>>::create_writer(&new_sblock); //new_sblock.create_writer(); cursor.extend(iter); let _ = new_sblock.pre_commit(cursor, &prev); @@ -118,16 +125,16 @@ impl - Extend<(K, V)> for HashMapWriteTxn<'_, K, V> +impl + Extend<(K, V)> for HashMapWriteTxn<'_, K, V, M> { fn extend>(&mut self, iter: I) { self.inner.as_mut().extend(iter); } } -impl - HashMapWriteTxn<'_, K, V> +impl + HashMapWriteTxn<'_, K, V, M> { /* pub(crate) fn prehash(&self, k: &Q) -> u64 @@ -225,15 +232,15 @@ impl HashMapReadSnapshot { + pub fn to_snapshot(&self) -> HashMapReadSnapshot { HashMapReadSnapshot { inner: SnapshotType::W(self.inner.as_ref()), } } } -impl - HashMapReadTxn<'_, K, V> +impl + HashMapReadTxn<'_, K, V, M> { pub(crate) fn get_prehashed(&self, k: &Q, k_hash: u64) -> Option<&V> where @@ -290,15 +297,15 @@ impl HashMapReadSnapshot { + pub fn to_snapshot(&self) -> HashMapReadSnapshot { HashMapReadSnapshot { inner: SnapshotType::R(self.inner.as_ref()), } } } -impl - HashMapReadSnapshot<'_, K, V> +impl + HashMapReadSnapshot<'_, K, V, M> { /// Retrieve a value from the tree. If the value exists, a reference is returned /// as `Some(&V)`, otherwise if not present `None` is returned. diff --git a/src/hashmap/mod.rs b/src/hashmap/mod.rs index 84e8730..92cf114 100644 --- a/src/hashmap/mod.rs +++ b/src/hashmap/mod.rs @@ -19,6 +19,7 @@ #![allow(clippy::implicit_hasher)] + #[cfg(feature = "asynch")] pub mod asynch; @@ -38,8 +39,8 @@ use crate::internals::lincowcell::{LinCowCell, LinCowCellReadTxn, LinCowCellWrit include!("impl.rs"); -impl - HashMap +impl + HashMap { /// Construct a new concurrent hashmap pub fn new() -> Self { @@ -51,29 +52,29 @@ impl HashMapReadTxn { + pub fn read(&self) -> HashMapReadTxn { let inner = self.inner.read(); HashMapReadTxn { inner } } /// Initiate a write transaction for the map, exclusive to this /// writer, and concurrently to all existing reads. - pub fn write(&self) -> HashMapWriteTxn { + pub fn write(&self) -> HashMapWriteTxn { let inner = self.inner.write(); HashMapWriteTxn { inner } } /// Attempt to create a new write, returns None if another writer /// already exists. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| HashMapWriteTxn { inner }) } } -impl - HashMapWriteTxn<'_, K, V> +impl + HashMapWriteTxn<'_, K, V, M> { #[cfg(all(feature = "arcache", feature = "arcache-is-hashmap"))] pub(crate) fn get_txid(&self) -> u64 { @@ -107,8 +108,8 @@ impl - HashMapReadTxn<'_, K, V> +impl + HashMapReadTxn<'_, K, V, M> { #[cfg(all(feature = "arcache", feature = "arcache-is-hashmap"))] pub(crate) fn get_txid(&self) -> u64 { @@ -126,10 +127,11 @@ impl Serialize for HashMapReadTxn<'_, K, V> +impl Serialize for HashMapReadTxn<'_, K, V, M> where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, + M: RawMutex { fn serialize(&self, serializer: S) -> Result where @@ -146,10 +148,11 @@ where } #[cfg(feature = "serde")] -impl Serialize for HashMap +impl Serialize for HashMap where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn serialize(&self, serializer: S) -> Result where @@ -160,10 +163,11 @@ where } #[cfg(feature = "serde")] -impl<'de, K, V> Deserialize<'de> for HashMap +impl<'de, K, V, M> Deserialize<'de> for HashMap where K: Deserialize<'de> + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn deserialize(deserializer: D) -> Result where diff --git a/src/hashtrie/impl.rs b/src/hashtrie/impl.rs index aae10da..5d9b823 100644 --- a/src/hashtrie/impl.rs +++ b/src/hashtrie/impl.rs @@ -1,3 +1,5 @@ +use lock_api::RawMutex; + use crate::internals::hashtrie::cursor::CursorReadOps; use crate::internals::hashtrie::cursor::{CursorRead, CursorWrite, SuperBlock}; use crate::internals::hashtrie::iter::*; @@ -25,32 +27,34 @@ use std::iter::FromIterator; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `HashTrieWriteTxn` without calling `commit()`. -pub struct HashTrie +pub struct HashTrie where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex + 'static { - inner: LinCowCell, CursorRead, CursorWrite>, + inner: LinCowCell, CursorRead, CursorWrite, M>, } -unsafe impl - Send for HashTrie +unsafe impl + Send for HashTrie { } -unsafe impl - Sync for HashTrie +unsafe impl + Sync for HashTrie { } /// An active read transaction over a `HashTrie`. The data in this tree /// is guaranteed to not change and will remain consistent for the life /// of this transaction. -pub struct HashTrieReadTxn<'a, K, V> +pub struct HashTrieReadTxn<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCellReadTxn<'a, SuperBlock, CursorRead, CursorWrite>, + inner: LinCowCellReadTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } /// An active write transaction for a `HashTrie`. The data in this tree @@ -58,20 +62,22 @@ where /// readers. The write may be rolledback/aborted by dropping this guard /// without calling `commit()`. Once `commit()` is called, readers will be /// able to access and perceive changes in new transactions. -pub struct HashTrieWriteTxn<'a, K, V> +pub struct HashTrieWriteTxn<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex { - inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite>, + inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } -enum SnapshotType<'a, K, V> +enum SnapshotType<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex + 'static { - R(&'a CursorRead), + R(&'a CursorRead), W(&'a CursorWrite), } @@ -83,29 +89,33 @@ where /// This snapshot IS safe within the read thread due to the nature of the /// implementation borrowing the inner tree to prevent mutations within the /// same thread while the read snapshot is open. -pub struct HashTrieReadSnapshot<'a, K, V> +pub struct HashTrieReadSnapshot<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, + M: RawMutex + 'static { - inner: SnapshotType<'a, K, V>, + inner: SnapshotType<'a, K, V, M>, } -impl Default - for HashTrie +impl Default + for HashTrie { fn default() -> Self { Self::new() } } -impl - FromIterator<(K, V)> for HashTrie +impl + FromIterator<(K, V)> for HashTrie { fn from_iter>(iter: I) -> Self { let mut new_sblock = unsafe { SuperBlock::new() }; - let prev = new_sblock.create_reader(); - let mut cursor = new_sblock.create_writer(); + let prev: CursorRead = new_sblock.create_reader(); + + // TODO - can we specify the bound some other way that doesn't make this type vomit? + use crate::internals::hashtrie::cursor; + let mut cursor = as LinCowCellCapable, cursor::CursorWrite>>::create_writer(&new_sblock); cursor.extend(iter); let _ = new_sblock.pre_commit(cursor, &prev); @@ -116,16 +126,16 @@ impl - Extend<(K, V)> for HashTrieWriteTxn<'_, K, V> +impl + Extend<(K, V)> for HashTrieWriteTxn<'_, K, V, M> { fn extend>(&mut self, iter: I) { self.inner.as_mut().extend(iter); } } -impl - HashTrieWriteTxn<'_, K, V> +impl + HashTrieWriteTxn<'_, K, V, M> { /* pub(crate) fn prehash(&self, k: &Q) -> u64 @@ -223,15 +233,15 @@ impl HashTrieReadSnapshot { + pub fn to_snapshot(&self) -> HashTrieReadSnapshot { HashTrieReadSnapshot { inner: SnapshotType::W(self.inner.as_ref()), } } } -impl - HashTrieReadTxn<'_, K, V> +impl + HashTrieReadTxn<'_, K, V, M> { pub(crate) fn get_prehashed(&self, k: &Q, k_hash: u64) -> Option<&V> where @@ -288,15 +298,15 @@ impl HashTrieReadSnapshot { + pub fn to_snapshot(&self) -> HashTrieReadSnapshot { HashTrieReadSnapshot { inner: SnapshotType::R(self.inner.as_ref()), } } } -impl - HashTrieReadSnapshot<'_, K, V> +impl + HashTrieReadSnapshot<'_, K, V, M> { /// Retrieve a value from the tree. If the value exists, a reference is returned /// as `Some(&V)`, otherwise if not present `None` is returned. diff --git a/src/hashtrie/mod.rs b/src/hashtrie/mod.rs index 0308aff..163f58a 100644 --- a/src/hashtrie/mod.rs +++ b/src/hashtrie/mod.rs @@ -43,8 +43,8 @@ use crate::internals::lincowcell::{LinCowCell, LinCowCellReadTxn, LinCowCellWrit include!("impl.rs"); -impl - HashTrie +impl + HashTrie { /// Construct a new concurrent hashtrie pub fn new() -> Self { @@ -56,29 +56,29 @@ impl HashTrieReadTxn { + pub fn read(&self) -> HashTrieReadTxn { let inner = self.inner.read(); HashTrieReadTxn { inner } } /// Initiate a write transaction for the map, exclusive to this /// writer, and concurrently to all existing reads. - pub fn write(&self) -> HashTrieWriteTxn { + pub fn write(&self) -> HashTrieWriteTxn { let inner = self.inner.write(); HashTrieWriteTxn { inner } } /// Attempt to create a new write, returns None if another writer /// already exists. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| HashTrieWriteTxn { inner }) } } -impl - HashTrieWriteTxn<'_, K, V> +impl + HashTrieWriteTxn<'_, K, V, M> { /// View the current transaction ID for this cache. This is a monotonically increasing /// value. If two transactions have the same txid, they are the same data generation. @@ -113,8 +113,8 @@ impl - HashTrieReadTxn<'_, K, V> +impl + HashTrieReadTxn<'_, K, V, M> { /// View the current transaction ID for this cache. This is a monotonically increasing /// value. If two transactions have the same txid, they are the same data generation. @@ -133,10 +133,11 @@ impl Serialize for HashTrieReadTxn<'_, K, V> +impl Serialize for HashTrieReadTxn<'_, K, V, M> where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn serialize(&self, serializer: S) -> Result where @@ -153,10 +154,11 @@ where } #[cfg(feature = "serde")] -impl Serialize for HashTrie +impl Serialize for HashTrie where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn serialize(&self, serializer: S) -> Result where @@ -167,10 +169,11 @@ where } #[cfg(feature = "serde")] -impl<'de, K, V> Deserialize<'de> for HashTrie +impl<'de, K, V, M> Deserialize<'de> for HashTrie where K: Deserialize<'de> + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, + M: RawMutex + 'static { fn deserialize(deserializer: D) -> Result where diff --git a/src/internals/bptree/cursor.rs b/src/internals/bptree/cursor.rs index 67b9a68..7999b3c 100644 --- a/src/internals/bptree/cursor.rs +++ b/src/internals/bptree/cursor.rs @@ -4,6 +4,13 @@ // Additionally, the cursor also is responsible for general movement // throughout the structure and how to handle that effectively +#[cfg(not(feature = "std"))] +use alloc::vec; +#[cfg(feature = "std")] +use std::vec; + +use vec::Vec; + use super::node::*; use crate::internals::lincowcell::LinCowCellCapable; use std::borrow::Borrow; @@ -15,7 +22,7 @@ use super::mutiter::RangeMutIter; use super::states::*; use std::ops::RangeBounds; -use std::sync::Mutex; +use lock_api::{Mutex, RawMutex}; /// The internal root of the tree, with associated garbage lists etc. #[derive(Debug)] @@ -38,10 +45,10 @@ unsafe impl LinCowCellCapable, CursorWrite> - for SuperBlock +impl + LinCowCellCapable, CursorWrite> for SuperBlock { - fn create_reader(&self) -> CursorRead { + fn create_reader(&self) -> CursorRead { // This sets up the first reader. CursorRead::new(self) } @@ -54,9 +61,9 @@ impl LinCowCellCapable, Curso fn pre_commit( &mut self, mut new: CursorWrite, - prev: &CursorRead, - ) -> CursorRead { - let mut prev_last_seen = prev.last_seen.lock().unwrap(); + prev: &CursorRead, + ) -> CursorRead { + let mut prev_last_seen = prev.last_seen.lock(); debug_assert!((*prev_last_seen).is_empty()); let new_last_seen = &mut new.last_seen; @@ -129,7 +136,7 @@ impl SuperBlock { } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Ord + Clone + Debug, V: Clone, @@ -137,15 +144,21 @@ where txid: u64, length: usize, root: *mut Node, - last_seen: Mutex>>, + last_seen: Mutex>>, } -unsafe impl Send - for CursorRead +unsafe impl< + K: Clone + Ord + Debug + Send + 'static, + V: Clone + Send + 'static, + R: RawMutex + Send + 'static, + > Send for CursorRead { } -unsafe impl Sync - for CursorRead +unsafe impl< + K: Clone + Ord + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + R: RawMutex + Send + Sync + 'static, + > Sync for CursorRead { } @@ -584,7 +597,7 @@ impl Drop for CursorWrite { } } -impl Drop for CursorRead { +impl Drop for CursorRead { fn drop(&mut self) { // If there is content in last_seen, a future generation wants us to remove it! let last_seen_guard = self @@ -609,7 +622,7 @@ impl Drop for SuperBlock { } } -impl CursorRead { +impl CursorRead { pub(crate) fn new(sblock: &SuperBlock) -> Self { // println!("starting rd txid -> {:?}", sblock.txid); CursorRead { @@ -621,7 +634,7 @@ impl CursorRead { } } -impl CursorReadOps for CursorRead { +impl CursorReadOps for CursorRead { fn get_root_ref(&self) -> &Node { unsafe { &*(self.root) } } diff --git a/src/internals/bptree/iter.rs b/src/internals/bptree/iter.rs index b251c22..541847f 100644 --- a/src/internals/bptree/iter.rs +++ b/src/internals/bptree/iter.rs @@ -1,12 +1,16 @@ //! Iterators for the map. +#[cfg(feature = "std")] +use std::collections::VecDeque; +#[cfg(not(feature = "std"))] +use alloc::collections::VecDeque; + // Iterators for the bptree use super::node::{Branch, Leaf, Meta, Node}; -use std::borrow::Borrow; -use std::collections::VecDeque; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::ops::{Bound, RangeBounds}; +use core::borrow::Borrow; +use core::fmt::Debug; +use core::marker::PhantomData; +use core::ops::{Bound, RangeBounds}; pub(crate) struct LeafIter<'a, K, V> where diff --git a/src/internals/bptree/mutiter.rs b/src/internals/bptree/mutiter.rs index 155c593..cf6eaa9 100644 --- a/src/internals/bptree/mutiter.rs +++ b/src/internals/bptree/mutiter.rs @@ -81,8 +81,7 @@ mod tests { use super::super::cursor::SuperBlock; use super::super::node::{Leaf, Node, L_CAPACITY}; use super::RangeMutIter; - use std::ops::Bound; - use std::ops::Bound::*; + use std::ops::Bound::{self, *}; use crate::internals::lincowcell::LinCowCellCapable; diff --git a/src/internals/bptree/node.rs b/src/internals/bptree/node.rs index a891c2e..379e387 100644 --- a/src/internals/bptree/node.rs +++ b/src/internals/bptree/node.rs @@ -1,18 +1,27 @@ use super::states::*; use crate::utils::*; // use libc::{c_void, mprotect, PROT_READ, PROT_WRITE}; -use crossbeam_utils::CachePadded; use std::borrow::Borrow; use std::fmt::{self, Debug, Error}; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::ptr; use std::slice; +use crossbeam_utils::CachePadded; + +#[cfg(feature = "std")] +use std::{boxed, vec}; +#[cfg(not(feature = "std"))] +use alloc::{boxed, vec}; + +use boxed::Box; +use vec::Vec; + -#[cfg(test)] -use std::collections::BTreeSet; #[cfg(all(test, not(miri)))] use std::sync::atomic::{AtomicUsize, Ordering}; +#[cfg(test)] +use std::collections::BTreeSet; #[cfg(all(test, not(miri)))] use std::sync::Mutex; @@ -649,7 +658,7 @@ impl Leaf { nid: alloc_nid(), })); - debug_assert!((x.meta.0 & FLAG_INVALID) != 0); + debug_assert!(0u64 != (x.meta.0 & FLAG_INVALID)); // Copy in the values to the correct location. for idx in 0..self.count() { @@ -663,7 +672,7 @@ impl Leaf { // Finally undo the invalid flag to allow drop to proceed. x.meta.0 &= !FLAG_INVALID; - debug_assert!((x.meta.0 & FLAG_INVALID) == 0); + debug_assert!(0u64 == (x.meta.0 & FLAG_INVALID)); Some(Box::into_raw(x) as *mut Node) } @@ -844,11 +853,11 @@ impl Leaf { let rk: &K = unsafe { &*self.key[work_idx].as_ptr() }; if lk >= rk { // println!("{:?}", self); - if cfg!(test) { + cfg_if::cfg_if! {if #[cfg(test)] { return false; } else { debug_assert!(false); - } + }} } lk = rk; } @@ -868,11 +877,11 @@ impl Leaf { let rk: &K = unsafe { &*(*pointer).key[work_idx].as_ptr() }; if lk >= rk { // println!("{:?}", self); - if cfg!(test) { + cfg_if::cfg_if!{ if #[cfg(test)] { return false; } else { debug_assert!(false); - } + }} } lk = rk; } @@ -1008,7 +1017,7 @@ impl Branch { nid: alloc_nid(), })); - debug_assert!((x.meta.0 & FLAG_INVALID) != 0); + debug_assert!(0u64 != (x.meta.0 & FLAG_INVALID)); // Copy in the keys to the correct location. for idx in 0..self.count() { @@ -1020,7 +1029,7 @@ impl Branch { // Finally undo the invalid flag to allow drop to proceed. x.meta.0 &= !FLAG_INVALID; - debug_assert!((x.meta.0 & FLAG_INVALID) == 0); + debug_assert!(0u64 == (x.meta.0 & FLAG_INVALID)); Some(Box::into_raw(x) as *mut Node) } diff --git a/src/internals/hashmap/cursor.rs b/src/internals/hashmap/cursor.rs index 8ce8dc5..0570d16 100644 --- a/src/internals/hashmap/cursor.rs +++ b/src/internals/hashmap/cursor.rs @@ -9,16 +9,24 @@ use std::borrow::Borrow; use std::fmt::Debug; use std::mem; -#[cfg(feature = "ahash")] +#[cfg(not(feature = "std"))] +use alloc::vec; +#[cfg(feature = "std")] +use std::vec; + +use vec::Vec; + +#[cfg(any(feature = "ahash", not(feature = "std")))] use ahash::RandomState; -#[cfg(not(feature = "ahash"))] + +#[cfg(all(not(feature = "ahash"), feature = "std"))] use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash, Hasher}; use super::iter::{Iter, KeyIter, ValueIter}; use super::states::*; -use std::sync::Mutex; +use lock_api::{Mutex, RawMutex}; use crate::internals::lincowcell::LinCowCellCapable; @@ -57,10 +65,10 @@ unsafe impl LinCowCellCapable, CursorWrite> - for SuperBlock +impl + LinCowCellCapable, CursorWrite> for SuperBlock { - fn create_reader(&self) -> CursorRead { + fn create_reader(&self) -> CursorRead { CursorRead::new(self) } @@ -71,9 +79,9 @@ impl LinCowCellCapable, fn pre_commit( &mut self, mut new: CursorWrite, - prev: &CursorRead, - ) -> CursorRead { - let mut prev_last_seen = prev.last_seen.lock().unwrap(); + prev: &CursorRead, + ) -> CursorRead { + let mut prev_last_seen = prev.last_seen.lock(); debug_assert!((*prev_last_seen).is_empty()); let new_last_seen = &mut new.last_seen; @@ -134,25 +142,32 @@ impl SuperBlock { } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Hash + Eq + Clone + Debug, V: Clone, + M: RawMutex, { #[allow(dead_code)] txid: u64, length: usize, root: *mut Node, - last_seen: Mutex>>, + last_seen: Mutex>>, build_hasher: RandomState, } -unsafe impl Send - for CursorRead +unsafe impl< + K: Clone + Hash + Eq + Debug + Send + 'static, + V: Clone + Send + 'static, + M: RawMutex + Send + 'static, + > Send for CursorRead { } -unsafe impl - Sync for CursorRead +unsafe impl< + K: Clone + Hash + Eq + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + Send + Sync + 'static, + > Sync for CursorRead { } @@ -502,7 +517,7 @@ impl Drop for CursorWrite { } } -impl Drop for CursorRead { +impl Drop for CursorRead { fn drop(&mut self) { // If there is content in last_seen, a future generation wants us to remove it! let last_seen_guard = self @@ -527,7 +542,7 @@ impl Drop for SuperBlock { } } -impl CursorRead { +impl CursorRead { pub(crate) fn new(sblock: &SuperBlock) -> Self { // println!("starting rd txid -> {:?}", sblock.txid); let build_hasher = sblock.build_hasher.clone(); @@ -549,7 +564,9 @@ impl Drop for CursorRead { } */ -impl CursorReadOps for CursorRead { +impl CursorReadOps + for CursorRead +{ fn get_root_ref(&self) -> &Node { unsafe { &*(self.root) } } diff --git a/src/internals/hashmap/iter.rs b/src/internals/hashmap/iter.rs index d7e371c..fbfb846 100644 --- a/src/internals/hashmap/iter.rs +++ b/src/internals/hashmap/iter.rs @@ -1,11 +1,15 @@ //! Iterators for the map. +#[cfg(feature = "std")] +use std::collections::VecDeque; +#[cfg(not(feature = "std"))] +use alloc::collections::VecDeque; + // Iterators for the bptree use super::node::{Branch, Leaf, Meta, Node}; -use std::collections::VecDeque; -use std::fmt::Debug; -use std::hash::Hash; -use std::marker::PhantomData; +use core::fmt::Debug; +use core::hash::Hash; +use core::marker::PhantomData; pub(crate) struct LeafIter<'a, K, V> where diff --git a/src/internals/hashmap/macros.rs b/src/internals/hashmap/macros.rs index 8fd4acf..a70cf51 100644 --- a/src/internals/hashmap/macros.rs +++ b/src/internals/hashmap/macros.rs @@ -31,7 +31,7 @@ macro_rules! branch_ref { ($x:expr, $k:ty, $v:ty) => {{ #[allow(unused_unsafe)] unsafe { - debug_assert!(unsafe { (*$x).ctrl.a.0.is_branch() }); + debug_assert!(unsafe { $x.as_ref().unwrap().ctrl.a.0.is_branch() }); &mut *($x as *mut Branch<$k, $v>) } }}; @@ -41,7 +41,7 @@ macro_rules! leaf_ref { ($x:expr, $k:ty, $v:ty) => {{ #[allow(unused_unsafe)] unsafe { - debug_assert!(unsafe { (*$x).ctrl.a.0.is_leaf() }); + debug_assert!(unsafe { $x.as_ref().unwrap().ctrl.a.0.is_leaf() }); &mut *($x as *mut Leaf<$k, $v>) } }}; diff --git a/src/internals/hashmap/node.rs b/src/internals/hashmap/node.rs index 1a5f69f..483eafb 100644 --- a/src/internals/hashmap/node.rs +++ b/src/internals/hashmap/node.rs @@ -14,7 +14,15 @@ use std::ptr; use smallvec::SmallVec; #[cfg(feature = "simd_support")] -use core_simd::u64x8; +use std::simd::u64x8; + +#[cfg(feature = "std")] +use std::{boxed, vec}; +#[cfg(not(feature = "std"))] +use alloc::{boxed, vec}; + +use boxed::Box; +use vec::Vec; #[cfg(test)] use std::collections::BTreeSet; @@ -96,6 +104,7 @@ pub(crate) fn assert_released() { } } +#[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Meta(u64); @@ -652,7 +661,7 @@ impl Leaf { for idx in 0..self.slots() { unsafe { let lvalue: Bucket = (*self.values[idx].as_ptr()).clone(); - (*x).values[idx].as_mut_ptr().write(lvalue); + x.as_mut().unwrap().values[idx].write(lvalue); } } @@ -903,11 +912,11 @@ impl Leaf { // Eq not ok as we have buckets. if lk >= rk { // println!("{:?}", self); - if cfg!(test) { + cfg_if::cfg_if! { if #[cfg(test)] { return false; } else { debug_assert!(false); - } + }} } lk = rk; } @@ -1468,7 +1477,7 @@ impl Branch { debug_assert!(!left.is_null()); debug_assert!(!right.is_null()); - match unsafe { (*left).ctrl.a.0 .0 & FLAG_MASK } { + match unsafe { left.as_ref().unwrap().ctrl.a.0 .0 & FLAG_MASK } { FLAG_HASH_LEAF => { let lmut = leaf_ref!(left, K, V); let rmut = leaf_ref!(right, K, V); @@ -1781,7 +1790,7 @@ impl Branch { let sib_ptr = self.nodes[idx]; debug_assert!(!sib_ptr.is_null()); // Do we need to clone? - let res = match unsafe { (*sib_ptr).ctrl.a.0 .0 } & FLAG_MASK { + let res = match unsafe { sib_ptr.as_ref().unwrap().ctrl.a.0 .0 } & FLAG_MASK { FLAG_HASH_LEAF => { let lref = unsafe { &*(sib_ptr as *const _ as *const Leaf) }; lref.req_clone(txid) @@ -1938,6 +1947,7 @@ impl Branch { // Check everything above slots is u64::max for work_idx in unsafe { self.ctrl.a.0.slots() }..H_CAPACITY { if unsafe { self.ctrl.a.1[work_idx] } != u64::MAX { + #[cfg(feature = "std")] eprintln!("FAILED ARRAY -> {:?}", unsafe { self.ctrl.a.1 }); debug_assert!(false); } diff --git a/src/internals/hashmap/simd.rs b/src/internals/hashmap/simd.rs index 6d3b27b..f4d063a 100644 --- a/src/internals/hashmap/simd.rs +++ b/src/internals/hashmap/simd.rs @@ -1,5 +1,5 @@ #[cfg(feature = "simd_support")] -use core_simd::u64x8; +use std::simd::u64x8; use std::borrow::Borrow; use std::fmt::Debug; use std::hash::Hash; diff --git a/src/internals/hashtrie/cursor.rs b/src/internals/hashtrie/cursor.rs index 35d7c7c..9ef3404 100644 --- a/src/internals/hashtrie/cursor.rs +++ b/src/internals/hashtrie/cursor.rs @@ -4,23 +4,33 @@ //! Additionally, the cursor also is responsible for general movement //! throughout the structure and how to handle that effectively + +#[cfg(feature = "std")] +use std::{boxed, vec, collections}; +#[cfg(not(feature = "std"))] +use alloc::{boxed, vec, collections}; + +use boxed::Box; +use vec::Vec; + use crate::internals::lincowcell::LinCowCellCapable; use std::borrow::Borrow; use std::cmp::Ordering; -use std::collections::{BTreeSet, VecDeque}; -use std::fmt::{self, Debug}; +use collections::{BTreeSet, VecDeque}; +use std::fmt; +use std::fmt::Debug; use std::marker::PhantomData; use std::ptr; -use std::sync::Mutex; +use lock_api::{Mutex, RawMutex}; use smallvec::SmallVec; use super::iter::*; -#[cfg(feature = "ahash")] +#[cfg(any(feature = "ahash", not(feature = "std")))] use ahash::RandomState; -#[cfg(not(feature = "ahash"))] +#[cfg(all(not(feature = "ahash"), feature = "std"))] use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash, Hasher}; @@ -432,10 +442,10 @@ impl SuperBlock { } } -impl LinCowCellCapable, CursorWrite> +impl LinCowCellCapable, CursorWrite> for SuperBlock { - fn create_reader(&self) -> CursorRead { + fn create_reader(&self) -> CursorRead { CursorRead::new(self) } @@ -446,9 +456,9 @@ impl LinCowCellCapable, fn pre_commit( &mut self, mut new: CursorWrite, - prev: &CursorRead, - ) -> CursorRead { - let mut prev_last_seen = prev.last_seen.lock().unwrap(); + prev: &CursorRead, + ) -> CursorRead { + let mut prev_last_seen = prev.last_seen.lock(); debug_assert!((*prev_last_seen).is_empty()); let new_last_seen = &mut new.last_seen; @@ -670,11 +680,11 @@ impl CursorWrite { } } - if cfg!(debug_assertions) { + cfg_if::cfg_if! {if #[cfg(debug_assertions)] { for n in tgt_ptr.as_branch::().nodes.iter() { assert!(n.is_null() || !n.is_dirty()); } - } + }} } } @@ -850,7 +860,7 @@ impl CursorWrite { let tgt_bkt_mut = tgt_ptr.as_bucket_mut::(); let Datum { v, .. } = tgt_bkt_mut.remove(0); // Keep any pointer that ISN'T the one we are oob freeing. - self.first_seen.retain(|e| *e != tgt_ptr); + self.first_seen.retain(|e: &Ptr| *e != tgt_ptr); tgt_ptr.free::(); v } else { @@ -1042,7 +1052,7 @@ impl CursorReadOps for CursorWrite } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Hash + Eq + Clone + Debug, V: Clone, @@ -1050,13 +1060,13 @@ where txid: u64, length: usize, root: Ptr, - last_seen: Mutex>, + last_seen: Mutex>, build_hasher: RandomState, k: PhantomData, v: PhantomData, } -impl CursorRead { +impl CursorRead { pub(crate) fn new(sblock: &SuperBlock) -> Self { let build_hasher = sblock.build_hasher.clone(); CursorRead { @@ -1071,7 +1081,7 @@ impl CursorRead { } } -impl Drop for CursorRead { +impl Drop for CursorRead { fn drop(&mut self) { let last_seen_guard = self .last_seen @@ -1082,7 +1092,7 @@ impl Drop for CursorRead { } } -impl CursorReadOps for CursorRead { +impl CursorReadOps for CursorRead { fn get_root_ptr(&self) -> Ptr { self.root } diff --git a/src/internals/hashtrie/iter.rs b/src/internals/hashtrie/iter.rs index a7e33c6..50968ad 100644 --- a/src/internals/hashtrie/iter.rs +++ b/src/internals/hashtrie/iter.rs @@ -1,10 +1,14 @@ //! Iterators for the hashtrie -use super::cursor::{Ptr, HT_CAPACITY, MAX_HEIGHT}; +#[cfg(feature = "std")] use std::collections::VecDeque; -use std::fmt::Debug; -use std::hash::Hash; -use std::marker::PhantomData; +#[cfg(not(feature = "std"))] +use alloc::collections::VecDeque; + +use super::cursor::{Ptr, HT_CAPACITY, MAX_HEIGHT}; +use core::fmt::Debug; +use core::hash::Hash; +use core::marker::PhantomData; /// Iterator over references to Key Value pairs stored in the map. pub struct Iter<'a, K, V> diff --git a/src/internals/lincowcell/mod.rs b/src/internals/lincowcell/mod.rs index a2726e3..a3ab6ce 100644 --- a/src/internals/lincowcell/mod.rs +++ b/src/internals/lincowcell/mod.rs @@ -55,11 +55,16 @@ * */ +#[cfg(feature = "std")] +use std::sync::Arc; +#[cfg(not(feature = "std"))] +use alloc::sync::Arc; + use std::marker::PhantomData; use std::ops::Deref; use std::ops::DerefMut; -use std::sync::Arc; -use std::sync::{Mutex, MutexGuard}; +use lock_api::RawMutex; +use lock_api::{Mutex, MutexGuard}; use arc_swap::{ArcSwap, ArcSwapOption}; @@ -79,18 +84,18 @@ pub trait LinCowCellCapable { #[derive(Debug)] /// A concurrently readable cell with linearised drop behaviour. -pub struct LinCowCell { +pub struct LinCowCell { updater: PhantomData, - write: Mutex, + write: Mutex, active: ArcSwap>, } #[derive(Debug)] /// A write txn over a linear cell. -pub struct LinCowCellWriteTxn<'a, T, R, U> { +pub struct LinCowCellWriteTxn<'a, T, R, U, M: RawMutex> { // This way we know who to contact for updating our data .... - caller: &'a LinCowCell, - guard: MutexGuard<'a, T>, + caller: &'a LinCowCell, + guard: MutexGuard<'a, M, T>, work: U, } @@ -103,9 +108,9 @@ struct LinCowCellInner { #[derive(Debug)] /// A read txn over a linear cell. -pub struct LinCowCellReadTxn<'a, T, R, U> { +pub struct LinCowCellReadTxn<'a, T, R, U, M: RawMutex> { // We must outlive the root - _caller: &'a LinCowCell, + _caller: &'a LinCowCell, // We pin the current version. work: Arc>, } @@ -119,9 +124,10 @@ impl LinCowCellInner { } } -impl LinCowCell +impl LinCowCell where T: LinCowCellCapable, + M: RawMutex { /// Create a new linear 🐄 cell. pub fn new(data: T) -> Self { @@ -134,7 +140,7 @@ where } /// Begin a read txn - pub fn read(&self) -> LinCowCellReadTxn { + pub fn read(&self) -> LinCowCellReadTxn { // inc the arc. let work = self.active.load_full(); LinCowCellReadTxn { @@ -144,9 +150,9 @@ where } /// Begin a write txn - pub fn write(&self) -> LinCowCellWriteTxn { + pub fn write(&self) -> LinCowCellWriteTxn { /* Take the exclusive write lock first */ - let write_guard = self.write.lock().unwrap(); + let write_guard = self.write.lock(); /* Now take a ro-txn to get the data copied */ // let active_guard = self.active.lock(); /* This copies the data */ @@ -160,8 +166,8 @@ where } /// Attempt a write txn - pub fn try_write(&self) -> Option> { - self.write.try_lock().ok().map(|write_guard| { + pub fn try_write(&self) -> Option> { + self.write.try_lock().map(|write_guard| { /* This copies the data */ let work: U = (*write_guard).create_writer(); /* Now build the write struct */ @@ -173,7 +179,7 @@ where }) } - fn commit(&self, write: LinCowCellWriteTxn) { + fn commit(&self, write: LinCowCellWriteTxn) { // Destructure our writer. let LinCowCellWriteTxn { // This is self. @@ -199,7 +205,7 @@ where } } -impl Deref for LinCowCellReadTxn<'_, T, R, U> { +impl Deref for LinCowCellReadTxn<'_, T, R, U, M> { type Target = R; #[inline] @@ -208,16 +214,17 @@ impl Deref for LinCowCellReadTxn<'_, T, R, U> { } } -impl AsRef for LinCowCellReadTxn<'_, T, R, U> { +impl AsRef for LinCowCellReadTxn<'_, T, R, U, M> { #[inline] fn as_ref(&self) -> &R { &self.work.data } } -impl LinCowCellWriteTxn<'_, T, R, U> +impl LinCowCellWriteTxn<'_, T, R, U, M> where T: LinCowCellCapable, + M: RawMutex { #[inline] /// Get the mutable inner of this type @@ -232,7 +239,7 @@ where } } -impl Deref for LinCowCellWriteTxn<'_, T, R, U> { +impl Deref for LinCowCellWriteTxn<'_, T, R, U, M> { type Target = U; #[inline] @@ -241,21 +248,21 @@ impl Deref for LinCowCellWriteTxn<'_, T, R, U> { } } -impl DerefMut for LinCowCellWriteTxn<'_, T, R, U> { +impl DerefMut for LinCowCellWriteTxn<'_, T, R, U, M> { #[inline] fn deref_mut(&mut self) -> &mut U { &mut self.work } } -impl AsRef for LinCowCellWriteTxn<'_, T, R, U> { +impl AsRef for LinCowCellWriteTxn<'_, T, R, U, M> { #[inline] fn as_ref(&self) -> &U { &self.work } } -impl AsMut for LinCowCellWriteTxn<'_, T, R, U> { +impl AsMut for LinCowCellWriteTxn<'_, T, R, U, M> { #[inline] fn as_mut(&mut self) -> &mut U { &mut self.work diff --git a/src/lib.rs b/src/lib.rs index 2bad767..7fa87c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,12 +34,25 @@ //! By default all of these features are enabled. If you are planning to use this crate in a wasm //! context we recommend you use only `maps` as a feature. + +//#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] + #![deny(warnings)] #![warn(unused_extern_crates)] #![warn(missing_docs)] #![allow(clippy::needless_lifetimes)] #![cfg_attr(feature = "simd_support", feature(portable_simd))] +// TODO - can I remove this? Need a backup to tell if we can use AtomicUsize +//#![feature(cfg_target_has_atomic)] + +#[cfg(not(any(test, feature = "std")))] +extern crate alloc; + +#[cfg(any(test, feature = "std"))] +extern crate std; + #[cfg(all(test, feature = "dhat-heap"))] #[global_allocator] static ALLOC: dhat::Alloc = dhat::Alloc; @@ -64,8 +77,11 @@ pub mod threadcache; // This is where the scary rust lives. #[cfg(feature = "maps")] pub mod internals; + // This is where the good rust lives. -#[cfg(feature = "maps")] +// We're allowing unuzed here since we may or may not use all items based on enabled features +// All potentially incompatible features must be feature gated internally. +#[allow(unused)] mod utils; #[cfg(feature = "maps")] diff --git a/src/unsound3.rs b/src/unsound3.rs index 89b27bf..c0f380a 100644 --- a/src/unsound3.rs +++ b/src/unsound3.rs @@ -13,7 +13,7 @@ enum RefOrInt<'a> { Int(u64), } -#[cfg(feature = "unsoundness")] +#[cfg(all(feature = "unsoundness", feature = "std"))] fn main() { use concread::arcache::ARCache; use std::cell::Cell; diff --git a/src/utils.rs b/src/utils.rs index 927008a..9d279fa 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,13 +1,13 @@ -use std::borrow::Borrow; -use std::cmp::Ordering; -// use std::mem::MaybeUninit; +use core::borrow::Borrow; +use core::cmp::{Ordering, PartialOrd}; +// use core::mem::MaybeUninit; #[cfg(feature = "serde")] -use std::fmt; +use core::fmt; #[cfg(feature = "serde")] -use std::iter; +use core::iter; #[cfg(feature = "serde")] -use std::marker::PhantomData; -use std::ptr; +use core::marker::PhantomData; +use core::ptr; #[cfg(feature = "serde")] use serde::de::{Deserialize, MapAccess, Visitor}; @@ -86,17 +86,17 @@ where Err(slice.len()) } -#[cfg(feature = "serde")] +#[cfg(all(feature = "serde", feature = "maps"))] pub struct MapCollector(PhantomData<(T, K, V)>); -#[cfg(feature = "serde")] +#[cfg(all(feature = "serde", feature = "maps"))] impl MapCollector { pub fn new() -> Self { Self(PhantomData) } } -#[cfg(feature = "serde")] +#[cfg(all(feature = "serde", feature = "maps"))] impl<'de, T, K, V> Visitor<'de> for MapCollector where T: FromIterator<(K, V)>, @@ -116,3 +116,36 @@ where iter::from_fn(|| access.next_entry().transpose()).collect() } } + + +/// This is intended for comparing the insertion times of items into the ArCache type. +/// This would Default to an implementation over the Instant type on std, but could be an atomic counter with a caller-defined bit width in no_std environments. +/// +/// SAFETY: This has been marked unsafe as there is a behaviour contract on the `next` function that will not be checked by the caller. Subsequent calls to `next` should +/// ALWAYS return an equal or greater value (based on the type's impl of PartialOrd) +pub unsafe trait Monotonic { + type Output: PartialOrd + Copy; + /// Create a new instance, taking no arguments - this type shoud be instantiated without runtime generated inputs. + fn new() -> Self; + /// Gets the current value - provides an option for introspection where the value can change without calls to `next`, + /// but they don't _have_ to changed without `next`. + fn current(&self) -> Self::Output; + fn next(&self) -> Self::Output; +} + + + +// provide default locking types +#[cfg(feature = "std")] +#[allow(unused)] +pub type DefaultRawMutex = parking_lot::RawMutex; +/// Provide a defaulkt raw mutex implementation for no_std environments via spinning +#[cfg(not(feature = "std"))] +#[allow(unused)] +pub type DefaultRawMutex = spin::mutex::SpinMutex<()>; + +#[cfg(feature = "std")] +pub type DefaultRawRwLock = parking_lot::RawRwLock; +#[cfg(not(feature = "std"))] +/// Provide a defaulkt raw mutex implementation for no_std environments via spinning +pub type DefaultRawRwLock = spin::RwLock<()>; \ No newline at end of file From d0c038cee8e69fc026b36d7a31c0ba401684257d Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Thu, 10 Jul 2025 14:18:53 +0930 Subject: [PATCH 02/10] Fixups after merging the latest master commits --- Cargo.toml | 2 +- benches/hashmap_benchmark.rs | 13 +-- src/bptree/asynch.rs | 22 ++--- src/bptree/impl.rs | 16 ++-- src/bptree/mod.rs | 14 +-- src/cowcell/mod.rs | 38 +++++---- src/ebrcell/mod.rs | 2 +- src/hashmap/asynch.rs | 14 +-- src/hashmap/impl.rs | 77 +++++++++++------ src/hashtrie/asynch.rs | 14 +-- src/hashtrie/impl.rs | 4 +- src/hashtrie/mod.rs | 4 +- src/internals/bptree/cursor.rs | 117 +++++++++++++------------- src/internals/bptree/mutiter.rs | 3 +- src/internals/hashmap/cursor.rs | 95 ++++++++++----------- src/internals/hashtrie/cursor.rs | 57 +++++++------ src/internals/lincowcell/mod.rs | 54 ++++-------- src/internals/lincowcell_async/mod.rs | 50 ++++++----- src/lc_tests.rs | 10 +-- tests/bptree_map.rs | 8 +- 20 files changed, 318 insertions(+), 296 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca6ec65..85bb9b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ tcache = [] std = ["ahash/std", "ahash/runtime-rng", "crossbeam-epoch/std", "crossbeam-queue/std", "crossbeam-utils/std", "tracing/std", "dep:parking_lot", "smallvec/write"] #serde = ["lock_api/serde"] -no_std = ["crossbeam-epoch/alloc", "crossbeam-queue/alloc", "serde/alloc", "dep:spin", "ahash", "arc-swap/experimental-thread-local"] +no_std = ["crossbeam-epoch/alloc", "crossbeam-queue/alloc", "serde/alloc", "dep:spin", "ahash"] # Internal features for tweaking some align/perf behaviours. dhat-heap = ["dep:dhat"] diff --git a/benches/hashmap_benchmark.rs b/benches/hashmap_benchmark.rs index 53de162..62dc534 100644 --- a/benches/hashmap_benchmark.rs +++ b/benches/hashmap_benchmark.rs @@ -20,6 +20,7 @@ extern crate rand; use concread::hashmap::*; use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +use lock_api::RawMutex; use rand::{thread_rng, Rng}; // ranges of counts for different benchmarks (MINs are inclusive, MAXes exclusive): @@ -165,10 +166,10 @@ criterion_main!(insert, remove, search); // Utility functions: -fn insert_vec( - map: &mut HashMap, +fn insert_vec( + map: &mut HashMap, list: Vec<(u32, V)>, -) -> HashMapWriteTxn { +) -> HashMapWriteTxn { let mut write_txn = map.write(); for (key, val) in list.into_iter() { write_txn.insert(key, val); @@ -176,10 +177,10 @@ fn insert_vec( write_txn } -fn remove_vec<'a, V: Clone + Sync + Send + 'static>( - map: &'a mut HashMap, +fn remove_vec<'a, V: Clone + Sync + Send + 'static, M: RawMutex + 'static>( + map: &'a mut HashMap, list: &Vec, -) -> HashMapWriteTxn<'a, u32, V> { +) -> HashMapWriteTxn<'a, u32, V, M> { let mut write_txn = map.write(); for i in list.iter() { write_txn.remove(i); diff --git a/src/bptree/asynch.rs b/src/bptree/asynch.rs index 3dfb7a7..bda9ede 100644 --- a/src/bptree/asynch.rs +++ b/src/bptree/asynch.rs @@ -13,26 +13,26 @@ use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCe include!("impl.rs"); -impl - BptreeMap +impl + BptreeMap { /// Initiate a read transaction for the tree, concurrent to any /// other readers or writers. - pub fn read<'x>(&'x self) -> BptreeMapReadTxn<'x, K, V> { + pub fn read<'x>(&'x self) -> BptreeMapReadTxn<'x, K, V, R> { let inner = self.inner.read(); BptreeMapReadTxn { inner } } /// Initiate a write transaction for the tree, exclusive to this /// writer, and concurrently to all existing reads. - pub async fn write<'x>(&'x self) -> BptreeMapWriteTxn<'x, K, V> { + pub async fn write<'x>(&'x self) -> BptreeMapWriteTxn<'x, K, V, R> { let inner = self.inner.write().await; BptreeMapWriteTxn { inner } } } -impl - BptreeMapWriteTxn<'_, K, V> +impl + BptreeMapWriteTxn<'_, K, V, R> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceive these changes. @@ -169,7 +169,7 @@ mod tests { async fn test_bptree2_map_from_iter_1() { let ins: Vec = (0..(L_CAPACITY << 4)).collect(); - let map = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); + let map: BptreeMap = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); { let w = map.write().await; @@ -187,7 +187,7 @@ mod tests { let mut ins: Vec = (0..(L_CAPACITY << 4)).collect(); ins.shuffle(&mut rng); - let map = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); + let map: BptreeMap = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); { let w = map.write().await; @@ -203,7 +203,7 @@ mod tests { async fn bptree_map_basic_concurrency(lower: usize, upper: usize) { // Create a map - let map = BptreeMap::new(); + let map: BptreeMap = BptreeMap::new(); // add values { @@ -268,10 +268,10 @@ mod tests { // Need to ensure that txns are dropped in order. // Add data, enough to cause a split. All data should be *2 - let map = BptreeMap::new(); + let map: BptreeMap = BptreeMap::new(); // add values { - let mut w: crate::bptree::asynch::BptreeMapWriteTxn<'_, usize, usize> = map.write().await; + let mut w = map.write().await; w.extend((0..(L_CAPACITY * 2)).map(|v| (v * 2, v * 2))); w.commit(); } diff --git a/src/bptree/impl.rs b/src/bptree/impl.rs index 9551cba..377db4f 100644 --- a/src/bptree/impl.rs +++ b/src/bptree/impl.rs @@ -31,11 +31,11 @@ use std::ops::RangeBounds; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `BptreeMapWriteTxn` without calling `commit()`. -pub struct BptreeMap +pub struct BptreeMap where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex + 'static { inner: LinCowCell, CursorRead, CursorWrite, M>, } @@ -79,7 +79,7 @@ pub struct BptreeMapWriteTxn<'a, K, V, M> where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex + 'static { inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } @@ -119,8 +119,8 @@ impl - BptreeMap +impl + BptreeMap { /// Construct a new concurrent tree pub fn new() -> Self { @@ -132,7 +132,7 @@ impl Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| BptreeMapWriteTxn { inner }) @@ -315,7 +315,7 @@ impl BptreeMapReadSnapshot<'_, K, V> { + pub fn to_snapshot(&self) -> BptreeMapReadSnapshot<'_, K, V, M> { BptreeMapReadSnapshot { inner: SnapshotType::W(self.inner.as_ref()), } @@ -396,7 +396,7 @@ impl BptreeMapReadSnapshot<'_, K, V> { + pub fn to_snapshot(&self) -> BptreeMapReadSnapshot<'_, K, V, M> { BptreeMapReadSnapshot { inner: SnapshotType::R(self.inner.as_ref()), } diff --git a/src/bptree/mod.rs b/src/bptree/mod.rs index 1ddc63e..2ac0d03 100644 --- a/src/bptree/mod.rs +++ b/src/bptree/mod.rs @@ -177,7 +177,7 @@ mod tests { fn test_bptree2_map_from_iter_1() { let ins: Vec = (0..(L_CAPACITY << 4)).collect(); - let map = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); + let map: BptreeMap = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); { let w = map.write(); @@ -195,7 +195,7 @@ mod tests { let mut ins: Vec = (0..(L_CAPACITY << 4)).collect(); ins.shuffle(&mut rng); - let map = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); + let map: BptreeMap = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); { let w = map.write(); @@ -211,7 +211,7 @@ mod tests { fn bptree_map_basic_concurrency(lower: usize, upper: usize) { // Create a map - let map = BptreeMap::new(); + let map: BptreeMap = BptreeMap::new(); // add values { @@ -276,7 +276,7 @@ mod tests { // Need to ensure that txns are dropped in order. // Add data, enough to cause a split. All data should be *2 - let map = BptreeMap::new(); + let map: BptreeMap = BptreeMap::new(); // add values { let mut w = map.write(); @@ -346,7 +346,7 @@ mod tests { fn test_bptree2_map_rangeiter_1() { let ins: Vec = (0..100).collect(); - let map = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); + let map: BptreeMap = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); { let w = map.write(); @@ -362,7 +362,7 @@ mod tests { #[test] fn test_bptree2_map_rangeiter_2() { - let map = BptreeMap::from_iter([(3, ()), (4, ()), (0, ())]); + let map: BptreeMap = BptreeMap::from_iter([(3, ()), (4, ()), (0, ())]); let r = map.read(); assert!(r.range(1..=2).count() == 0); @@ -370,7 +370,7 @@ mod tests { #[test] fn test_bptree2_map_rangeiter_3() { - let map = BptreeMap::from_iter([0, 1, 2, 3, 4, 5, 6, 8].map(|v| (v, ()))); + let map: BptreeMap = BptreeMap::from_iter([0, 1, 2, 3, 4, 5, 6, 8].map(|v| (v, ()))); let r = map.read(); assert!(r.range((Bound::Excluded(6), Bound::Included(7))).count() == 0); diff --git a/src/cowcell/mod.rs b/src/cowcell/mod.rs index 004473d..0648618 100644 --- a/src/cowcell/mod.rs +++ b/src/cowcell/mod.rs @@ -12,8 +12,6 @@ #[cfg(feature = "asynch")] pub mod asynch; - - use core::ops::{Deref, DerefMut}; use lock_api::{Mutex, MutexGuard, RawMutex}; @@ -23,7 +21,6 @@ use alloc::sync::Arc; #[cfg(feature = "std")] use std::sync::Arc; - /// A conncurrently readable cell. /// /// This structure behaves in a similar manner to a `RwLock`. However unlike @@ -65,12 +62,21 @@ use std::sync::Arc; /// // And a new read transaction has '1' /// assert_eq!(*new_read_txn, 1); /// ``` -#[derive(Debug, Default)] +#[derive(Debug)] pub struct CowCell { write: Mutex, active: Mutex>, } +impl Default for CowCell { + fn default() -> Self { + Self { + write: Mutex::new(()), + active: Mutex::new(Arc::new(Default::default())), + } + } +} + /// A `CowCell` Write Transaction handle. /// /// This allows mutation of the content of the `CowCell` without blocking or @@ -105,7 +111,7 @@ impl Clone for CowCellReadTxn { impl CowCell where T: Clone, - R: RawMutex + R: RawMutex, { /// Create a new `CowCell` for storing type `T`. `T` must implement `Clone` /// to enable clone-on-write. @@ -120,7 +126,7 @@ where /// the read guard is guaranteed to be consistent for the life time of the /// read - even if writers commit during. pub fn read(&self) -> CowCellReadTxn { - let rwguard = self.active.lock().unwrap(); + let rwguard = self.active.lock(); CowCellReadTxn(rwguard.clone()) // rwguard ends here } @@ -133,7 +139,7 @@ where let mguard = self.write.lock(); // We delay copying until the first get_mut. let read = { - let rwguard = self.active.lock().unwrap(); + let rwguard = self.active.lock(); rwguard.clone() }; /* Now build the write struct */ @@ -153,7 +159,7 @@ where self.write.try_lock().map(|mguard| { // We delay copying until the first get_mut. let read = { - let rwguard = self.active.lock().unwrap(); + let rwguard = self.active.lock(); rwguard.clone() }; /* Now build the write struct */ @@ -168,7 +174,7 @@ where fn commit(&self, newdata: Option) { if let Some(new_data) = newdata { - let mut rwguard = self.active.lock().unwrap(); + let mut rwguard = self.active.lock(); let new_inner = Arc::new(new_data); // now over-write the last value in the mutex. *rwguard = new_inner; @@ -190,7 +196,7 @@ impl Deref for CowCellReadTxn { impl CowCellWriteTxn<'_, T, R> where T: Clone, - R: RawMutex + R: RawMutex, { /// Access a mutable pointer of the data in the `CowCell`. This data is only /// visible to the write transaction object in this thread, until you call @@ -224,7 +230,7 @@ where impl Deref for CowCellWriteTxn<'_, T, R> where T: Clone, - R: RawMutex + R: RawMutex, { type Target = T; @@ -240,7 +246,7 @@ where impl DerefMut for CowCellWriteTxn<'_, T, R> where T: Clone, - R: RawMutex + R: RawMutex, { #[inline(always)] fn deref_mut(&mut self) -> &mut T { @@ -259,7 +265,7 @@ mod tests { #[test] fn test_deref_mut() { let data: i64 = 0; - let cc = CowCell::new(data); + let cc: CowCell = CowCell::new(data); { /* Take a write txn */ let mut cc_wrtxn = cc.write(); @@ -273,7 +279,7 @@ mod tests { #[test] fn test_try_write() { let data: i64 = 0; - let cc = CowCell::new(data); + let cc: CowCell = CowCell::new(data); /* Take a write txn */ let cc_wrtxn_a = cc.try_write(); assert!(cc_wrtxn_a.is_some()); @@ -285,7 +291,7 @@ mod tests { #[test] fn test_simple_create() { let data: i64 = 0; - let cc = CowCell::new(data); + let cc: CowCell = CowCell::new(data); let cc_rotxn_a = cc.read(); assert_eq!(*cc_rotxn_a, 0); @@ -323,7 +329,7 @@ mod tests { let start = Instant::now(); // Create the new cowcell. let data: i64 = 0; - let cc = CowCell::new(data); + let cc: CowCell = CowCell::new(data); assert!(scope(|scope| { let cc_ref = &cc; diff --git a/src/ebrcell/mod.rs b/src/ebrcell/mod.rs index 5e1ea7c..cb688ef 100644 --- a/src/ebrcell/mod.rs +++ b/src/ebrcell/mod.rs @@ -191,7 +191,7 @@ where caller: self, _guard: mguard, } - }).ok() + }) } /// This is an internal component of the commit cycle. It takes ownership diff --git a/src/hashmap/asynch.rs b/src/hashmap/asynch.rs index 26f32d7..237e400 100644 --- a/src/hashmap/asynch.rs +++ b/src/hashmap/asynch.rs @@ -17,8 +17,8 @@ use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCe include!("impl.rs"); -impl - HashMap +impl + HashMap { /// Construct a new concurrent hashmap pub fn new() -> Self { @@ -30,29 +30,29 @@ impl(&'x self) -> HashMapReadTxn<'x, K, V> { + pub fn read<'x>(&'x self) -> HashMapReadTxn<'x, K, V, M> { let inner = self.inner.read(); HashMapReadTxn { inner } } /// Initiate a write transaction for the map, exclusive to this /// writer, and concurrently to all existing reads. - pub async fn write<'x>(&'x self) -> HashMapWriteTxn<'x, K, V> { + pub async fn write<'x>(&'x self) -> HashMapWriteTxn<'x, K, V, M> { let inner = self.inner.write().await; HashMapWriteTxn { inner } } /// Attempt to create a new write, returns None if another writer /// already exists. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| HashMapWriteTxn { inner }) } } -impl - HashMapWriteTxn<'_, K, V> +impl + HashMapWriteTxn<'_, K, V, M> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceive these changes. diff --git a/src/hashmap/impl.rs b/src/hashmap/impl.rs index 7ae8abc..39725e6 100644 --- a/src/hashmap/impl.rs +++ b/src/hashmap/impl.rs @@ -29,21 +29,27 @@ use std::iter::FromIterator; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `HashMapWriteTxn` without calling `commit()`. -pub struct HashMap +pub struct HashMap where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex + 'static, { inner: LinCowCell, CursorRead, CursorWrite, M>, } -unsafe impl - Send for HashMap +unsafe impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + Send + 'static, + > Send for HashMap { } -unsafe impl - Sync for HashMap +unsafe impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + Send + Sync + 'static, + > Sync for HashMap { } @@ -54,7 +60,7 @@ pub struct HashMapReadTxn<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex, { inner: LinCowCellReadTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } @@ -68,7 +74,7 @@ pub struct HashMapWriteTxn<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex, { inner: LinCowCellWriteTxn<'a, SuperBlock, CursorRead, CursorWrite, M>, } @@ -77,7 +83,7 @@ enum SnapshotType<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex, { R(&'a CursorRead), W(&'a CursorWrite), @@ -95,26 +101,35 @@ pub struct HashMapReadSnapshot<'a, K, V, M> where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex, { inner: SnapshotType<'a, K, V, M>, } -impl Default - for HashMap +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > Default for HashMap { fn default() -> Self { Self::new() } } -impl - FromIterator<(K, V)> for HashMap +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > FromIterator<(K, V)> for HashMap { fn from_iter>(iter: I) -> Self { let mut new_sblock = unsafe { SuperBlock::new() }; let prev: CursorRead = new_sblock.create_reader(); - let mut cursor = as LinCowCellCapable, CursorWrite>>::create_writer(&new_sblock); //new_sblock.create_writer(); + let mut cursor = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&new_sblock); //new_sblock.create_writer(); cursor.extend(iter); let _ = new_sblock.pre_commit(cursor, &prev); @@ -125,16 +140,22 @@ impl - Extend<(K, V)> for HashMapWriteTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > Extend<(K, V)> for HashMapWriteTxn<'_, K, V, M> { fn extend>(&mut self, iter: I) { self.inner.as_mut().extend(iter); } } -impl - HashMapWriteTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashMapWriteTxn<'_, K, V, M> { /* pub(crate) fn prehash(&self, k: &Q) -> u64 @@ -239,8 +260,11 @@ impl - HashMapReadTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex, + > HashMapReadTxn<'_, K, V, M> { pub(crate) fn get_prehashed(&self, k: &Q, k_hash: u64) -> Option<&V> where @@ -267,7 +291,7 @@ impl, Q: Hash + Eq + ?Sized, { - self.get(k).is_some() + self.get::(k).is_some() } /// Returns the current number of k:v pairs in the tree @@ -277,7 +301,7 @@ impl bool { - self.inner.as_ref().len() == 0 + 0usize == self.inner.as_ref().len() } /// Iterator over `(&K, &V)` of the set @@ -304,8 +328,11 @@ impl - HashMapReadSnapshot<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashMapReadSnapshot<'_, K, V, M> { /// Retrieve a value from the tree. If the value exists, a reference is returned /// as `Some(&V)`, otherwise if not present `None` is returned. diff --git a/src/hashtrie/asynch.rs b/src/hashtrie/asynch.rs index b15cf68..0e173f3 100644 --- a/src/hashtrie/asynch.rs +++ b/src/hashtrie/asynch.rs @@ -17,8 +17,8 @@ use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCe include!("impl.rs"); -impl - HashTrie +impl + HashTrie { /// Construct a new concurrent hashtrie pub fn new() -> Self { @@ -30,29 +30,29 @@ impl(&'x self) -> HashTrieReadTxn<'x, K, V> { + pub fn read<'x>(&'x self) -> HashTrieReadTxn<'x, K, V, M> { let inner = self.inner.read(); HashTrieReadTxn { inner } } /// Initiate a write transaction for the map, exclusive to this /// writer, and concurrently to all existing reads. - pub async fn write<'x>(&'x self) -> HashTrieWriteTxn<'x, K, V> { + pub async fn write<'x>(&'x self) -> HashTrieWriteTxn<'x, K, V, M> { let inner = self.inner.write().await; HashTrieWriteTxn { inner } } /// Attempt to create a new write, returns None if another writer /// already exists. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| HashTrieWriteTxn { inner }) } } -impl - HashTrieWriteTxn<'_, K, V> +impl + HashTrieWriteTxn<'_, K, V, M> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceieve these changes. diff --git a/src/hashtrie/impl.rs b/src/hashtrie/impl.rs index 2aa0387..84aec6a 100644 --- a/src/hashtrie/impl.rs +++ b/src/hashtrie/impl.rs @@ -27,7 +27,7 @@ use std::iter::FromIterator; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `HashTrieWriteTxn` without calling `commit()`. -pub struct HashTrie +pub struct HashTrie where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, @@ -98,7 +98,7 @@ where inner: SnapshotType<'a, K, V, M>, } -impl Default +impl Default for HashTrie { fn default() -> Self { diff --git a/src/hashtrie/mod.rs b/src/hashtrie/mod.rs index 7ac3f54..a11b93f 100644 --- a/src/hashtrie/mod.rs +++ b/src/hashtrie/mod.rs @@ -50,7 +50,7 @@ impl Self { // I acknowledge I understand what is required to make this safe. HashTrie { - inner: LinCowCell::new(unsafe { SuperBlock::new() }), + inner: LinCowCell::, CursorRead, CursorWrite, M>::new(unsafe { SuperBlock::new() }), } } @@ -70,7 +70,7 @@ impl Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| HashTrieWriteTxn { inner }) diff --git a/src/internals/bptree/cursor.rs b/src/internals/bptree/cursor.rs index 7999b3c..fe4d4cc 100644 --- a/src/internals/bptree/cursor.rs +++ b/src/internals/bptree/cursor.rs @@ -45,10 +45,10 @@ unsafe impl - LinCowCellCapable, CursorWrite> for SuperBlock +impl + LinCowCellCapable, CursorWrite> for SuperBlock { - fn create_reader(&self) -> CursorRead { + fn create_reader(&self) -> CursorRead { // This sets up the first reader. CursorRead::new(self) } @@ -61,8 +61,8 @@ impl fn pre_commit( &mut self, mut new: CursorWrite, - prev: &CursorRead, - ) -> CursorRead { + prev: &CursorRead, + ) -> CursorRead { let mut prev_last_seen = prev.last_seen.lock(); debug_assert!((*prev_last_seen).is_empty()); @@ -136,10 +136,11 @@ impl SuperBlock { } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Ord + Clone + Debug, V: Clone, + R: RawMutex, { txid: u64, length: usize, @@ -1298,7 +1299,7 @@ mod tests { use super::super::node::*; use super::super::states::*; use super::SuperBlock; - use super::{CursorRead, CursorReadOps}; + use super::{CursorRead, CursorReadOps, CursorWrite}; use crate::internals::lincowcell::LinCowCellCapable; use rand::seq::SliceRandom; use std::mem; @@ -1348,7 +1349,7 @@ mod tests { // First create the node + cursor let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); eprintln!("{:?}", wcurs); @@ -1389,7 +1390,7 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let prev_txid = wcurs.root_txid(); let r = wcurs.insert(1, 1); @@ -1411,7 +1412,7 @@ mod tests { // to trigger a clone of leaf AND THEN to cause the split. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(L_CAPACITY + 1) { // println!("ITER v {}", v); @@ -1439,7 +1440,7 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // println!("{:?}", wcurs); @@ -1469,7 +1470,7 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(29, 29); @@ -1497,7 +1498,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1531,7 +1532,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1562,7 +1563,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(11, 11); @@ -1597,7 +1598,7 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(19, 19); @@ -1622,7 +1623,7 @@ mod tests { // so we do this to a reasonable number. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { // println!("ITER v {}", v); @@ -1643,7 +1644,7 @@ mod tests { // Insert descending let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in (1..(L_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -1668,7 +1669,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v, v); @@ -1689,10 +1690,10 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let mut sb = unsafe { SuperBlock::new() }; - let mut rdr = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in 1..(L_CAPACITY << 4) { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v, v); assert!(r.is_none()); @@ -1711,10 +1712,10 @@ mod tests { fn test_bptree2_cursor_insert_stress_5() { // Insert descending let mut sb = unsafe { SuperBlock::new() }; - let mut rdr = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in (1..(L_CAPACITY << 4)).rev() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v, v); assert!(r.is_none()); @@ -1736,10 +1737,10 @@ mod tests { ins.shuffle(&mut rng); let mut sb = unsafe { SuperBlock::new() }; - let mut rdr = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in ins.into_iter() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let r = wcurs.insert(v, v); assert!(r.is_none()); assert!(wcurs.verify()); @@ -1756,7 +1757,7 @@ mod tests { fn test_bptree2_cursor_search_1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { let r = wcurs.insert(v, v); @@ -1780,7 +1781,7 @@ mod tests { // Check the length is consistent on operations. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { let r = wcurs.insert(v, v); @@ -1800,7 +1801,7 @@ mod tests { // let lnode = create_leaf_node_full(0); let sb = SuperBlock::new_test(1, lnode); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("{:?}", wcurs); for v in 0..L_CAPACITY { @@ -1823,7 +1824,7 @@ mod tests { fn test_bptree2_cursor_remove_01_p1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let _ = wcurs.remove(&0); // println!("{:?}", wcurs); @@ -1849,7 +1850,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("{:?}", wcurs); assert!(wcurs.verify()); @@ -1876,7 +1877,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -1902,7 +1903,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Setup sibling leaf to already be cloned. @@ -1932,7 +1933,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -1962,7 +1963,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -2001,7 +2002,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); @@ -2039,7 +2040,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -2076,7 +2077,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&80); @@ -2113,7 +2114,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -2150,7 +2151,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); @@ -2191,7 +2192,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(&20); @@ -2231,7 +2232,7 @@ mod tests { let root = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); // let count = BV_CAPACITY + 2; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(&0); @@ -2271,7 +2272,7 @@ mod tests { let root = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); for i in 0..BV_CAPACITY { @@ -2295,7 +2296,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&20); @@ -2313,7 +2314,7 @@ mod tests { let rnode = create_leaf_node_full(20) as *mut Node; let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -2331,7 +2332,7 @@ mod tests { let mut sb = unsafe { SuperBlock::new() }; let rdr = sb.create_reader(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v, v); @@ -2347,7 +2348,7 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { // println!("-- ITER v {}", v); @@ -2368,7 +2369,7 @@ mod tests { fn test_bptree2_cursor_remove_stress_2() { // Insert descending let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in (1..(L_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -2391,7 +2392,7 @@ mod tests { ins.shuffle(&mut rng); let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.remove(&v); @@ -2413,7 +2414,7 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in 1..(L_CAPACITY << 4) { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(&v); assert!(r == Some(v)); @@ -2431,7 +2432,7 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in (1..(L_CAPACITY << 4)).rev() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(&v); assert!(r == Some(v)); @@ -2453,7 +2454,7 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in ins.into_iter() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let r = wcurs.remove(&v); assert!(r == Some(v)); assert!(wcurs.verify()); @@ -2555,7 +2556,7 @@ mod tests { // Do a split_off_lt. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); wcurs.split_off_lt(&5); @@ -2573,7 +2574,7 @@ mod tests { // Do a split_off_lt. let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); wcurs.split_off_lt(&11); @@ -2591,7 +2592,7 @@ mod tests { // Do a split_off_lt. let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); wcurs.path_clone(&11); wcurs.split_off_lt(&11); @@ -2610,7 +2611,7 @@ mod tests { let tree = create_split_off_tree(); let sb = SuperBlock::new_test(1, tree); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // 0 is min, and not present, will cause no change. // clone everything let outer: [usize; 4] = [0, 100, 200, 300]; @@ -2641,7 +2642,7 @@ mod tests { // println!("START -> {:?}", tree); let sb = SuperBlock::new_test(1, tree); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // 0 is min, and not present, will cause no change. wcurs.split_off_lt(&v); assert!(wcurs.verify()); @@ -2697,7 +2698,7 @@ mod tests { for v in data.iter() { let node: *mut Leaf = Node::new_leaf(0) as *mut _; let sb = SuperBlock::new_test(1, node as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); wcurs.extend(data.iter().map(|v| (*v, *v))); if v > &0 { @@ -2720,7 +2721,7 @@ mod tests { fn test_bptree_cursor_double_extend() { let node: *mut Leaf = Node::new_leaf(0) as *mut _; let sb = SuperBlock::new_test(1, node as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); wcurs.extend([(0, 0), (1, 1), (2, 2), (3, 3)]); assert!(wcurs.len() == 4); diff --git a/src/internals/bptree/mutiter.rs b/src/internals/bptree/mutiter.rs index cf6eaa9..0447fcf 100644 --- a/src/internals/bptree/mutiter.rs +++ b/src/internals/bptree/mutiter.rs @@ -83,6 +83,7 @@ mod tests { use super::RangeMutIter; use std::ops::Bound::{self, *}; + use crate::internals::bptree::cursor::{CursorRead, CursorWrite}; use crate::internals::lincowcell::LinCowCellCapable; fn create_leaf_node_full(vbase: usize) -> *mut Node { @@ -103,7 +104,7 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let bounds: (Bound, Bound) = (Unbounded, Unbounded); let range_mut_iter = RangeMutIter::new(&mut wcurs, bounds); diff --git a/src/internals/hashmap/cursor.rs b/src/internals/hashmap/cursor.rs index 0fcfced..8bb631f 100644 --- a/src/internals/hashmap/cursor.rs +++ b/src/internals/hashmap/cursor.rs @@ -19,8 +19,8 @@ use vec::Vec; #[cfg(any(feature = "ahash", not(feature = "std")))] use ahash::RandomState; -#[cfg(feature = "foldhash")] -use foldhash::fast::RandomState; +//#[cfg(feature = "foldhash")] +//use foldhash::fast::RandomState; #[cfg(all(not(feature = "ahash"), not(feature = "foldhash")))] use std::collections::hash_map::RandomState; @@ -145,7 +145,7 @@ impl SuperBlock { } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Hash + Eq + Clone + Debug, V: Clone, @@ -1112,6 +1112,7 @@ mod tests { use super::super::states::*; use super::SuperBlock; use super::{CursorRead, CursorReadOps}; + use crate::internals::hashmap::cursor::CursorWrite; use crate::internals::lincowcell::LinCowCellCapable; use rand::seq::SliceRandom; use std::mem; @@ -1161,7 +1162,7 @@ mod tests { // First create the node + cursor let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let prev_txid = wcurs.root_txid(); // Now insert - the txid should be different. @@ -1197,7 +1198,7 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let prev_txid = wcurs.root_txid(); let r = wcurs.insert(1, 1, 1); @@ -1219,7 +1220,7 @@ mod tests { // to trigger a clone of leaf AND THEN to cause the split. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(H_CAPACITY + 1) { // println!("ITER v {}", v); @@ -1247,7 +1248,7 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // println!("{:?}", wcurs); @@ -1277,7 +1278,7 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(29, 29, 29); @@ -1305,7 +1306,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1339,7 +1340,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1370,7 +1371,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(11, 11, 11); @@ -1405,7 +1406,7 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(19, 19, 19); @@ -1430,7 +1431,7 @@ mod tests { // so we do this to a reasonable number. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { // println!("ITER v {}", v); @@ -1451,7 +1452,7 @@ mod tests { // Insert descending let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in (1..(H_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -1476,7 +1477,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v as u64, v, v); @@ -1497,10 +1498,10 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let mut sb = unsafe { SuperBlock::new() }; - let mut rdr = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in 1..(H_CAPACITY << 4) { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v as u64, v, v); assert!(r.is_none()); @@ -1518,10 +1519,10 @@ mod tests { fn test_hashmap2_cursor_insert_stress_5() { // Insert descending let mut sb = unsafe { SuperBlock::new() }; - let mut rdr = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in (1..(H_CAPACITY << 4)).rev() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v as u64, v, v); assert!(r.is_none()); @@ -1543,10 +1544,10 @@ mod tests { ins.shuffle(&mut rng); let mut sb = unsafe { SuperBlock::new() }; - let mut rdr = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in ins.into_iter() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let r = wcurs.insert(v as u64, v, v); assert!(r.is_none()); assert!(wcurs.verify()); @@ -1563,7 +1564,7 @@ mod tests { fn test_hashmap2_cursor_search_1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { let r = wcurs.insert(v as u64, v, v); @@ -1587,7 +1588,7 @@ mod tests { // Check the length is consistent on operations. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { let r = wcurs.insert(v as u64, v, v); @@ -1610,7 +1611,7 @@ mod tests { // let lnode = create_leaf_node_full(0); let sb = SuperBlock::new_test(1, lnode); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("{:?}", wcurs); for v in 0..H_CAPACITY { @@ -1633,7 +1634,7 @@ mod tests { fn test_hashmap2_cursor_remove_01_p1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let _ = wcurs.remove(0, &0); // println!("{:?}", wcurs); @@ -1659,7 +1660,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); println!("{:?}", wcurs); assert!(wcurs.verify()); wcurs.remove(20, &20); @@ -1685,7 +1686,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -1711,7 +1712,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Setup sibling leaf to already be cloned. @@ -1741,7 +1742,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -1771,7 +1772,7 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -1811,7 +1812,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); @@ -1849,7 +1850,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -1886,7 +1887,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(80, &80); @@ -1923,7 +1924,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -1960,7 +1961,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); @@ -2001,7 +2002,7 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(20); @@ -2042,7 +2043,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _) as *mut Node; // let count = HBV_CAPACITY + 2; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(0); @@ -2083,7 +2084,7 @@ mod tests { let root = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); for i in 0..HBV_CAPACITY { @@ -2107,7 +2108,7 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(20, &20); @@ -2125,7 +2126,7 @@ mod tests { let rnode = create_leaf_node_full(20) as *mut Node; let root = Node::new_branch(0, lnode, rnode) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -2143,7 +2144,7 @@ mod tests { let mut sb = unsafe { SuperBlock::new() }; let rdr = sb.create_reader(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v as u64, v, v); @@ -2160,7 +2161,7 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { // println!("-- ITER v {}", v); @@ -2181,7 +2182,7 @@ mod tests { fn test_hashmap2_cursor_remove_stress_2() { // Insert descending let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in (1..(H_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -2207,7 +2208,7 @@ mod tests { ins.shuffle(&mut rng); let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.remove(v as u64, &v); @@ -2232,7 +2233,7 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in 1..(H_CAPACITY << 4) { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(v as u64, &v); assert!(r == Some(v)); @@ -2252,7 +2253,7 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in (1..(H_CAPACITY << 4)).rev() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(v as u64, &v); assert!(r == Some(v)); @@ -2277,7 +2278,7 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in ins.into_iter() { - let mut wcurs = sb.create_writer(); + let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); let r = wcurs.remove(v as u64, &v); assert!(r == Some(v)); assert!(wcurs.verify()); diff --git a/src/internals/hashtrie/cursor.rs b/src/internals/hashtrie/cursor.rs index 91a90a3..4132f92 100644 --- a/src/internals/hashtrie/cursor.rs +++ b/src/internals/hashtrie/cursor.rs @@ -31,8 +31,8 @@ use super::iter::*; #[cfg(any(feature = "ahash", not(feature = "std")))] use ahash::RandomState; -#[cfg(feature = "foldhash")] -use foldhash::fast::RandomState; +//#[cfg(feature = "foldhash")] +//use foldhash::fast::RandomState; #[cfg(all(not(feature = "ahash"), not(feature = "foldhash")))] use std::collections::hash_map::RandomState; @@ -87,17 +87,17 @@ macro_rules! hash_key { } #[cfg(all(test, not(miri)))] -thread_local!(static ALLOC_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); +thread_local!(static ALLOC_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); #[cfg(all(test, not(miri)))] -thread_local!(static WRITE_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); +thread_local!(static WRITE_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); #[cfg(test)] fn assert_released() { #[cfg(not(miri))] { let is_empty = ALLOC_LIST.with(|llist| { - let x = llist.lock().unwrap(); + let x = llist.lock(); println!("Remaining -> {:?}", x); x.is_empty() }); @@ -182,14 +182,14 @@ impl Ptr { #[inline(always)] fn mark_dirty(&mut self) { #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - WRITE_LIST.with(|llist| assert!(llist.lock().unwrap().insert(self.untagged()))); + WRITE_LIST.with(|llist| assert!(llist.lock().insert(self.untagged()))); self.p = self.p.map_addr(|a| a | FLAG_DIRTY) } #[inline(always)] fn mark_clean(&mut self) { #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - WRITE_LIST.with(|llist| assert!(llist.lock().unwrap().remove(&(self.untagged())))); + WRITE_LIST.with(|llist| assert!(llist.lock().remove(&(self.untagged())))); self.p = self.p.map_addr(|a| a & MARK_CLEAN) } @@ -197,7 +197,7 @@ impl Ptr { pub(crate) fn as_bucket(&self) -> &Bucket { debug_assert!(self.is_bucket()); #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged()))); + ALLOC_LIST.with(|llist| assert!(llist.lock().contains(&self.untagged()))); unsafe { &*(self.p.map_addr(|a| a & UNTAG) as *const Bucket) } } @@ -205,7 +205,7 @@ impl Ptr { fn as_bucket_raw(&self) -> *mut Bucket { debug_assert!(self.is_bucket()); #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged()))); + ALLOC_LIST.with(|llist| assert!(llist.lock().contains(&self.untagged()))); self.p.map_addr(|a| a & UNTAG) as *mut Bucket } @@ -218,7 +218,7 @@ impl Ptr { debug_assert!(self.is_dirty()); #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] WRITE_LIST.with(|llist| { - let wlist_guard = llist.lock().unwrap(); + let wlist_guard = llist.lock(); assert!(wlist_guard.contains(&self.untagged())) }); unsafe { &mut *(self.p.map_addr(|a| a & UNTAG) as *mut Bucket) } @@ -228,7 +228,7 @@ impl Ptr { pub(crate) fn as_branch(&self) -> &Branch { debug_assert!(self.is_branch()); #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged()))); + ALLOC_LIST.with(|llist| assert!(llist.lock().contains(&self.untagged()))); unsafe { &*(self.p.map_addr(|a| a & UNTAG) as *const Branch) } } @@ -236,7 +236,7 @@ impl Ptr { fn as_branch_raw(&self) -> *mut Branch { debug_assert!(self.is_branch()); #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged()))); + ALLOC_LIST.with(|llist| assert!(llist.lock().contains(&self.untagged()))); self.p.map_addr(|a| a & UNTAG) as *mut Branch } @@ -249,7 +249,7 @@ impl Ptr { debug_assert!(self.is_dirty()); #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] WRITE_LIST.with(|llist| { - let wlist_guard = llist.lock().unwrap(); + let wlist_guard = llist.lock(); assert!(wlist_guard.contains(&self.untagged())) }); unsafe { &mut *(self.p.map_addr(|a| a & UNTAG) as *mut Branch) } @@ -269,7 +269,7 @@ impl Ptr { fn free(&self) { // We MUST have allocated this, else it's a double free #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().contains(&self.untagged()))); + ALLOC_LIST.with(|llist| assert!(llist.lock().contains(&self.untagged()))); // It's getting freeeeeedddd unsafe { @@ -282,11 +282,11 @@ impl Ptr { #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] if self.is_dirty() { - WRITE_LIST.with(|llist| assert!(llist.lock().unwrap().remove(&(self.untagged())))) + WRITE_LIST.with(|llist| assert!(llist.lock().remove(&(self.untagged())))) }; #[cfg(all(test, not(miri), not(feature = "dhat-heap")))] - ALLOC_LIST.with(|llist| assert!(llist.lock().unwrap().remove(&self.untagged()))); + ALLOC_LIST.with(|llist| assert!(llist.lock().remove(&self.untagged()))); } } @@ -298,7 +298,7 @@ impl From>> for Ptr { p: rptr.map_addr(|a| a | FLAG_BRANCH) as *mut i32, }; #[cfg(all(test, not(miri)))] - ALLOC_LIST.with(|llist| llist.lock().unwrap().insert(r.untagged())); + ALLOC_LIST.with(|llist| llist.lock().insert(r.untagged())); r } } @@ -311,7 +311,7 @@ impl From>> for Ptr { p: rptr.map_addr(|a| a | FLAG_BUCKET) as *mut i32, }; #[cfg(all(test, not(miri)))] - ALLOC_LIST.with(|llist| llist.lock().unwrap().insert(r.untagged())); + ALLOC_LIST.with(|llist| llist.lock().insert(r.untagged())); r } } @@ -1056,10 +1056,11 @@ impl CursorReadOps for CursorWrite } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Hash + Eq + Clone + Debug, V: Clone, + R: RawMutex { txid: u64, length: usize, @@ -1130,7 +1131,7 @@ mod tests { fn test_hashtrie_cursor_basic() { let sb: SuperBlock = unsafe { SuperBlock::new() }; - let mut wr = sb.create_writer(); + let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wr.len() == 0); assert!(wr.search(0, &0).is_none()); @@ -1151,8 +1152,8 @@ mod tests { #[test] fn test_hashtrie_cursor_insert_max_depth() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; - let rdr = sb.create_reader(); - let mut wr = sb.create_writer(); + let rdr: CursorRead = sb.create_reader(); + let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wr.len() == 0); for i in 0..(ABS_MAX_HEIGHT * 2) { @@ -1187,8 +1188,8 @@ mod tests { #[test] fn test_hashtrie_cursor_insert_broad() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; - let rdr = sb.create_reader(); - let mut wr = sb.create_writer(); + let rdr: CursorRead = sb.create_reader(); + let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wr.len() == 0); for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { @@ -1222,20 +1223,20 @@ mod tests { #[test] fn test_hashtrie_cursor_insert_multiple_txns() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; - let mut rdr = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); // Do thing assert!(rdr.len() == 0); for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { - let mut wr = sb.create_writer(); + let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wr.insert(i, i, i).is_none()); wr.verify(); rdr = sb.pre_commit(wr, &rdr); } { - let rdr2 = sb.create_reader(); + let rdr2: CursorRead = sb.create_reader(); assert!(rdr2.len() == (ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) as usize); for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { assert!(rdr2.search(i, &i).is_some()); @@ -1243,7 +1244,7 @@ mod tests { } for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { - let mut wr = sb.create_writer(); + let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); assert!(wr.remove(i, &i).is_some()); wr.verify(); rdr = sb.pre_commit(wr, &rdr); diff --git a/src/internals/lincowcell/mod.rs b/src/internals/lincowcell/mod.rs index ac32aee..b16a079 100644 --- a/src/internals/lincowcell/mod.rs +++ b/src/internals/lincowcell/mod.rs @@ -82,10 +82,10 @@ pub trait LinCowCellCapable { #[derive(Debug)] /// A concurrently readable cell with linearised drop behaviour. -pub struct LinCowCell { +pub struct LinCowCell { updater: PhantomData, write: Mutex, - active: Mutex>>, + active: Mutex>>, } #[derive(Debug)] @@ -98,9 +98,9 @@ pub struct LinCowCellWriteTxn<'a, T, R, U, M: RawMutex> { } #[derive(Debug)] -struct LinCowCellInner { +struct LinCowCellInner { // This gives the chain effect. - pin: Mutex>>>, + pin: Mutex>>>, data: R, } @@ -110,10 +110,10 @@ pub struct LinCowCellReadTxn<'a, T, R, U, M: RawMutex> { // We must outlive the root _caller: &'a LinCowCell, // We pin the current version. - work: Arc>, + work: Arc>, } -impl LinCowCellInner { +impl LinCowCellInner { pub fn new(data: R) -> Self { LinCowCellInner { pin: Mutex::new(None), @@ -122,10 +122,10 @@ impl LinCowCellInner { } } -impl Drop for LinCowCellInner { +impl Drop for LinCowCellInner { fn drop(&mut self) { // Ensure the default drop won't recursively drop the chain - let mut current = self.pin.lock().unwrap().take(); + let mut current: Option>> = self.pin.lock().deref_mut().take(); // Drop the chain iteratively to avoid stack overflow while let Some(arc) = current { @@ -133,29 +133,7 @@ impl Drop for LinCowCellInner { match Arc::try_unwrap(arc) { Ok(inner) => { // Continue with the next link. - current = inner.pin.lock().unwrap().take(); - } - Err(_) => { - // Another reference exists, so we can safely let it drop normally without recursion - break; - } - } - } - } -} - -impl Drop for LinCowCellInner { - fn drop(&mut self) { - // Ensure the default drop won't recursively drop the chain - let mut current = self.pin.lock().unwrap().take(); - - // Drop the chain iteratively to avoid stack overflow - while let Some(arc) = current { - // Try to get exclusive ownership of the next link - match Arc::try_unwrap(arc) { - Ok(inner) => { - // Continue with the next link. - current = inner.pin.lock().unwrap().take(); + current = inner.pin.lock().deref_mut().take(); } Err(_) => { // Another reference exists, so we can safely let it drop normally without recursion @@ -183,7 +161,7 @@ where /// Begin a read txn pub fn read(&self) -> LinCowCellReadTxn<'_, T, R, U, M> { - let rwguard = self.active.lock().unwrap(); + let rwguard = self.active.lock(); LinCowCellReadTxn { _caller: self, // inc the arc. @@ -231,7 +209,7 @@ where } = write; // Get the previous generation. - let mut rwguard = self.active.lock().unwrap(); + let mut rwguard = self.active.lock(); // Start to setup for the commit. let newdata = guard.pre_commit(work, &rwguard.data); @@ -239,7 +217,7 @@ where let new_inner = Arc::new(LinCowCellInner::new(newdata)); { // This modifies the next pointer of the existing read txns - let mut rwguard_inner = rwguard.pin.lock().unwrap(); + let mut rwguard_inner = rwguard.pin.lock(); // Create the arc pointer to our new data // add it to the last value *rwguard_inner = Some(new_inner.clone()); @@ -360,9 +338,9 @@ mod tests { #[test] fn test_simple_create() { let data = TestData { x: 0 }; - let cc = LinCowCell::new(data); + let cc: LinCowCell = LinCowCell::new(data); - let cc_rotxn_a = cc.read(); + let cc_rotxn_a= cc.read(); println!("cc_rotxn_a -> {:?}", cc_rotxn_a); assert_eq!(cc_rotxn_a.work.data.x, 0); @@ -576,7 +554,7 @@ mod tests { #[cfg_attr(miri, ignore)] fn test_long_chain_drop_no_stack_overflow() { let data = TestData { x: 0 }; - let cc = LinCowCell::new(data); + let cc: LinCowCell = LinCowCell::new(data); // Simulate a read txn that is not dropped. let initial_read = cc.read(); @@ -664,7 +642,7 @@ mod tests_linear { GC_COUNT.store(0, Ordering::Release); assert!(GC_COUNT.load(Ordering::Acquire) == 0); let data = TestGcWrapper { data: 0 }; - let cc = LinCowCell::new(data); + let cc: LinCowCell, TestGcWrapperReadTxn, TestGcWrapperWriteTxn> = LinCowCell::new(data); // Open a read A. let cc_rotxn_a = cc.read(); diff --git a/src/internals/lincowcell_async/mod.rs b/src/internals/lincowcell_async/mod.rs index 267ee04..28e218e 100644 --- a/src/internals/lincowcell_async/mod.rs +++ b/src/internals/lincowcell_async/mod.rs @@ -65,19 +65,21 @@ use crate::internals::lincowcell::LinCowCellCapable; #[derive(Debug)] /// A concurrently readable cell with linearised drop behaviour. -pub struct LinCowCell { +pub struct LinCowCell { updater: PhantomData, write: Mutex, active: SyncMutex>>, + _phantom: PhantomData } #[derive(Debug)] /// A write txn over a linear cell. -pub struct LinCowCellWriteTxn<'a, T, R, U> { +pub struct LinCowCellWriteTxn<'a, T, R, U, M> { // This way we know who to contact for updating our data .... - caller: &'a LinCowCell, + caller: &'a LinCowCell, guard: MutexGuard<'a, T>, work: U, + _phantom: PhantomData } #[derive(Debug)] @@ -89,9 +91,9 @@ struct LinCowCellInner { #[derive(Debug)] /// A read txn over a linear cell. -pub struct LinCowCellReadTxn<'a, T, R, U> { +pub struct LinCowCellReadTxn<'a, T, R, U, M> { // We must outlive the root - _caller: &'a LinCowCell, + _caller: &'a LinCowCell, // We pin the current version. work: Arc>, } @@ -127,7 +129,7 @@ impl Drop for LinCowCellInner { } } -impl LinCowCell +impl LinCowCell where T: LinCowCellCapable, { @@ -138,11 +140,12 @@ where updater: PhantomData, write: Mutex::new(data), active: SyncMutex::new(Arc::new(LinCowCellInner::new(r))), + _phantom: PhantomData } } /// Begin a read txn - pub fn read(&self) -> LinCowCellReadTxn<'_, T, R, U> { + pub fn read(&self) -> LinCowCellReadTxn<'_, T, R, U, M> { let rwguard = self.active.lock().unwrap(); LinCowCellReadTxn { _caller: self, @@ -152,7 +155,7 @@ where } /// Begin a write txn - pub async fn write<'x>(&'x self) -> LinCowCellWriteTxn<'x, T, R, U> { + pub async fn write<'x>(&'x self) -> LinCowCellWriteTxn<'x, T, R, U, M> { /* Take the exclusive write lock first */ let write_guard = self.write.lock().await; /* Now take a ro-txn to get the data copied */ @@ -164,11 +167,12 @@ where caller: self, guard: write_guard, work, + _phantom: PhantomData } } /// Attempt a write txn - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.write .try_lock() .map(|write_guard| { @@ -179,18 +183,20 @@ where caller: self, guard: write_guard, work, + _phantom: PhantomData } }) .ok() } - fn commit(&self, write: LinCowCellWriteTxn<'_, T, R, U>) { + fn commit(&self, write: LinCowCellWriteTxn<'_, T, R, U, M>) { // Destructure our writer. let LinCowCellWriteTxn { // This is self. caller: _caller, mut guard, work, + _phantom: PhantomData } = write; // Get the previous generation. @@ -212,7 +218,7 @@ where } } -impl Deref for LinCowCellReadTxn<'_, T, R, U> { +impl Deref for LinCowCellReadTxn<'_, T, R, U, M> { type Target = R; #[inline] @@ -221,14 +227,14 @@ impl Deref for LinCowCellReadTxn<'_, T, R, U> { } } -impl AsRef for LinCowCellReadTxn<'_, T, R, U> { +impl AsRef for LinCowCellReadTxn<'_, T, R, U, M> { #[inline] fn as_ref(&self) -> &R { &self.work.data } } -impl LinCowCellWriteTxn<'_, T, R, U> +impl LinCowCellWriteTxn<'_, T, R, U, M> where T: LinCowCellCapable, { @@ -245,7 +251,7 @@ where } } -impl Deref for LinCowCellWriteTxn<'_, T, R, U> { +impl Deref for LinCowCellWriteTxn<'_, T, R, U, M> { type Target = U; #[inline] @@ -254,21 +260,21 @@ impl Deref for LinCowCellWriteTxn<'_, T, R, U> { } } -impl DerefMut for LinCowCellWriteTxn<'_, T, R, U> { +impl DerefMut for LinCowCellWriteTxn<'_, T, R, U, M> { #[inline] fn deref_mut(&mut self) -> &mut U { &mut self.work } } -impl AsRef for LinCowCellWriteTxn<'_, T, R, U> { +impl AsRef for LinCowCellWriteTxn<'_, T, R, U, M> { #[inline] fn as_ref(&self) -> &U { &self.work } } -impl AsMut for LinCowCellWriteTxn<'_, T, R, U> { +impl AsMut for LinCowCellWriteTxn<'_, T, R, U, M> { #[inline] fn as_mut(&mut self) -> &mut U { &mut self.work @@ -321,7 +327,7 @@ mod tests { #[tokio::test] async fn test_simple_create() { let data = TestData { x: 0 }; - let cc = LinCowCell::new(data); + let cc: LinCowCell = LinCowCell::new(data); let cc_rotxn_a = cc.read(); println!("cc_rotxn_a -> {:?}", cc_rotxn_a); @@ -405,7 +411,7 @@ mod tests { let start = Instant::now(); // Create the new cowcell. let data = TestData { x: 0 }; - let cc = Arc::new(LinCowCell::new(data)); + let cc: Arc> = Arc::new(LinCowCell::new(data)); let _ = tokio::join!( tokio::task::spawn_blocking({ @@ -508,7 +514,7 @@ mod tests { async fn test_gc_operation() { GC_COUNT.store(0, Ordering::Release); let data = TestGcWrapper { data: 0 }; - let cc = Arc::new(LinCowCell::new(data)); + let cc: Arc, TestGcWrapperReadTxn, TestGcWrapperWriteTxn>> = Arc::new(LinCowCell::new(data)); let _ = tokio::join!( tokio::task::spawn(test_gc_operation_thread(cc.clone())), @@ -524,7 +530,7 @@ mod tests { #[cfg_attr(miri, ignore)] async fn test_long_chain_drop_no_stack_overflow() { let data = TestData { x: 0 }; - let cc = LinCowCell::new(data); + let cc: LinCowCell = LinCowCell::new(data); // Simulate a read txn that is not dropped. let initial_read = cc.read(); @@ -612,7 +618,7 @@ mod tests_linear { GC_COUNT.store(0, Ordering::Release); assert!(GC_COUNT.load(Ordering::Acquire) == 0); let data = TestGcWrapper { data: 0 }; - let cc = LinCowCell::new(data); + let cc: LinCowCell, TestGcWrapperReadTxn, TestGcWrapperWriteTxn> = LinCowCell::new(data); // Open a read A. let cc_rotxn_a = cc.read(); diff --git a/src/lc_tests.rs b/src/lc_tests.rs index 5b09e66..dd54df0 100644 --- a/src/lc_tests.rs +++ b/src/lc_tests.rs @@ -7,8 +7,8 @@ struct TestStruct { } struct TestStructRead { - bptree_map_a: CursorRead, - bptree_map_b: CursorRead, + bptree_map_a: CursorRead, + bptree_map_b: CursorRead, } struct TestStructWrite { @@ -28,8 +28,8 @@ impl LinCowCellCapable for TestStruct { fn create_writer(&self) -> TestStructWrite { // This sets up the first writer. TestStructWrite { - bptree_map_a: self.bptree_map_a.create_writer(), - bptree_map_b: self.bptree_map_b.create_writer(), + bptree_map_a: as LinCowCellCapable, CursorWrite>>::create_writer(&self.bptree_map_a), + bptree_map_b: as LinCowCellCapable, CursorWrite>>::create_writer(&self.bptree_map_b), } } @@ -56,7 +56,7 @@ impl LinCowCellCapable for TestStruct { #[test] fn test_lc_basic() { - let lcc = LinCowCell::new(TestStruct { + let lcc: LinCowCell = LinCowCell::new(TestStruct { bptree_map_a: unsafe { SuperBlock::new() }, bptree_map_b: unsafe { SuperBlock::new() }, }); diff --git a/tests/bptree_map.rs b/tests/bptree_map.rs index 4ed4fbc..d46fb15 100644 --- a/tests/bptree_map.rs +++ b/tests/bptree_map.rs @@ -8,7 +8,7 @@ proptest::proptest! { fn bptree_range_iter_consistent(values: BTreeSet, left in 0..u8::MAX - 1, len in 1..u8::MAX, bounds: (Bound<()>, Bound<()>)) { let range = (bounds.0.map(|()| left), bounds.1.map(|()| left.saturating_add(len))); let btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v, ()))); - let bptree_map = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, ()))); + let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, ()))); let bptree_map_read_tx = bptree_map.read(); let btree_iter = btree_map.range(range); @@ -22,7 +22,7 @@ proptest::proptest! { #[test] fn bptree_get_consistent(values: BTreeSet, key: u8) { let btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v, v))); - let bptree_map = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, v))); + let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, v))); let bptree_map_read_tx = bptree_map.read(); let btree_value = btree_map.get(&key); @@ -34,7 +34,7 @@ proptest::proptest! { #[test] fn bptree_remove_consistent(values in proptest::collection::btree_set(proptest::arbitrary::any::(), 1..256), indices: Vec ) { let mut btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v.to_string(), v.to_string()))); - let bptree_map = BptreeMap::from_iter(values.iter().cloned().map(|v| (v.to_string(), v.to_string()))); + let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v.to_string(), v.to_string()))); let mut bptree_map_write_tx = bptree_map.write(); for index in indices { @@ -66,7 +66,7 @@ fn bptree_remove_1() { let to_remove = [9u8, 27, 40, 4].map(|v| v.to_string()); - let bptree_map = BptreeMap::from_iter( + let bptree_map: BptreeMap = BptreeMap::from_iter( values .iter() .cloned() From 7a50d6357c54d38f60d84b8a9e5359de30862dae Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Thu, 10 Jul 2025 15:11:39 +0930 Subject: [PATCH 03/10] Fix broken `Debug` derive from recurive trait requirements. --- src/cowcell/mod.rs | 2 +- src/internals/lincowcell/mod.rs | 139 +++++++++++++++++++++++++------- 2 files changed, 113 insertions(+), 28 deletions(-) diff --git a/src/cowcell/mod.rs b/src/cowcell/mod.rs index 0648618..f88c033 100644 --- a/src/cowcell/mod.rs +++ b/src/cowcell/mod.rs @@ -44,7 +44,7 @@ use std::sync::Arc; /// use concread::cowcell::CowCell; /// /// let data: i64 = 0; -/// let cowcell = CowCell::new(data); +/// let cowcell = CowCell::::new(data); /// /// // Begin a read transaction /// let read_txn = cowcell.read(); diff --git a/src/internals/lincowcell/mod.rs b/src/internals/lincowcell/mod.rs index b16a079..70df5c4 100644 --- a/src/internals/lincowcell/mod.rs +++ b/src/internals/lincowcell/mod.rs @@ -55,14 +55,15 @@ * */ -#[cfg(feature = "std")] -use std::sync::Arc; #[cfg(not(feature = "std"))] use alloc::sync::Arc; +#[cfg(feature = "std")] +use std::sync::Arc; -use std::marker::PhantomData; -use std::ops::Deref; -use std::ops::DerefMut; +use core::fmt::Debug; +use core::marker::PhantomData; +use core::ops::Deref; +use core::ops::DerefMut; use lock_api::RawMutex; use lock_api::{Mutex, MutexGuard}; @@ -80,7 +81,6 @@ pub trait LinCowCellCapable { fn pre_commit(&mut self, new: U, prev: &R) -> R; } -#[derive(Debug)] /// A concurrently readable cell with linearised drop behaviour. pub struct LinCowCell { updater: PhantomData, @@ -88,29 +88,70 @@ pub struct LinCowCell { active: Mutex>>, } -#[derive(Debug)] -/// A write txn over a linear cell. -pub struct LinCowCellWriteTxn<'a, T, R, U, M: RawMutex> { - // This way we know who to contact for updating our data .... - caller: &'a LinCowCell, - guard: MutexGuard<'a, M, T>, - work: U, +impl Debug for LinCowCell { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut f = f.debug_struct("LinCowCell"); + match self.write.try_lock() { + Some(guard) => { + f.field("write", &&*guard); + } + None => { + struct LockedPlaceholder; + impl core::fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("") + } + } + + f.field("write", &LockedPlaceholder); + } + } + match self.active.try_lock() { + Some(guard) => { + f.field("active", &&*guard); + } + None => { + struct LockedPlaceholder; + impl core::fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("") + } + } + + f.field("active", &LockedPlaceholder); + } + } + + f.finish() + } } -#[derive(Debug)] struct LinCowCellInner { // This gives the chain effect. - pin: Mutex>>>, + pin: Mutex>>>, data: R, } -#[derive(Debug)] -/// A read txn over a linear cell. -pub struct LinCowCellReadTxn<'a, T, R, U, M: RawMutex> { - // We must outlive the root - _caller: &'a LinCowCell, - // We pin the current version. - work: Arc>, +impl Debug for LinCowCellInner { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut f = f.debug_struct("LinCowCellInner"); + match self.pin.try_lock() { + Some(guard) => { + f.field("pin", &&*guard); + } + None => { + struct LockedPlaceholder; + impl core::fmt::Debug for LockedPlaceholder { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("") + } + } + + f.field("pin", &LockedPlaceholder); + } + } + f.field("data", &self.data).finish() + } } impl LinCowCellInner { @@ -144,10 +185,44 @@ impl Drop for LinCowCellInner { } } +/// A read txn over a linear cell. +pub struct LinCowCellReadTxn<'a, T, R, U, M: RawMutex> { + // We must outlive the root + _caller: &'a LinCowCell, + // We pin the current version. + work: Arc>, +} + +impl Debug for LinCowCellReadTxn<'_, T, R, U, M> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LinCowCellReadTxn") + .field("work", &self.work) + .finish_non_exhaustive() + } +} + +/// A write txn over a linear cell. +pub struct LinCowCellWriteTxn<'a, T, R, U, M: RawMutex> { + // This way we know who to contact for updating our data .... + caller: &'a LinCowCell, + guard: MutexGuard<'a, M, T>, + work: U, +} + +impl Debug for LinCowCellWriteTxn<'_, T, R, U, M> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LinCowCellWriteTxn") + .field("caller", &self.caller) + .field("guard", &self.guard) + .field("work", &self.work) + .finish() + } +} + impl LinCowCell where T: LinCowCellCapable, - M: RawMutex + M: RawMutex, { /// Create a new linear 🐄 cell. pub fn new(data: T) -> Self { @@ -246,7 +321,7 @@ impl AsRef for LinCowCellReadTxn<'_, T, R, U, M> { impl LinCowCellWriteTxn<'_, T, R, U, M> where T: LinCowCellCapable, - M: RawMutex + M: RawMutex, { #[inline] /// Get the mutable inner of this type @@ -340,7 +415,7 @@ mod tests { let data = TestData { x: 0 }; let cc: LinCowCell = LinCowCell::new(data); - let cc_rotxn_a= cc.read(); + let cc_rotxn_a = cc.read(); println!("cc_rotxn_a -> {:?}", cc_rotxn_a); assert_eq!(cc_rotxn_a.work.data.x, 0); @@ -364,7 +439,13 @@ mod tests { assert_eq!(cc_rotxn_a.work.data.x, 0); { /* Take a new write txn */ - let mut cc_wrtxn = cc.write(); + let mut cc_wrtxn: crate::internals::lincowcell::LinCowCellWriteTxn< + '_, + TestData, + TestDataReadTxn, + TestDataWriteTxn, + parking_lot::RawMutex, + > = cc.write(); println!("cc_wrtxn -> {:?}", cc_wrtxn); assert_eq!(cc_wrtxn.work.x, 0); assert_eq!(cc_wrtxn.as_ref().x, 0); @@ -642,7 +723,11 @@ mod tests_linear { GC_COUNT.store(0, Ordering::Release); assert!(GC_COUNT.load(Ordering::Acquire) == 0); let data = TestGcWrapper { data: 0 }; - let cc: LinCowCell, TestGcWrapperReadTxn, TestGcWrapperWriteTxn> = LinCowCell::new(data); + let cc: LinCowCell< + TestGcWrapper, + TestGcWrapperReadTxn, + TestGcWrapperWriteTxn, + > = LinCowCell::new(data); // Open a read A. let cc_rotxn_a = cc.read(); From cfd0921f76412a9476e1686a4101cfb1b86cc57d Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Thu, 10 Jul 2025 15:13:48 +0930 Subject: [PATCH 04/10] Remove my vscode settings file from the changes. --- .vscode/settings.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 62ba47e..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rust-analyzer.cargo.allTargets": true, - "rust-analyzer.check.features": ["default"] -} \ No newline at end of file From 8916852aa01161bdb0d7b7100fc47c9be9cc065d Mon Sep 17 00:00:00 2001 From: William Brown Date: Fri, 11 Jul 2025 13:11:54 +1000 Subject: [PATCH 05/10] tidy up --- Cargo.toml | 8 +- src/arcache/ll.rs | 6 +- src/arcache/mod.rs | 80 +++++---- src/bptree/asynch.rs | 14 +- src/bptree/mod.rs | 29 ++-- src/cowcell/mod.rs | 14 +- src/ebrcell/mod.rs | 9 +- src/hashmap/asynch.rs | 14 +- src/hashmap/mod.rs | 28 +-- src/hashtrie/asynch.rs | 14 +- src/hashtrie/mod.rs | 31 ++-- src/internals/bptree/cursor.rs | 240 ++++++++++++++++++++------ src/internals/bptree/iter.rs | 4 +- src/internals/bptree/mutiter.rs | 5 +- src/internals/bptree/node.rs | 13 +- src/internals/hashmap/cursor.rs | 205 +++++++++++++++++----- src/internals/hashmap/iter.rs | 4 +- src/internals/hashmap/node.rs | 4 +- src/internals/hashmap/simd.rs | 4 +- src/internals/hashtrie/cursor.rs | 46 +++-- src/internals/hashtrie/iter.rs | 4 +- src/internals/lincowcell_async/mod.rs | 25 ++- src/lc_tests.rs | 19 +- src/lib.rs | 9 +- src/utils.rs | 9 +- tests/bptree_map.rs | 135 ++++++++------- tests/lib.rs | 2 +- 27 files changed, 662 insertions(+), 313 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 85bb9b6..24a79c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,31 +23,29 @@ name = "concread" path = "src/lib.rs" [features] -default = ["std", "asynch","foldhash", "ebr", "maps", "arcache-is-hashtrie"] +default = ["std", "asynch", "foldhash", "ebr", "maps", "arcache-is-hashtrie"] # Features to add/remove contents. ahash = ["dep:ahash"] foldhash = ["dep:foldhash"] - arcache = ["maps", "lru", "crossbeam-queue"] asynch = ["dep:tokio", "std"] ebr = ["std"] maps = ["dep:crossbeam-utils", "smallvec"] tcache = [] std = ["ahash/std", "ahash/runtime-rng", "crossbeam-epoch/std", "crossbeam-queue/std", "crossbeam-utils/std", "tracing/std", "dep:parking_lot", "smallvec/write"] -#serde = ["lock_api/serde"] - no_std = ["crossbeam-epoch/alloc", "crossbeam-queue/alloc", "serde/alloc", "dep:spin", "ahash"] + # Internal features for tweaking some align/perf behaviours. dhat-heap = ["dep:dhat"] skinny = [] hashtrie_skinny = [] - arcache-is-hashmap = ["arcache"] arcache-is-hashtrie = ["arcache"] simd_support = [] + [dependencies] ahash = { version = "0.8", default-features = false, optional = true} foldhash = { version = "0.1.5",default-features = false, optional = true } diff --git a/src/arcache/ll.rs b/src/arcache/ll.rs index fb3c19b..06dd69a 100644 --- a/src/arcache/ll.rs +++ b/src/arcache/ll.rs @@ -1,9 +1,7 @@ - - -#[cfg(feature = "std")] -use std::boxed::Box; #[cfg(not(feature = "std"))] use alloc::boxed::Box; +#[cfg(feature = "std")] +use std::boxed::Box; use std::fmt::Debug; use std::marker::PhantomData; diff --git a/src/arcache/mod.rs b/src/arcache/mod.rs index de5bfdc..17a893e 100644 --- a/src/arcache/mod.rs +++ b/src/arcache/mod.rs @@ -10,11 +10,10 @@ //! writers that are serialised. This formally means that this is an ACID //! compliant Cache. - -#[cfg(feature = "std")] -use std::{vec::Vec, borrow::ToOwned, sync::Arc}; #[cfg(not(feature = "std"))] -use alloc::{vec::Vec, borrow::ToOwned, sync::Arc}; +use alloc::{borrow::ToOwned, sync::Arc, vec::Vec}; +#[cfg(feature = "std")] +use std::{borrow::ToOwned, sync::Arc, vec::Vec}; mod ll; /// Stats collection for [ARCache] @@ -36,8 +35,8 @@ use crate::utils::{self, Monotonic}; use crossbeam_queue::ArrayQueue; use hashbrown::HashMap as Map; -use std::sync::atomic::{AtomicBool, Ordering}; use lock_api::{Mutex, RawMutex, RawRwLock, RwLock}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::borrow::Borrow; use std::cell::UnsafeCell; @@ -68,7 +67,7 @@ const WATERMARK_DISABLE_RATIO: usize = 18; mod monotonic_timer { pub struct MonotonicTimer; - unsafe impl crate::utils::Monotonic for MonotonicTimer{ + unsafe impl crate::utils::Monotonic for MonotonicTimer { type Output = std::time::Instant; fn new() -> Self { @@ -89,9 +88,9 @@ mod monotonic_timer { mod monotonic_timer { use std::sync::atomic::{AtomicUsize, Ordering}; /// This provides a mnonotonic generation counter, with the bit width equal to the pointer width of the platform. - /// + /// /// # SAFETY - /// + /// /// This wraps around on overflow, so the result becomes invalid if you call it more than `usize::MAX`. /// Overflow will panic on debug, and continue on release mode. pub struct MonotonicTimer(AtomicUsize); @@ -113,7 +112,10 @@ mod monotonic_timer { // we can use relaxed ordering here as it still guarantees that each value will only be observed once. // the downside is that close calls may have re-ordered insertion do the relaxed ordering on the read. let counter = self.0.fetch_add(1, Ordering::Relaxed); - debug_assert!(counter != usize::MAX, "The default monotonic counter reached the maximum number of valid calls"); + debug_assert!( + counter != usize::MAX, + "The default monotonic counter reached the maximum number of valid calls" + ); counter } } @@ -211,7 +213,7 @@ struct ArcInner where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, - M: Monotonic + M: Monotonic, { /// Weight of items between the two caches. p: usize, @@ -229,7 +231,7 @@ struct ArcShared where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, - M: Monotonic + M: Monotonic, { // Max number of elements to cache. max: usize, @@ -246,13 +248,18 @@ where /// A concurrently readable adaptive replacement cache. Operations are performed on the /// cache via read and write operations. -pub struct ARCache -where +pub struct ARCache< + K, + V, + M = monotonic_timer::MonotonicTimer, + RawMutexImpl = utils::DefaultRawMutex, + RawRwLockImpl = utils::DefaultRawRwLock, +> where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, M: Monotonic + 'static, RawMutexImpl: RawMutex + 'static, - RawRwLockImpl: RawRwLock + 'static + RawRwLockImpl: RawRwLock + 'static, { // Use a unified tree, allows simpler movement of items between the // cache types. @@ -265,7 +272,7 @@ where // stats: CowCell, above_watermark: AtomicBool, look_back_limit: u64, - monotonic: M + monotonic: M, } unsafe impl< @@ -273,7 +280,7 @@ unsafe impl< V: Clone + Debug + Sync + Send + 'static, M: Monotonic + Send + Send + 'static, Mutex: RawMutex + Sync + Send + 'static, - RwLock: RawRwLock + Sync + Send + 'static + RwLock: RawRwLock + Sync + Send + 'static, > Send for ARCache { } @@ -282,7 +289,7 @@ unsafe impl< V: Clone + Debug + Sync + Send + 'static, M: Monotonic + Send + 'static, Mutex: RawMutex + Sync + 'static, - RwLock: RawRwLock + Sync + 'static + RwLock: RawRwLock + Sync + 'static, > Sync for ARCache { } @@ -331,7 +338,7 @@ where S: ARCacheReadStat + Clone, M: Monotonic + 'static, Mutex: RawMutex + 'static, - RwLock: RawRwLock + 'static + RwLock: RawRwLock + 'static, { caller: &'a ARCache, // ro_txn to cache @@ -350,7 +357,7 @@ unsafe impl< S: ARCacheReadStat + Clone + Sync + Send + 'static, M: Monotonic + Sync + Send + 'static, Mutex: RawMutex + Sync + Send + 'static, - RwLock: RawRwLock + Sync + Send + 'static + RwLock: RawRwLock + Sync + Send + 'static, > Send for ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { } @@ -360,7 +367,7 @@ unsafe impl< S: ARCacheReadStat + Clone + Sync + Send + 'static, M: Monotonic + Sync + 'static, Mutex: RawMutex + Sync + 'static, - RwLock: RawRwLock + Sync+ 'static + RwLock: RawRwLock + Sync + 'static, > Sync for ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { } @@ -375,8 +382,8 @@ where V: Clone + Debug + Sync + Send + 'static, S: ARCacheWriteStat, M: Monotonic + 'static, - Mutex: RawMutex + 'static, - RwLock: RawRwLock + 'static + Mutex: RawMutex + 'static, + RwLock: RawRwLock + 'static, { caller: &'a ARCache, // wr_txn to cache @@ -432,7 +439,7 @@ pub struct ARCacheBuilder { watermark: Option, reader_quiesce: bool, look_back_limit: Option, - monotonic: Option + monotonic: Option, } impl Default for ARCacheBuilder { @@ -443,14 +450,14 @@ impl Default for ARCacheBuilder { watermark: None, reader_quiesce: true, look_back_limit: None, - monotonic: None + monotonic: None, } } } impl ARCacheBuilder where - M: Monotonic + M: Monotonic, { /// Create a new ARCache builder that you can configure before creation. pub fn new() -> Self { @@ -567,7 +574,9 @@ where /// Consume this builder, returning a cache if successful. If configured parameters are /// missing or incorrect, a None will be returned. - pub fn build(self) -> Option> + pub fn build( + self, + ) -> Option> where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, @@ -579,7 +588,7 @@ where watermark, reader_quiesce, look_back_limit, - monotonic + monotonic, } = self; let (max, read_max) = max.zip(read_max)?; @@ -611,7 +620,7 @@ where let chan_size = READ_THREAD_CHANNEL_SIZE; let inc_queue = Arc::new(ArrayQueue::new(chan_size)); - let shared = RwLock::>::new(ArcShared { + let shared = RwLock::>::new(ArcShared { max, read_max, // stat_tx, @@ -641,7 +650,7 @@ where // stats: CowCell::new(stats), above_watermark: AtomicBool::new(init_watermark), look_back_limit, - monotonic: monotonic.unwrap_or(M::new()) + monotonic: monotonic.unwrap_or(M::new()), }) } } @@ -651,7 +660,7 @@ impl< V: Clone + Debug + Sync + Send + 'static, M: Monotonic + 'static, Mutex: RawMutex + 'static, - RwLock: RawRwLock + 'static + RwLock: RawRwLock + 'static, > ARCache { /// Use ARCacheBuilder instead @@ -751,7 +760,10 @@ impl< } } - fn try_write_stats(&self, stats: S) -> Result, S> + fn try_write_stats( + &self, + stats: S, + ) -> Result, S> where S: ARCacheWriteStat, { @@ -1679,7 +1691,7 @@ impl< S: ARCacheWriteStat, M: Monotonic + 'static, Mutex: RawMutex + 'static, - RwLock: RawRwLock + 'static + RwLock: RawRwLock + 'static, > ARCacheWriteTxn<'_, K, V, S, M, Mutex, RwLock> { /// Commit the changes of this writer, making them globally visible. This causes @@ -2074,7 +2086,7 @@ impl< S: ARCacheReadStat + Clone, M: Monotonic + 'static, Mutex: RawMutex, - RwLock: RawRwLock + RwLock: RawRwLock, > ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { /// Attempt to retrieve a k-v pair from the cache. If it is present in the main cache OR @@ -2232,7 +2244,7 @@ impl< S: ARCacheReadStat + Clone, M: Monotonic + 'static, Mutex: RawMutex, - RwLock: RawRwLock + RwLock: RawRwLock, > Drop for ARCacheReadTxn<'_, K, V, S, M, Mutex, RwLock> { fn drop(&mut self) { diff --git a/src/bptree/asynch.rs b/src/bptree/asynch.rs index bda9ede..6d8e83e 100644 --- a/src/bptree/asynch.rs +++ b/src/bptree/asynch.rs @@ -13,8 +13,11 @@ use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCe include!("impl.rs"); -impl - BptreeMap +impl< + K: Clone + Ord + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + R: RawMutex + 'static, + > BptreeMap { /// Initiate a read transaction for the tree, concurrent to any /// other readers or writers. @@ -31,8 +34,11 @@ impl - BptreeMapWriteTxn<'_, K, V, R> +impl< + K: Clone + Ord + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + R: RawMutex + 'static, + > BptreeMapWriteTxn<'_, K, V, R> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceive these changes. diff --git a/src/bptree/mod.rs b/src/bptree/mod.rs index 2ac0d03..eee192b 100644 --- a/src/bptree/mod.rs +++ b/src/bptree/mod.rs @@ -16,8 +16,11 @@ use crate::internals::lincowcell::{LinCowCell, LinCowCellReadTxn, LinCowCellWrit include!("impl.rs"); -impl - BptreeMap +impl< + K: Clone + Ord + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex, + > BptreeMap { /// Initiate a read transaction for the tree, concurrent to any /// other readers or writers. @@ -34,8 +37,11 @@ impl - BptreeMapWriteTxn<'_, K, V, M> +impl< + K: Clone + Ord + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex, + > BptreeMapWriteTxn<'_, K, V, M> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceive these changes. @@ -51,7 +57,7 @@ impl Serialize for BptreeMapReadTxn<'_, K, V, M> where K: Serialize + Clone + Ord + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn serialize(&self, serializer: S) -> Result where @@ -72,7 +78,7 @@ impl Serialize for BptreeMap where K: Serialize + Clone + Ord + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn serialize(&self, serializer: S) -> Result where @@ -87,7 +93,7 @@ impl<'de, K, V, M> Deserialize<'de> for BptreeMap where K: Deserialize<'de> + Clone + Ord + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn deserialize(deserializer: D) -> Result where @@ -346,7 +352,8 @@ mod tests { fn test_bptree2_map_rangeiter_1() { let ins: Vec = (0..100).collect(); - let map: BptreeMap = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); + let map: BptreeMap = + BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); { let w = map.write(); @@ -362,7 +369,8 @@ mod tests { #[test] fn test_bptree2_map_rangeiter_2() { - let map: BptreeMap = BptreeMap::from_iter([(3, ()), (4, ()), (0, ())]); + let map: BptreeMap = + BptreeMap::from_iter([(3, ()), (4, ()), (0, ())]); let r = map.read(); assert!(r.range(1..=2).count() == 0); @@ -370,7 +378,8 @@ mod tests { #[test] fn test_bptree2_map_rangeiter_3() { - let map: BptreeMap = BptreeMap::from_iter([0, 1, 2, 3, 4, 5, 6, 8].map(|v| (v, ()))); + let map: BptreeMap = + BptreeMap::from_iter([0, 1, 2, 3, 4, 5, 6, 8].map(|v| (v, ()))); let r = map.read(); assert!(r.range((Bound::Excluded(6), Bound::Included(7))).count() == 0); diff --git a/src/cowcell/mod.rs b/src/cowcell/mod.rs index f88c033..812ad4b 100644 --- a/src/cowcell/mod.rs +++ b/src/cowcell/mod.rs @@ -16,7 +16,7 @@ use core::ops::{Deref, DerefMut}; use lock_api::{Mutex, MutexGuard, RawMutex}; #[cfg(not(feature = "std"))] -use alloc::sync::Arc; +use ::alloc::sync::Arc; #[cfg(feature = "std")] use std::sync::Arc; @@ -257,10 +257,6 @@ where #[cfg(test)] mod tests { use super::CowCell; - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::time::Instant; - - use std::thread::scope; #[test] fn test_deref_mut() { @@ -320,6 +316,14 @@ mod tests { assert_eq!(*cc_rotxn_c, 1); assert_eq!(*cc_rotxn_a, 0); } +} + +#[cfg(all(test, feature = "std"))] +mod tests_std { + use super::CowCell; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::thread::scope; + use std::time::Instant; const MAX_TARGET: i64 = 2000; diff --git a/src/ebrcell/mod.rs b/src/ebrcell/mod.rs index cb688ef..ae97a5d 100644 --- a/src/ebrcell/mod.rs +++ b/src/ebrcell/mod.rs @@ -15,9 +15,9 @@ //! or crossbeam library components. //! If you need accurate memory reclaim, use the Arc (`CowCell`) implementation. -use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crossbeam_epoch as epoch; use crossbeam_epoch::{Atomic, Guard, Owned}; +use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use std::mem; use std::ops::{Deref, DerefMut}; @@ -33,10 +33,7 @@ use std::sync::{Mutex, MutexGuard}; /// abort a change, don't call commit and allow the write transaction to /// go out of scope. This causes the `EbrCell` to unlock allowing other /// writes to proceed. -pub struct EbrCellWriteTxn< - 'a, - T: 'static + Clone + Send + Sync -> { +pub struct EbrCellWriteTxn<'a, T: 'static + Clone + Send + Sync> { data: Option, // This way we know who to contact for updating our data .... caller: &'a EbrCell, @@ -135,7 +132,7 @@ where #[derive(Debug)] pub struct EbrCell where - T: Clone + Sync + Send + 'static + T: Clone + Sync + Send + 'static, { write: Mutex<()>, active: Atomic, diff --git a/src/hashmap/asynch.rs b/src/hashmap/asynch.rs index 237e400..95a53cb 100644 --- a/src/hashmap/asynch.rs +++ b/src/hashmap/asynch.rs @@ -17,8 +17,11 @@ use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCe include!("impl.rs"); -impl - HashMap +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashMap { /// Construct a new concurrent hashmap pub fn new() -> Self { @@ -51,8 +54,11 @@ impl - HashMapWriteTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashMapWriteTxn<'_, K, V, M> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceive these changes. diff --git a/src/hashmap/mod.rs b/src/hashmap/mod.rs index 1dae105..533242f 100644 --- a/src/hashmap/mod.rs +++ b/src/hashmap/mod.rs @@ -19,7 +19,6 @@ #![allow(clippy::implicit_hasher)] - #[cfg(feature = "asynch")] pub mod asynch; @@ -39,8 +38,11 @@ use crate::internals::lincowcell::{LinCowCell, LinCowCellReadTxn, LinCowCellWrit include!("impl.rs"); -impl - HashMap +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashMap { /// Construct a new concurrent hashmap pub fn new() -> Self { @@ -73,8 +75,11 @@ impl - HashMapWriteTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashMapWriteTxn<'_, K, V, M> { #[cfg(all(feature = "arcache", feature = "arcache-is-hashmap"))] pub(crate) fn get_txid(&self) -> u64 { @@ -108,8 +113,11 @@ impl - HashMapReadTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex, + > HashMapReadTxn<'_, K, V, M> { #[cfg(all(feature = "arcache", feature = "arcache-is-hashmap"))] pub(crate) fn get_txid(&self) -> u64 { @@ -131,7 +139,7 @@ impl Serialize for HashMapReadTxn<'_, K, V, M> where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, - M: RawMutex + M: RawMutex, { fn serialize(&self, serializer: S) -> Result where @@ -152,7 +160,7 @@ impl Serialize for HashMap where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn serialize(&self, serializer: S) -> Result where @@ -167,7 +175,7 @@ impl<'de, K, V, M> Deserialize<'de> for HashMap where K: Deserialize<'de> + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn deserialize(deserializer: D) -> Result where diff --git a/src/hashtrie/asynch.rs b/src/hashtrie/asynch.rs index 0e173f3..d80b170 100644 --- a/src/hashtrie/asynch.rs +++ b/src/hashtrie/asynch.rs @@ -17,8 +17,11 @@ use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCe include!("impl.rs"); -impl - HashTrie +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashTrie { /// Construct a new concurrent hashtrie pub fn new() -> Self { @@ -51,8 +54,11 @@ impl - HashTrieWriteTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashTrieWriteTxn<'_, K, V, M> { /// Commit the changes from this write transaction. Readers after this point /// will be able to perceieve these changes. diff --git a/src/hashtrie/mod.rs b/src/hashtrie/mod.rs index a11b93f..cc9a50d 100644 --- a/src/hashtrie/mod.rs +++ b/src/hashtrie/mod.rs @@ -43,14 +43,19 @@ use crate::internals::lincowcell::{LinCowCell, LinCowCellReadTxn, LinCowCellWrit include!("impl.rs"); -impl - HashTrie +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashTrie { /// Construct a new concurrent hashtrie pub fn new() -> Self { // I acknowledge I understand what is required to make this safe. HashTrie { - inner: LinCowCell::, CursorRead, CursorWrite, M>::new(unsafe { SuperBlock::new() }), + inner: LinCowCell::, CursorRead, CursorWrite, M>::new( + unsafe { SuperBlock::new() }, + ), } } @@ -77,8 +82,11 @@ impl - HashTrieWriteTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashTrieWriteTxn<'_, K, V, M> { /// View the current transaction ID for this cache. This is a monotonically increasing /// value. If two transactions have the same txid, they are the same data generation. @@ -113,8 +121,11 @@ impl - HashTrieReadTxn<'_, K, V, M> +impl< + K: Hash + Eq + Clone + Debug + Sync + Send + 'static, + V: Clone + Sync + Send + 'static, + M: RawMutex + 'static, + > HashTrieReadTxn<'_, K, V, M> { /// View the current transaction ID for this cache. This is a monotonically increasing /// value. If two transactions have the same txid, they are the same data generation. @@ -137,7 +148,7 @@ impl Serialize for HashTrieReadTxn<'_, K, V, M> where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn serialize(&self, serializer: S) -> Result where @@ -158,7 +169,7 @@ impl Serialize for HashTrie where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn serialize(&self, serializer: S) -> Result where @@ -173,7 +184,7 @@ impl<'de, K, V, M> Deserialize<'de> for HashTrie where K: Deserialize<'de> + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, - M: RawMutex + 'static + M: RawMutex + 'static, { fn deserialize(deserializer: D) -> Result where diff --git a/src/internals/bptree/cursor.rs b/src/internals/bptree/cursor.rs index fe4d4cc..f1b3821 100644 --- a/src/internals/bptree/cursor.rs +++ b/src/internals/bptree/cursor.rs @@ -1349,7 +1349,10 @@ mod tests { // First create the node + cursor let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); eprintln!("{:?}", wcurs); @@ -1390,7 +1393,10 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let prev_txid = wcurs.root_txid(); let r = wcurs.insert(1, 1); @@ -1412,7 +1418,10 @@ mod tests { // to trigger a clone of leaf AND THEN to cause the split. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(L_CAPACITY + 1) { // println!("ITER v {}", v); @@ -1440,7 +1449,10 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // println!("{:?}", wcurs); @@ -1470,7 +1482,10 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(29, 29); @@ -1498,7 +1513,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1532,7 +1550,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1563,7 +1584,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(11, 11); @@ -1598,7 +1622,10 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(19, 19); @@ -1623,7 +1650,10 @@ mod tests { // so we do this to a reasonable number. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { // println!("ITER v {}", v); @@ -1644,7 +1674,10 @@ mod tests { // Insert descending let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in (1..(L_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -1669,7 +1702,10 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v, v); @@ -1693,7 +1729,10 @@ mod tests { let mut rdr: CursorRead = sb.create_reader(); for v in 1..(L_CAPACITY << 4) { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v, v); assert!(r.is_none()); @@ -1715,7 +1754,10 @@ mod tests { let mut rdr: CursorRead = sb.create_reader(); for v in (1..(L_CAPACITY << 4)).rev() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v, v); assert!(r.is_none()); @@ -1740,7 +1782,10 @@ mod tests { let mut rdr: CursorRead = sb.create_reader(); for v in ins.into_iter() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let r = wcurs.insert(v, v); assert!(r.is_none()); assert!(wcurs.verify()); @@ -1757,7 +1802,10 @@ mod tests { fn test_bptree2_cursor_search_1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { let r = wcurs.insert(v, v); @@ -1781,7 +1829,10 @@ mod tests { // Check the length is consistent on operations. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { let r = wcurs.insert(v, v); @@ -1801,7 +1852,10 @@ mod tests { // let lnode = create_leaf_node_full(0); let sb = SuperBlock::new_test(1, lnode); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("{:?}", wcurs); for v in 0..L_CAPACITY { @@ -1824,7 +1878,10 @@ mod tests { fn test_bptree2_cursor_remove_01_p1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let _ = wcurs.remove(&0); // println!("{:?}", wcurs); @@ -1850,7 +1907,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("{:?}", wcurs); assert!(wcurs.verify()); @@ -1877,7 +1937,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -1903,7 +1966,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Setup sibling leaf to already be cloned. @@ -1933,7 +1999,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -1963,7 +2032,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -2002,7 +2074,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2040,7 +2115,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -2077,7 +2155,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&80); @@ -2114,7 +2195,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -2151,7 +2235,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2192,7 +2279,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(&20); @@ -2232,7 +2322,10 @@ mod tests { let root = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); // let count = BV_CAPACITY + 2; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(&0); @@ -2272,7 +2365,10 @@ mod tests { let root = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); for i in 0..BV_CAPACITY { @@ -2296,7 +2392,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&20); @@ -2314,7 +2413,10 @@ mod tests { let rnode = create_leaf_node_full(20) as *mut Node; let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(&10); @@ -2332,7 +2434,10 @@ mod tests { let mut sb = unsafe { SuperBlock::new() }; let rdr = sb.create_reader(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v, v); @@ -2348,7 +2453,10 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(L_CAPACITY << 4) { // println!("-- ITER v {}", v); @@ -2369,7 +2477,10 @@ mod tests { fn test_bptree2_cursor_remove_stress_2() { // Insert descending let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in (1..(L_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -2392,7 +2503,10 @@ mod tests { ins.shuffle(&mut rng); let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.remove(&v); @@ -2414,7 +2528,10 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in 1..(L_CAPACITY << 4) { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(&v); assert!(r == Some(v)); @@ -2432,7 +2549,10 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in (1..(L_CAPACITY << 4)).rev() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(&v); assert!(r == Some(v)); @@ -2454,7 +2574,10 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in ins.into_iter() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let r = wcurs.remove(&v); assert!(r == Some(v)); assert!(wcurs.verify()); @@ -2556,7 +2679,10 @@ mod tests { // Do a split_off_lt. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); wcurs.split_off_lt(&5); @@ -2574,7 +2700,10 @@ mod tests { // Do a split_off_lt. let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); wcurs.split_off_lt(&11); @@ -2592,7 +2721,10 @@ mod tests { // Do a split_off_lt. let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); wcurs.path_clone(&11); wcurs.split_off_lt(&11); @@ -2611,7 +2743,10 @@ mod tests { let tree = create_split_off_tree(); let sb = SuperBlock::new_test(1, tree); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // 0 is min, and not present, will cause no change. // clone everything let outer: [usize; 4] = [0, 100, 200, 300]; @@ -2642,7 +2777,10 @@ mod tests { // println!("START -> {:?}", tree); let sb = SuperBlock::new_test(1, tree); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // 0 is min, and not present, will cause no change. wcurs.split_off_lt(&v); assert!(wcurs.verify()); @@ -2698,7 +2836,10 @@ mod tests { for v in data.iter() { let node: *mut Leaf = Node::new_leaf(0) as *mut _; let sb = SuperBlock::new_test(1, node as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); wcurs.extend(data.iter().map(|v| (*v, *v))); if v > &0 { @@ -2721,7 +2862,10 @@ mod tests { fn test_bptree_cursor_double_extend() { let node: *mut Leaf = Node::new_leaf(0) as *mut _; let sb = SuperBlock::new_test(1, node as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); wcurs.extend([(0, 0), (1, 1), (2, 2), (3, 3)]); assert!(wcurs.len() == 4); diff --git a/src/internals/bptree/iter.rs b/src/internals/bptree/iter.rs index 541847f..cc284cb 100644 --- a/src/internals/bptree/iter.rs +++ b/src/internals/bptree/iter.rs @@ -1,9 +1,9 @@ //! Iterators for the map. -#[cfg(feature = "std")] -use std::collections::VecDeque; #[cfg(not(feature = "std"))] use alloc::collections::VecDeque; +#[cfg(feature = "std")] +use std::collections::VecDeque; // Iterators for the bptree use super::node::{Branch, Leaf, Meta, Node}; diff --git a/src/internals/bptree/mutiter.rs b/src/internals/bptree/mutiter.rs index 0447fcf..87bb313 100644 --- a/src/internals/bptree/mutiter.rs +++ b/src/internals/bptree/mutiter.rs @@ -104,7 +104,10 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let bounds: (Bound, Bound) = (Unbounded, Unbounded); let range_mut_iter = RangeMutIter::new(&mut wcurs, bounds); diff --git a/src/internals/bptree/node.rs b/src/internals/bptree/node.rs index 379e387..7c86d1c 100644 --- a/src/internals/bptree/node.rs +++ b/src/internals/bptree/node.rs @@ -1,28 +1,27 @@ use super::states::*; use crate::utils::*; // use libc::{c_void, mprotect, PROT_READ, PROT_WRITE}; +use crossbeam_utils::CachePadded; use std::borrow::Borrow; use std::fmt::{self, Debug, Error}; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::ptr; use std::slice; -use crossbeam_utils::CachePadded; -#[cfg(feature = "std")] -use std::{boxed, vec}; #[cfg(not(feature = "std"))] use alloc::{boxed, vec}; +#[cfg(feature = "std")] +use std::{boxed, vec}; use boxed::Box; use vec::Vec; - -#[cfg(all(test, not(miri)))] -use std::sync::atomic::{AtomicUsize, Ordering}; #[cfg(test)] use std::collections::BTreeSet; #[cfg(all(test, not(miri)))] +use std::sync::atomic::{AtomicUsize, Ordering}; +#[cfg(all(test, not(miri)))] use std::sync::Mutex; pub(crate) const TXID_MASK: u64 = 0x0fff_ffff_ffff_fff0; @@ -877,7 +876,7 @@ impl Leaf { let rk: &K = unsafe { &*(*pointer).key[work_idx].as_ptr() }; if lk >= rk { // println!("{:?}", self); - cfg_if::cfg_if!{ if #[cfg(test)] { + cfg_if::cfg_if! { if #[cfg(test)] { return false; } else { debug_assert!(false); diff --git a/src/internals/hashmap/cursor.rs b/src/internals/hashmap/cursor.rs index 8bb631f..d943147 100644 --- a/src/internals/hashmap/cursor.rs +++ b/src/internals/hashmap/cursor.rs @@ -1162,7 +1162,10 @@ mod tests { // First create the node + cursor let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let prev_txid = wcurs.root_txid(); // Now insert - the txid should be different. @@ -1198,7 +1201,10 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let prev_txid = wcurs.root_txid(); let r = wcurs.insert(1, 1, 1); @@ -1220,7 +1226,10 @@ mod tests { // to trigger a clone of leaf AND THEN to cause the split. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(H_CAPACITY + 1) { // println!("ITER v {}", v); @@ -1248,7 +1257,10 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // println!("{:?}", wcurs); @@ -1278,7 +1290,10 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(29, 29, 29); @@ -1306,7 +1321,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1340,7 +1358,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Now insert to trigger the needed actions. @@ -1371,7 +1392,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(11, 11, 11); @@ -1406,7 +1430,10 @@ mod tests { let rnode = create_leaf_node_full(20); let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); let r = wcurs.insert(19, 19, 19); @@ -1431,7 +1458,10 @@ mod tests { // so we do this to a reasonable number. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { // println!("ITER v {}", v); @@ -1452,7 +1482,10 @@ mod tests { // Insert descending let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in (1..(H_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -1477,7 +1510,10 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v as u64, v, v); @@ -1501,7 +1537,10 @@ mod tests { let mut rdr: CursorRead = sb.create_reader(); for v in 1..(H_CAPACITY << 4) { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v as u64, v, v); assert!(r.is_none()); @@ -1522,7 +1561,10 @@ mod tests { let mut rdr: CursorRead = sb.create_reader(); for v in (1..(H_CAPACITY << 4)).rev() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.insert(v as u64, v, v); assert!(r.is_none()); @@ -1547,7 +1589,10 @@ mod tests { let mut rdr: CursorRead = sb.create_reader(); for v in ins.into_iter() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let r = wcurs.insert(v as u64, v, v); assert!(r.is_none()); assert!(wcurs.verify()); @@ -1564,7 +1609,10 @@ mod tests { fn test_hashmap2_cursor_search_1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { let r = wcurs.insert(v as u64, v, v); @@ -1588,7 +1636,10 @@ mod tests { // Check the length is consistent on operations. let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { let r = wcurs.insert(v as u64, v, v); @@ -1611,7 +1662,10 @@ mod tests { // let lnode = create_leaf_node_full(0); let sb = SuperBlock::new_test(1, lnode); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("{:?}", wcurs); for v in 0..H_CAPACITY { @@ -1634,7 +1688,10 @@ mod tests { fn test_hashmap2_cursor_remove_01_p1() { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let _ = wcurs.remove(0, &0); // println!("{:?}", wcurs); @@ -1660,7 +1717,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); println!("{:?}", wcurs); assert!(wcurs.verify()); wcurs.remove(20, &20); @@ -1686,7 +1746,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -1712,7 +1775,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Setup sibling leaf to already be cloned. @@ -1742,7 +1808,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -1772,7 +1841,10 @@ mod tests { // Prevent the tree shrinking. unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); // Setup leaf to already be cloned. @@ -1812,7 +1884,10 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1850,7 +1925,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -1887,7 +1965,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(80, &80); @@ -1924,7 +2005,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -1961,7 +2045,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2002,7 +2089,10 @@ mod tests { let root: *mut Branch = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(20); @@ -2043,7 +2133,10 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _) as *mut Node; // let count = HBV_CAPACITY + 2; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.path_clone(0); @@ -2084,7 +2177,10 @@ mod tests { let root = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); for i in 0..HBV_CAPACITY { @@ -2108,7 +2204,10 @@ mod tests { let rnode = create_leaf_node(20); let root = Node::new_branch(0, lnode, rnode) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(20, &20); @@ -2126,7 +2225,10 @@ mod tests { let rnode = create_leaf_node_full(20) as *mut Node; let root = Node::new_branch(0, lnode, rnode) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wcurs.verify()); wcurs.remove(10, &10); @@ -2144,7 +2246,10 @@ mod tests { let mut sb = unsafe { SuperBlock::new() }; let rdr = sb.create_reader(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.insert(v as u64, v, v); @@ -2161,7 +2266,10 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in 1..(H_CAPACITY << 4) { // println!("-- ITER v {}", v); @@ -2182,7 +2290,10 @@ mod tests { fn test_hashmap2_cursor_remove_stress_2() { // Insert descending let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in (1..(H_CAPACITY << 4)).rev() { // println!("ITER v {}", v); @@ -2208,7 +2319,10 @@ mod tests { ins.shuffle(&mut rng); let (mut sb, rdr) = tree_create_rand(); - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); for v in ins.into_iter() { let r = wcurs.remove(v as u64, &v); @@ -2233,7 +2347,10 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in 1..(H_CAPACITY << 4) { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(v as u64, &v); assert!(r == Some(v)); @@ -2253,7 +2370,10 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in (1..(H_CAPACITY << 4)).rev() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); // println!("ITER v {}", v); let r = wcurs.remove(v as u64, &v); assert!(r == Some(v)); @@ -2278,7 +2398,10 @@ mod tests { let (mut sb, mut rdr) = tree_create_rand(); for v in ins.into_iter() { - let mut wcurs = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wcurs = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); let r = wcurs.remove(v as u64, &v); assert!(r == Some(v)); assert!(wcurs.verify()); diff --git a/src/internals/hashmap/iter.rs b/src/internals/hashmap/iter.rs index fbfb846..d506432 100644 --- a/src/internals/hashmap/iter.rs +++ b/src/internals/hashmap/iter.rs @@ -1,9 +1,9 @@ //! Iterators for the map. -#[cfg(feature = "std")] -use std::collections::VecDeque; #[cfg(not(feature = "std"))] use alloc::collections::VecDeque; +#[cfg(feature = "std")] +use std::collections::VecDeque; // Iterators for the bptree use super::node::{Branch, Leaf, Meta, Node}; diff --git a/src/internals/hashmap/node.rs b/src/internals/hashmap/node.rs index 2adc5b5..67e5574 100644 --- a/src/internals/hashmap/node.rs +++ b/src/internals/hashmap/node.rs @@ -16,10 +16,10 @@ use smallvec::SmallVec; #[cfg(feature = "simd_support")] use std::simd::u64x8; -#[cfg(feature = "std")] -use std::{boxed, vec}; #[cfg(not(feature = "std"))] use alloc::{boxed, vec}; +#[cfg(feature = "std")] +use std::{boxed, vec}; use boxed::Box; use vec::Vec; diff --git a/src/internals/hashmap/simd.rs b/src/internals/hashmap/simd.rs index f4d063a..916f97a 100644 --- a/src/internals/hashmap/simd.rs +++ b/src/internals/hashmap/simd.rs @@ -1,8 +1,8 @@ -#[cfg(feature = "simd_support")] -use std::simd::u64x8; use std::borrow::Borrow; use std::fmt::Debug; use std::hash::Hash; +#[cfg(feature = "simd_support")] +use std::simd::u64x8; use super::node::{Branch, Leaf}; diff --git a/src/internals/hashtrie/cursor.rs b/src/internals/hashtrie/cursor.rs index 4132f92..9836f92 100644 --- a/src/internals/hashtrie/cursor.rs +++ b/src/internals/hashtrie/cursor.rs @@ -4,25 +4,24 @@ //! Additionally, the cursor also is responsible for general movement //! throughout the structure and how to handle that effectively - -#[cfg(feature = "std")] -use std::{boxed, vec, collections}; #[cfg(not(feature = "std"))] -use alloc::{boxed, vec, collections}; +use alloc::{boxed, collections, vec}; +#[cfg(feature = "std")] +use std::{boxed, collections, vec}; use boxed::Box; use vec::Vec; use crate::internals::lincowcell::LinCowCellCapable; +use collections::{BTreeSet, VecDeque}; +use lock_api::{Mutex, RawMutex}; use std::borrow::Borrow; use std::cmp::Ordering; -use collections::{BTreeSet, VecDeque}; use std::fmt; use std::fmt::Debug; use std::marker::PhantomData; use std::ptr; -use lock_api::{Mutex, RawMutex}; use smallvec::SmallVec; @@ -446,8 +445,8 @@ impl SuperBlock { } } -impl LinCowCellCapable, CursorWrite> - for SuperBlock +impl + LinCowCellCapable, CursorWrite> for SuperBlock { fn create_reader(&self) -> CursorRead { CursorRead::new(self) @@ -1060,7 +1059,7 @@ pub(crate) struct CursorRead where K: Hash + Eq + Clone + Debug, V: Clone, - R: RawMutex + R: RawMutex, { txid: u64, length: usize, @@ -1097,7 +1096,9 @@ impl Drop for CursorRead CursorReadOps for CursorRead { +impl CursorReadOps + for CursorRead +{ fn get_root_ptr(&self) -> Ptr { self.root } @@ -1131,7 +1132,10 @@ mod tests { fn test_hashtrie_cursor_basic() { let sb: SuperBlock = unsafe { SuperBlock::new() }; - let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wr = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wr.len() == 0); assert!(wr.search(0, &0).is_none()); @@ -1153,7 +1157,10 @@ mod tests { fn test_hashtrie_cursor_insert_max_depth() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; let rdr: CursorRead = sb.create_reader(); - let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wr = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wr.len() == 0); for i in 0..(ABS_MAX_HEIGHT * 2) { @@ -1189,7 +1196,10 @@ mod tests { fn test_hashtrie_cursor_insert_broad() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; let rdr: CursorRead = sb.create_reader(); - let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wr = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wr.len() == 0); for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { @@ -1229,7 +1239,10 @@ mod tests { assert!(rdr.len() == 0); for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { - let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wr = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wr.insert(i, i, i).is_none()); wr.verify(); rdr = sb.pre_commit(wr, &rdr); @@ -1244,7 +1257,10 @@ mod tests { } for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { - let mut wr = as LinCowCellCapable, CursorWrite>>::create_writer(&sb); + let mut wr = as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&sb); assert!(wr.remove(i, &i).is_some()); wr.verify(); rdr = sb.pre_commit(wr, &rdr); diff --git a/src/internals/hashtrie/iter.rs b/src/internals/hashtrie/iter.rs index 50968ad..13b1347 100644 --- a/src/internals/hashtrie/iter.rs +++ b/src/internals/hashtrie/iter.rs @@ -1,9 +1,9 @@ //! Iterators for the hashtrie -#[cfg(feature = "std")] -use std::collections::VecDeque; #[cfg(not(feature = "std"))] use alloc::collections::VecDeque; +#[cfg(feature = "std")] +use std::collections::VecDeque; use super::cursor::{Ptr, HT_CAPACITY, MAX_HEIGHT}; use core::fmt::Debug; diff --git a/src/internals/lincowcell_async/mod.rs b/src/internals/lincowcell_async/mod.rs index 28e218e..264214d 100644 --- a/src/internals/lincowcell_async/mod.rs +++ b/src/internals/lincowcell_async/mod.rs @@ -69,7 +69,7 @@ pub struct LinCowCell { updater: PhantomData, write: Mutex, active: SyncMutex>>, - _phantom: PhantomData + _phantom: PhantomData, } #[derive(Debug)] @@ -79,7 +79,7 @@ pub struct LinCowCellWriteTxn<'a, T, R, U, M> { caller: &'a LinCowCell, guard: MutexGuard<'a, T>, work: U, - _phantom: PhantomData + _phantom: PhantomData, } #[derive(Debug)] @@ -140,7 +140,7 @@ where updater: PhantomData, write: Mutex::new(data), active: SyncMutex::new(Arc::new(LinCowCellInner::new(r))), - _phantom: PhantomData + _phantom: PhantomData, } } @@ -167,7 +167,7 @@ where caller: self, guard: write_guard, work, - _phantom: PhantomData + _phantom: PhantomData, } } @@ -183,7 +183,7 @@ where caller: self, guard: write_guard, work, - _phantom: PhantomData + _phantom: PhantomData, } }) .ok() @@ -196,7 +196,7 @@ where caller: _caller, mut guard, work, - _phantom: PhantomData + _phantom: PhantomData, } = write; // Get the previous generation. @@ -411,7 +411,8 @@ mod tests { let start = Instant::now(); // Create the new cowcell. let data = TestData { x: 0 }; - let cc: Arc> = Arc::new(LinCowCell::new(data)); + let cc: Arc> = + Arc::new(LinCowCell::new(data)); let _ = tokio::join!( tokio::task::spawn_blocking({ @@ -514,7 +515,9 @@ mod tests { async fn test_gc_operation() { GC_COUNT.store(0, Ordering::Release); let data = TestGcWrapper { data: 0 }; - let cc: Arc, TestGcWrapperReadTxn, TestGcWrapperWriteTxn>> = Arc::new(LinCowCell::new(data)); + let cc: Arc< + LinCowCell, TestGcWrapperReadTxn, TestGcWrapperWriteTxn>, + > = Arc::new(LinCowCell::new(data)); let _ = tokio::join!( tokio::task::spawn(test_gc_operation_thread(cc.clone())), @@ -618,7 +621,11 @@ mod tests_linear { GC_COUNT.store(0, Ordering::Release); assert!(GC_COUNT.load(Ordering::Acquire) == 0); let data = TestGcWrapper { data: 0 }; - let cc: LinCowCell, TestGcWrapperReadTxn, TestGcWrapperWriteTxn> = LinCowCell::new(data); + let cc: LinCowCell< + TestGcWrapper, + TestGcWrapperReadTxn, + TestGcWrapperWriteTxn, + > = LinCowCell::new(data); // Open a read A. let cc_rotxn_a = cc.read(); diff --git a/src/lc_tests.rs b/src/lc_tests.rs index dd54df0..8e701eb 100644 --- a/src/lc_tests.rs +++ b/src/lc_tests.rs @@ -28,8 +28,14 @@ impl LinCowCellCapable for TestStruct { fn create_writer(&self) -> TestStructWrite { // This sets up the first writer. TestStructWrite { - bptree_map_a: as LinCowCellCapable, CursorWrite>>::create_writer(&self.bptree_map_a), - bptree_map_b: as LinCowCellCapable, CursorWrite>>::create_writer(&self.bptree_map_b), + bptree_map_a: as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&self.bptree_map_a), + bptree_map_b: as LinCowCellCapable< + CursorRead, + CursorWrite, + >>::create_writer(&self.bptree_map_b), } } @@ -56,10 +62,11 @@ impl LinCowCellCapable for TestStruct { #[test] fn test_lc_basic() { - let lcc: LinCowCell = LinCowCell::new(TestStruct { - bptree_map_a: unsafe { SuperBlock::new() }, - bptree_map_b: unsafe { SuperBlock::new() }, - }); + let lcc: LinCowCell = + LinCowCell::new(TestStruct { + bptree_map_a: unsafe { SuperBlock::new() }, + bptree_map_b: unsafe { SuperBlock::new() }, + }); let x = lcc.write(); diff --git a/src/lib.rs b/src/lib.rs index 202cb23..2ad2463 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,19 +33,14 @@ //! By default all of these features are enabled. If you are planning to use this crate in a wasm //! context we recommend you use only `maps` as a feature. - //#![no_std] #![cfg_attr(not(feature = "std"), no_std)] - #![deny(warnings)] #![warn(unused_extern_crates)] #![warn(missing_docs)] #![allow(clippy::needless_lifetimes)] #![cfg_attr(feature = "simd_support", feature(portable_simd))] -// TODO - can I remove this? Need a backup to tell if we can use AtomicUsize -//#![feature(cfg_target_has_atomic)] - #[cfg(not(any(test, feature = "std")))] extern crate alloc; @@ -78,7 +73,7 @@ pub mod threadcache; pub mod internals; // This is where the good rust lives. -// We're allowing unuzed here since we may or may not use all items based on enabled features +// We're allowing unused here since we may or may not use all items based on enabled features // All potentially incompatible features must be feature gated internally. #[allow(unused)] mod utils; @@ -90,5 +85,5 @@ pub mod hashmap; #[cfg(feature = "maps")] pub mod hashtrie; -#[cfg(test)] +#[cfg(all(test, feature = "maps"))] mod lc_tests; diff --git a/src/utils.rs b/src/utils.rs index 9d279fa..de6f4b8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -117,10 +117,9 @@ where } } - /// This is intended for comparing the insertion times of items into the ArCache type. /// This would Default to an implementation over the Instant type on std, but could be an atomic counter with a caller-defined bit width in no_std environments. -/// +/// /// SAFETY: This has been marked unsafe as there is a behaviour contract on the `next` function that will not be checked by the caller. Subsequent calls to `next` should /// ALWAYS return an equal or greater value (based on the type's impl of PartialOrd) pub unsafe trait Monotonic { @@ -130,11 +129,9 @@ pub unsafe trait Monotonic { /// Gets the current value - provides an option for introspection where the value can change without calls to `next`, /// but they don't _have_ to changed without `next`. fn current(&self) -> Self::Output; - fn next(&self) -> Self::Output; + fn next(&self) -> Self::Output; } - - // provide default locking types #[cfg(feature = "std")] #[allow(unused)] @@ -148,4 +145,4 @@ pub type DefaultRawMutex = spin::mutex::SpinMutex<()>; pub type DefaultRawRwLock = parking_lot::RawRwLock; #[cfg(not(feature = "std"))] /// Provide a defaulkt raw mutex implementation for no_std environments via spinning -pub type DefaultRawRwLock = spin::RwLock<()>; \ No newline at end of file +pub type DefaultRawRwLock = spin::RwLock<()>; diff --git a/tests/bptree_map.rs b/tests/bptree_map.rs index d46fb15..1bb50bb 100644 --- a/tests/bptree_map.rs +++ b/tests/bptree_map.rs @@ -1,80 +1,83 @@ -use std::collections::{BTreeMap, BTreeSet}; -use std::ops::Bound; +#[cfg(feature = "maps")] +mod bptree_map_tests { -use concread::bptree::BptreeMap; + use concread::bptree::BptreeMap; + use std::collections::{BTreeMap, BTreeSet}; + use std::ops::Bound; -proptest::proptest! { - #[test] - fn bptree_range_iter_consistent(values: BTreeSet, left in 0..u8::MAX - 1, len in 1..u8::MAX, bounds: (Bound<()>, Bound<()>)) { - let range = (bounds.0.map(|()| left), bounds.1.map(|()| left.saturating_add(len))); - let btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v, ()))); - let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, ()))); - let bptree_map_read_tx = bptree_map.read(); - - let btree_iter = btree_map.range(range); - let bptree_iter = bptree_map_read_tx.range(range); - - assert!( - btree_iter.eq(bptree_iter) - ) - } - - #[test] - fn bptree_get_consistent(values: BTreeSet, key: u8) { - let btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v, v))); - let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, v))); - let bptree_map_read_tx = bptree_map.read(); - - let btree_value = btree_map.get(&key); - let bptree_value = bptree_map_read_tx.get(&key); + proptest::proptest! { + #[test] + fn bptree_range_iter_consistent(values: BTreeSet, left in 0..u8::MAX - 1, len in 1..u8::MAX, bounds: (Bound<()>, Bound<()>)) { + let range = (bounds.0.map(|()| left), bounds.1.map(|()| left.saturating_add(len))); + let btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v, ()))); + let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, ()))); + let bptree_map_read_tx = bptree_map.read(); - assert_eq!(btree_value, bptree_value); - } + let btree_iter = btree_map.range(range); + let bptree_iter = bptree_map_read_tx.range(range); - #[test] - fn bptree_remove_consistent(values in proptest::collection::btree_set(proptest::arbitrary::any::(), 1..256), indices: Vec ) { - let mut btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v.to_string(), v.to_string()))); - let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v.to_string(), v.to_string()))); - let mut bptree_map_write_tx = bptree_map.write(); - - for index in indices { - let index = index.index(values.len()); - let key = values.iter().nth(index).unwrap().to_string(); + assert!( + btree_iter.eq(bptree_iter) + ) + } - assert_eq!( - btree_map.remove(&key), - bptree_map_write_tx.remove(&key) - ); + #[test] + fn bptree_get_consistent(values: BTreeSet, key: u8) { + let btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v, v))); + let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v, v))); + let bptree_map_read_tx = bptree_map.read(); let btree_value = btree_map.get(&key); - assert_eq!(btree_value, None); - let bptree_value = bptree_map_write_tx.get(&key); - assert_eq!(bptree_value, None); + let bptree_value = bptree_map_read_tx.get(&key); - assert!( - btree_map.iter().eq(bptree_map_write_tx.iter()) - ); + assert_eq!(btree_value, bptree_value); } - } -} - -#[test] -fn bptree_remove_1() { - let values = [ - 4u8, 9, 12, 27, 34, 40, 59, 81, 89, 100, 142, 183, 189, 196, 218, 241, - ]; - let to_remove = [9u8, 27, 40, 4].map(|v| v.to_string()); + #[test] + fn bptree_remove_consistent(values in proptest::collection::btree_set(proptest::arbitrary::any::(), 1..256), indices: Vec ) { + let mut btree_map = BTreeMap::from_iter(values.iter().cloned().map(|v| (v.to_string(), v.to_string()))); + let bptree_map: BptreeMap = BptreeMap::from_iter(values.iter().cloned().map(|v| (v.to_string(), v.to_string()))); + let mut bptree_map_write_tx = bptree_map.write(); + + for index in indices { + let index = index.index(values.len()); + let key = values.iter().nth(index).unwrap().to_string(); + + assert_eq!( + btree_map.remove(&key), + bptree_map_write_tx.remove(&key) + ); + + let btree_value = btree_map.get(&key); + assert_eq!(btree_value, None); + let bptree_value = bptree_map_write_tx.get(&key); + assert_eq!(bptree_value, None); + + assert!( + btree_map.iter().eq(bptree_map_write_tx.iter()) + ); + } + } + } - let bptree_map: BptreeMap = BptreeMap::from_iter( - values - .iter() - .cloned() - .map(|v| (v.to_string(), v.to_string())), - ); - let mut bptree_map_write_tx = bptree_map.write(); + #[test] + fn bptree_remove_1() { + let values = [ + 4u8, 9, 12, 27, 34, 40, 59, 81, 89, 100, 142, 183, 189, 196, 218, 241, + ]; + + let to_remove = [9u8, 27, 40, 4].map(|v| v.to_string()); + + let bptree_map: BptreeMap = BptreeMap::from_iter( + values + .iter() + .cloned() + .map(|v| (v.to_string(), v.to_string())), + ); + let mut bptree_map_write_tx = bptree_map.write(); - for key in to_remove { - assert!(bptree_map_write_tx.remove(&key).is_some()); + for key in to_remove { + assert!(bptree_map_write_tx.remove(&key).is_some()); + } } } diff --git a/tests/lib.rs b/tests/lib.rs index c393578..8b13789 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1 +1 @@ -mod bptree_map; + From ee642f483362fdb883d666a9ca94c54f276f5c18 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Mon, 14 Jul 2025 10:52:38 +0930 Subject: [PATCH 06/10] Remove default implementations for non-`std`. If using in a `no_std` environment, consumers must provide their own sychronisation primitives. --- Cargo.toml | 4 +- benches/arccache.rs | 8 +- src/arcache/mod.rs | 38 ++++----- src/bptree/asynch.rs | 8 +- src/bptree/impl.rs | 27 ++++--- src/bptree/mod.rs | 10 +-- src/cowcell/mod.rs | 16 ++-- src/hashmap/asynch.rs | 12 +-- src/hashmap/impl.rs | 20 +++-- src/hashmap/mod.rs | 8 +- src/hashtrie/asynch.rs | 12 +-- src/hashtrie/impl.rs | 20 +++-- src/hashtrie/mod.rs | 8 +- src/internals/bptree/cursor.rs | 106 +++++++++++++------------- src/internals/bptree/mutiter.rs | 2 +- src/internals/hashmap/cursor.rs | 92 +++++++++++----------- src/internals/hashtrie/cursor.rs | 24 +++--- src/internals/lincowcell/mod.rs | 17 +++-- src/internals/lincowcell_async/mod.rs | 36 ++++----- src/lc_tests.rs | 6 +- src/lib.rs | 2 +- src/utils.rs | 17 +---- 22 files changed, 252 insertions(+), 241 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 24a79c5..60e3bb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ ebr = ["std"] maps = ["dep:crossbeam-utils", "smallvec"] tcache = [] std = ["ahash/std", "ahash/runtime-rng", "crossbeam-epoch/std", "crossbeam-queue/std", "crossbeam-utils/std", "tracing/std", "dep:parking_lot", "smallvec/write"] -no_std = ["crossbeam-epoch/alloc", "crossbeam-queue/alloc", "serde/alloc", "dep:spin", "ahash"] +no_std = ["crossbeam-epoch/alloc", "crossbeam-queue/alloc", "serde/alloc", "ahash"] # Internal features for tweaking some align/perf behaviours. @@ -62,7 +62,6 @@ tracing = {version = "0.1", default-features = false} lock_api = "0.4" parking_lot = {version = "0.12.3", optional = true } hashbrown = {version = "0.15.2", default-features = false} -spin = {version = "0.10.0", optional = true, default-features = false, features = ["lock_api", "spin_mutex", "rwlock"]} cfg-if = "1.0.0" [dev-dependencies] @@ -78,6 +77,7 @@ function_name = "0.3" serde_json = "1" tokio = { version = "1", features = ["rt", "macros"] } proptest = "1.0.0" +spin = {version = "0.10.0", default-features = false, features = ["lock_api", "spin_mutex", "rwlock"]} [[bench]] name = "hashmap_benchmark" diff --git a/benches/arccache.rs b/benches/arccache.rs index 642daa9..8e4ab6d 100644 --- a/benches/arccache.rs +++ b/benches/arccache.rs @@ -11,7 +11,7 @@ use std::thread; use std::time::{Duration, Instant}; // use uuid::Uuid; -use concread::arcache::{ARCache, ARCacheBuilder}; +use concread::arcache::{ARCacheRaw, ARCacheBuilder}; use concread::threadcache::ThreadLocal; use criterion::measurement::{Measurement, ValueFormatter}; @@ -261,7 +261,7 @@ where } fn multi_thread_worker( - arc: Arc>, + arc: Arc>, backing_set: Arc>, backing_set_delay: Option, access_pattern: AccessPattern, @@ -311,7 +311,7 @@ where csize = 1; } - let arc: Arc> = Arc::new( + let arc: Arc> = Arc::new( ARCacheBuilder::new() .set_size(csize, 0) .set_watermark(0) @@ -420,7 +420,7 @@ where csize = 1; } - let arc: ARCache = ARCacheBuilder::new() + let arc: ARCacheRaw = ARCacheBuilder::new() .set_size(csize, 0) .set_watermark(0) .set_reader_quiesce(false) diff --git a/src/arcache/mod.rs b/src/arcache/mod.rs index 17a893e..a3a27e5 100644 --- a/src/arcache/mod.rs +++ b/src/arcache/mod.rs @@ -24,12 +24,12 @@ use self::stats::{ARCacheReadStat, ARCacheWriteStat}; #[cfg(feature = "arcache-is-hashmap")] use crate::hashmap::{ - HashMap as DataMap, HashMapReadTxn as DataMapReadTxn, HashMapWriteTxn as DataMapWriteTxn, + HashMapRaw as DataMap, HashMapReadTxn as DataMapReadTxn, HashMapWriteTxn as DataMapWriteTxn, }; #[cfg(feature = "arcache-is-hashtrie")] use crate::hashtrie::{ - HashTrie as DataMap, HashTrieReadTxn as DataMapReadTxn, HashTrieWriteTxn as DataMapWriteTxn, + HashTrieRaw as DataMap, HashTrieReadTxn as DataMapReadTxn, HashTrieWriteTxn as DataMapWriteTxn, }; use crate::utils::{self, Monotonic}; @@ -246,18 +246,22 @@ where reader_quiesce: bool, } +/// ARCache structure with default sychronisation primitives for write transaction locking. +#[cfg(feature = "std")] +pub type ARCache = ARCacheRaw; + /// A concurrently readable adaptive replacement cache. Operations are performed on the /// cache via read and write operations. -pub struct ARCache< +pub struct ARCacheRaw< K, V, - M = monotonic_timer::MonotonicTimer, - RawMutexImpl = utils::DefaultRawMutex, - RawRwLockImpl = utils::DefaultRawRwLock, + MonotonicCounter, + RawMutexImpl, + RawRwLockImpl, > where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, - M: Monotonic + 'static, + MonotonicCounter: Monotonic + 'static, RawMutexImpl: RawMutex + 'static, RawRwLockImpl: RawRwLock + 'static, { @@ -266,13 +270,13 @@ pub struct ARCache< cache: DataMap, RawMutexImpl>, // This is normally only ever taken in "read" mode, so it's effectively // an uncontended barrier. - shared: RwLock>, + shared: RwLock>, // These are only taken during a quiesce - inner: Mutex>, + inner: Mutex>, // stats: CowCell, above_watermark: AtomicBool, look_back_limit: u64, - monotonic: M, + monotonic: MonotonicCounter, } unsafe impl< @@ -281,7 +285,7 @@ unsafe impl< M: Monotonic + Send + Send + 'static, Mutex: RawMutex + Sync + Send + 'static, RwLock: RawRwLock + Sync + Send + 'static, - > Send for ARCache + > Send for ARCacheRaw { } unsafe impl< @@ -290,7 +294,7 @@ unsafe impl< M: Monotonic + Send + 'static, Mutex: RawMutex + Sync + 'static, RwLock: RawRwLock + Sync + 'static, - > Sync for ARCache + > Sync for ARCacheRaw { } @@ -340,7 +344,7 @@ where Mutex: RawMutex + 'static, RwLock: RawRwLock + 'static, { - caller: &'a ARCache, + caller: &'a ARCacheRaw, // ro_txn to cache cache: DataMapReadTxn<'a, K, CacheItem, Mutex>, tlocal: Option>, @@ -385,7 +389,7 @@ where Mutex: RawMutex + 'static, RwLock: RawRwLock + 'static, { - caller: &'a ARCache, + caller: &'a ARCacheRaw, // wr_txn to cache cache: DataMapWriteTxn<'a, K, CacheItem, Mutex>, // Cache of missed items (w_ dirty/clean) @@ -576,7 +580,7 @@ where /// missing or incorrect, a None will be returned. pub fn build( self, - ) -> Option> + ) -> Option> where K: Hash + Eq + Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Debug + Sync + Send + 'static, @@ -643,7 +647,7 @@ where min_txid: 0, }); - Some(ARCache { + Some(ARCacheRaw { cache: DataMap::new(), shared, inner, @@ -661,7 +665,7 @@ impl< M: Monotonic + 'static, Mutex: RawMutex + 'static, RwLock: RawRwLock + 'static, - > ARCache + > ARCacheRaw { /// Use ARCacheBuilder instead #[deprecated(since = "0.2.20", note = "please use`ARCacheBuilder` instead")] diff --git a/src/bptree/asynch.rs b/src/bptree/asynch.rs index 6d8e83e..e3c9adc 100644 --- a/src/bptree/asynch.rs +++ b/src/bptree/asynch.rs @@ -9,7 +9,7 @@ use serde::{ #[cfg(feature = "serde")] use crate::utils::MapCollector; -use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCellWriteTxn}; +use crate::internals::lincowcell_async::{LinCowCellRaw, LinCowCellReadTxn, LinCowCellWriteTxn}; include!("impl.rs"); @@ -17,7 +17,7 @@ impl< K: Clone + Ord + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, R: RawMutex + 'static, - > BptreeMap + > BptreeMapRaw { /// Initiate a read transaction for the tree, concurrent to any /// other readers or writers. @@ -70,7 +70,7 @@ where } #[cfg(feature = "serde")] -impl Serialize for BptreeMap +impl Serialize for BptreeMapRaw where K: Serialize + Clone + Ord + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, @@ -84,7 +84,7 @@ where } #[cfg(feature = "serde")] -impl<'de, K, V> Deserialize<'de> for BptreeMap +impl<'de, K, V> Deserialize<'de> for BptreeMapRaw where K: Deserialize<'de> + Clone + Ord + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, diff --git a/src/bptree/impl.rs b/src/bptree/impl.rs index 377db4f..f2ea1c0 100644 --- a/src/bptree/impl.rs +++ b/src/bptree/impl.rs @@ -10,6 +10,11 @@ use std::fmt::Debug; use std::iter::FromIterator; use std::ops::RangeBounds; + +/// B+Tree structure with a default mutex type for write transaction locking. +#[cfg(feature = "std")] +pub type BptreeMap = BptreeMapRaw; + /// A concurrently readable map based on a modified B+Tree structure. /// /// This structure can be used in locations where you would otherwise us @@ -31,21 +36,21 @@ use std::ops::RangeBounds; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `BptreeMapWriteTxn` without calling `commit()`. -pub struct BptreeMap +pub struct BptreeMapRaw where K: Ord + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static { - inner: LinCowCell, CursorRead, CursorWrite, M>, + inner: LinCowCellRaw, CursorRead, CursorWrite, M>, } unsafe impl Send - for BptreeMap + for BptreeMapRaw { } unsafe impl Sync - for BptreeMap + for BptreeMapRaw { } @@ -112,7 +117,7 @@ where } impl Default - for BptreeMap + for BptreeMapRaw { fn default() -> Self { Self::new() @@ -120,13 +125,13 @@ impl - BptreeMap + BptreeMapRaw { /// Construct a new concurrent tree pub fn new() -> Self { // I acknowledge I understand what is required to make this safe. - BptreeMap { - inner: LinCowCell::new(unsafe { SuperBlock::new() }), + BptreeMapRaw { + inner: LinCowCellRaw::new(unsafe { SuperBlock::new() }), } } @@ -140,7 +145,7 @@ impl - FromIterator<(K, V)> for BptreeMap + FromIterator<(K, V)> for BptreeMapRaw { fn from_iter>(iter: I) -> Self { let mut new_sblock = unsafe { SuperBlock::new() }; @@ -154,8 +159,8 @@ impl BptreeMap + > BptreeMapRaw { /// Initiate a read transaction for the tree, concurrent to any /// other readers or writers. @@ -352,7 +352,7 @@ mod tests { fn test_bptree2_map_rangeiter_1() { let ins: Vec = (0..100).collect(); - let map: BptreeMap = + let map: BptreeMap = BptreeMap::from_iter(ins.into_iter().map(|v| (v, v))); { @@ -369,7 +369,7 @@ mod tests { #[test] fn test_bptree2_map_rangeiter_2() { - let map: BptreeMap = + let map: BptreeMap = BptreeMap::from_iter([(3, ()), (4, ()), (0, ())]); let r = map.read(); @@ -378,7 +378,7 @@ mod tests { #[test] fn test_bptree2_map_rangeiter_3() { - let map: BptreeMap = + let map: BptreeMap = BptreeMap::from_iter([0, 1, 2, 3, 4, 5, 6, 8].map(|v| (v, ()))); let r = map.read(); diff --git a/src/cowcell/mod.rs b/src/cowcell/mod.rs index 812ad4b..77bd650 100644 --- a/src/cowcell/mod.rs +++ b/src/cowcell/mod.rs @@ -21,7 +21,11 @@ use ::alloc::sync::Arc; #[cfg(feature = "std")] use std::sync::Arc; -/// A conncurrently readable cell. +/// CowCell with a default lock type provided. +#[cfg(feature = "std")] +pub type CowCell = CowCellRaw; + +/// A concurrently readable cell. /// /// This structure behaves in a similar manner to a `RwLock`. However unlike /// a `RwLock`, writes and parallel reads can be performed at the same time. This @@ -63,12 +67,12 @@ use std::sync::Arc; /// assert_eq!(*new_read_txn, 1); /// ``` #[derive(Debug)] -pub struct CowCell { +pub struct CowCellRaw { write: Mutex, active: Mutex>, } -impl Default for CowCell { +impl Default for CowCellRaw { fn default() -> Self { Self { write: Mutex::new(()), @@ -91,7 +95,7 @@ pub struct CowCellWriteTxn<'a, T, R: RawMutex> { work: Option, read: Arc, // This way we know who to contact for updating our data .... - caller: &'a CowCell, + caller: &'a CowCellRaw, _guard: MutexGuard<'a, R, ()>, } @@ -108,7 +112,7 @@ impl Clone for CowCellReadTxn { } } -impl CowCell +impl CowCellRaw where T: Clone, R: RawMutex, @@ -116,7 +120,7 @@ where /// Create a new `CowCell` for storing type `T`. `T` must implement `Clone` /// to enable clone-on-write. pub fn new(data: T) -> Self { - CowCell { + CowCellRaw { write: Mutex::new(()), active: Mutex::new(Arc::new(data)), } diff --git a/src/hashmap/asynch.rs b/src/hashmap/asynch.rs index 95a53cb..85afe73 100644 --- a/src/hashmap/asynch.rs +++ b/src/hashmap/asynch.rs @@ -13,7 +13,7 @@ use serde::{ #[cfg(feature = "serde")] use crate::utils::MapCollector; -use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCellWriteTxn}; +use crate::internals::lincowcell_async::{LinCowCellRaw, LinCowCellReadTxn, LinCowCellWriteTxn}; include!("impl.rs"); @@ -21,13 +21,13 @@ impl< K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static, - > HashMap + > HashMapRaw { /// Construct a new concurrent hashmap pub fn new() -> Self { // I acknowledge I understand what is required to make this safe. - HashMap { - inner: LinCowCell::new(unsafe { SuperBlock::new() }), + HashMapRaw { + inner: LinCowCellRaw::new(unsafe { SuperBlock::new() }), } } @@ -90,7 +90,7 @@ where } #[cfg(feature = "serde")] -impl Serialize for HashMap +impl Serialize for HashMapRaw where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, @@ -104,7 +104,7 @@ where } #[cfg(feature = "serde")] -impl<'de, K, V> Deserialize<'de> for HashMap +impl<'de, K, V> Deserialize<'de> for HashMapRaw where K: Deserialize<'de> + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, diff --git a/src/hashmap/impl.rs b/src/hashmap/impl.rs index 39725e6..114b71e 100644 --- a/src/hashmap/impl.rs +++ b/src/hashmap/impl.rs @@ -11,6 +11,10 @@ use std::fmt::Debug; use std::hash::Hash; use std::iter::FromIterator; +/// B+Tree-based map with a default mutex type provided. +#[cfg(feature = "std")] +pub type HashMap = HashMapRaw; + /// A concurrently readable map based on a modified B+Tree structured with fast /// parallel hashed key lookup. /// @@ -29,27 +33,27 @@ use std::iter::FromIterator; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `HashMapWriteTxn` without calling `commit()`. -pub struct HashMap +pub struct HashMapRaw where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static, { - inner: LinCowCell, CursorRead, CursorWrite, M>, + inner: LinCowCellRaw, CursorRead, CursorWrite, M>, } unsafe impl< K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + Send + 'static, - > Send for HashMap + > Send for HashMapRaw { } unsafe impl< K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + Send + Sync + 'static, - > Sync for HashMap + > Sync for HashMapRaw { } @@ -110,7 +114,7 @@ impl< K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static, - > Default for HashMap + > Default for HashMapRaw { fn default() -> Self { Self::new() @@ -121,7 +125,7 @@ impl< K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static, - > FromIterator<(K, V)> for HashMap + > FromIterator<(K, V)> for HashMapRaw { fn from_iter>(iter: I) -> Self { let mut new_sblock = unsafe { SuperBlock::new() }; @@ -134,8 +138,8 @@ impl< let _ = new_sblock.pre_commit(cursor, &prev); - HashMap { - inner: LinCowCell::new(new_sblock), + HashMapRaw { + inner: LinCowCellRaw::new(new_sblock), } } } diff --git a/src/hashmap/mod.rs b/src/hashmap/mod.rs index 533242f..a3dec7f 100644 --- a/src/hashmap/mod.rs +++ b/src/hashmap/mod.rs @@ -34,7 +34,7 @@ use crate::utils::MapCollector; #[cfg(all(feature = "arcache", feature = "arcache-is-hashmap"))] use crate::internals::hashmap::cursor::Datum; -use crate::internals::lincowcell::{LinCowCell, LinCowCellReadTxn, LinCowCellWriteTxn}; +use crate::internals::lincowcell::{LinCowCellRaw, LinCowCellReadTxn, LinCowCellWriteTxn}; include!("impl.rs"); @@ -42,13 +42,13 @@ impl< K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static, - > HashMap + > HashMapRaw { /// Construct a new concurrent hashmap pub fn new() -> Self { // I acknowledge I understand what is required to make this safe. - HashMap { - inner: LinCowCell::new(unsafe { SuperBlock::new() }), + HashMapRaw { + inner: LinCowCellRaw::new(unsafe { SuperBlock::new() }), } } diff --git a/src/hashtrie/asynch.rs b/src/hashtrie/asynch.rs index d80b170..fab36f6 100644 --- a/src/hashtrie/asynch.rs +++ b/src/hashtrie/asynch.rs @@ -13,7 +13,7 @@ use serde::{ #[cfg(feature = "serde")] use crate::utils::MapCollector; -use crate::internals::lincowcell_async::{LinCowCell, LinCowCellReadTxn, LinCowCellWriteTxn}; +use crate::internals::lincowcell_async::{LinCowCellRaw, LinCowCellReadTxn, LinCowCellWriteTxn}; include!("impl.rs"); @@ -21,13 +21,13 @@ impl< K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static, - > HashTrie + > HashTrieRaw { /// Construct a new concurrent hashtrie pub fn new() -> Self { // I acknowledge I understand what is required to make this safe. - HashTrie { - inner: LinCowCell::new(unsafe { SuperBlock::new() }), + HashTrieRaw { + inner: LinCowCellRaw::new(unsafe { SuperBlock::new() }), } } @@ -90,7 +90,7 @@ where } #[cfg(feature = "serde")] -impl Serialize for HashTrie +impl Serialize for HashTrieRaw where K: Serialize + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Serialize + Clone + Sync + Send + 'static, @@ -104,7 +104,7 @@ where } #[cfg(feature = "serde")] -impl<'de, K, V> Deserialize<'de> for HashTrie +impl<'de, K, V> Deserialize<'de> for HashTrieRaw where K: Deserialize<'de> + Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Deserialize<'de> + Clone + Sync + Send + 'static, diff --git a/src/hashtrie/impl.rs b/src/hashtrie/impl.rs index 84aec6a..c7d9690 100644 --- a/src/hashtrie/impl.rs +++ b/src/hashtrie/impl.rs @@ -11,6 +11,10 @@ use std::fmt::Debug; use std::hash::Hash; use std::iter::FromIterator; +/// HashTrie with a default lock type provided. +#[cfg(feature = "std")] +pub type HashTrie = HashTrieRaw; + /// A concurrently readable map based on a modified Trie. /// /// @@ -27,21 +31,21 @@ use std::iter::FromIterator; /// /// Transactions can be rolled-back (aborted) without penalty by dropping /// the `HashTrieWriteTxn` without calling `commit()`. -pub struct HashTrie +pub struct HashTrieRaw where K: Hash + Eq + Clone + Debug + Sync + Send + 'static, V: Clone + Sync + Send + 'static, M: RawMutex + 'static { - inner: LinCowCell, CursorRead, CursorWrite, M>, + inner: LinCowCellRaw, CursorRead, CursorWrite, M>, } unsafe impl - Send for HashTrie + Send for HashTrieRaw { } unsafe impl - Sync for HashTrie + Sync for HashTrieRaw { } @@ -99,7 +103,7 @@ where } impl Default - for HashTrie + for HashTrieRaw { fn default() -> Self { Self::new() @@ -107,7 +111,7 @@ impl - FromIterator<(K, V)> for HashTrie + FromIterator<(K, V)> for HashTrieRaw { fn from_iter>(iter: I) -> Self { let mut new_sblock = unsafe { SuperBlock::new() }; @@ -120,8 +124,8 @@ impl HashTrie + > HashTrieRaw { /// Construct a new concurrent hashtrie pub fn new() -> Self { // I acknowledge I understand what is required to make this safe. - HashTrie { - inner: LinCowCell::, CursorRead, CursorWrite, M>::new( + HashTrieRaw { + inner: LinCowCellRaw::, CursorRead, CursorWrite, M>::new( unsafe { SuperBlock::new() }, ), } diff --git a/src/internals/bptree/cursor.rs b/src/internals/bptree/cursor.rs index f1b3821..eb61e9f 100644 --- a/src/internals/bptree/cursor.rs +++ b/src/internals/bptree/cursor.rs @@ -136,7 +136,7 @@ impl SuperBlock { } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Ord + Clone + Debug, V: Clone, @@ -1350,7 +1350,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1394,7 +1394,7 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); let prev_txid = wcurs.root_txid(); @@ -1419,7 +1419,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1450,7 +1450,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1483,7 +1483,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1514,7 +1514,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1551,7 +1551,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1585,7 +1585,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1623,7 +1623,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1651,7 +1651,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1675,7 +1675,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1703,7 +1703,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1726,11 +1726,11 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let mut sb = unsafe { SuperBlock::new() }; - let mut rdr: CursorRead = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in 1..(L_CAPACITY << 4) { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -1751,11 +1751,11 @@ mod tests { fn test_bptree2_cursor_insert_stress_5() { // Insert descending let mut sb = unsafe { SuperBlock::new() }; - let mut rdr: CursorRead = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in (1..(L_CAPACITY << 4)).rev() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -1779,11 +1779,11 @@ mod tests { ins.shuffle(&mut rng); let mut sb = unsafe { SuperBlock::new() }; - let mut rdr: CursorRead = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in ins.into_iter() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); let r = wcurs.insert(v, v); @@ -1803,7 +1803,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1830,7 +1830,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1853,7 +1853,7 @@ mod tests { let lnode = create_leaf_node_full(0); let sb = SuperBlock::new_test(1, lnode); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("{:?}", wcurs); @@ -1879,7 +1879,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1908,7 +1908,7 @@ mod tests { unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("{:?}", wcurs); @@ -1938,7 +1938,7 @@ mod tests { unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1967,7 +1967,7 @@ mod tests { unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2000,7 +2000,7 @@ mod tests { unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2033,7 +2033,7 @@ mod tests { unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2075,7 +2075,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2116,7 +2116,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2156,7 +2156,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2196,7 +2196,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2236,7 +2236,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2280,7 +2280,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2323,7 +2323,7 @@ mod tests { // let count = BV_CAPACITY + 2; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2366,7 +2366,7 @@ mod tests { let root = Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2393,7 +2393,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2414,7 +2414,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2427,7 +2427,7 @@ mod tests { assert_released(); } - fn tree_create_rand() -> (SuperBlock, CursorRead) { + fn tree_create_rand() -> (SuperBlock, CursorRead) { let mut rng = rand::rng(); let mut ins: Vec = (1..(L_CAPACITY << 4)).collect(); ins.shuffle(&mut rng); @@ -2435,7 +2435,7 @@ mod tests { let mut sb = unsafe { SuperBlock::new() }; let rdr = sb.create_reader(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2454,7 +2454,7 @@ mod tests { // so we do this to a reasonable number. let (mut sb, rdr) = tree_create_rand(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2478,7 +2478,7 @@ mod tests { // Insert descending let (mut sb, rdr) = tree_create_rand(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2504,7 +2504,7 @@ mod tests { let (mut sb, rdr) = tree_create_rand(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2529,7 +2529,7 @@ mod tests { for v in 1..(L_CAPACITY << 4) { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -2550,7 +2550,7 @@ mod tests { for v in (1..(L_CAPACITY << 4)).rev() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -2575,7 +2575,7 @@ mod tests { for v in ins.into_iter() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); let r = wcurs.remove(&v); @@ -2680,7 +2680,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2701,7 +2701,7 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2722,7 +2722,7 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2744,7 +2744,7 @@ mod tests { let sb = SuperBlock::new_test(1, tree); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // 0 is min, and not present, will cause no change. @@ -2778,7 +2778,7 @@ mod tests { let sb = SuperBlock::new_test(1, tree); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // 0 is min, and not present, will cause no change. @@ -2837,7 +2837,7 @@ mod tests { let node: *mut Leaf = Node::new_leaf(0) as *mut _; let sb = SuperBlock::new_test(1, node as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); wcurs.extend(data.iter().map(|v| (*v, *v))); @@ -2863,7 +2863,7 @@ mod tests { let node: *mut Leaf = Node::new_leaf(0) as *mut _; let sb = SuperBlock::new_test(1, node as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); diff --git a/src/internals/bptree/mutiter.rs b/src/internals/bptree/mutiter.rs index 87bb313..10486b7 100644 --- a/src/internals/bptree/mutiter.rs +++ b/src/internals/bptree/mutiter.rs @@ -105,7 +105,7 @@ mod tests { let sb = SuperBlock::new_test(1, node as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); diff --git a/src/internals/hashmap/cursor.rs b/src/internals/hashmap/cursor.rs index d943147..3f29c5c 100644 --- a/src/internals/hashmap/cursor.rs +++ b/src/internals/hashmap/cursor.rs @@ -145,7 +145,7 @@ impl SuperBlock { } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Hash + Eq + Clone + Debug, V: Clone, @@ -1163,7 +1163,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); let prev_txid = wcurs.root_txid(); @@ -1202,7 +1202,7 @@ mod tests { let node = create_leaf_node_full(10); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); let prev_txid = wcurs.root_txid(); @@ -1227,7 +1227,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1258,7 +1258,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1291,7 +1291,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1322,7 +1322,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1359,7 +1359,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1393,7 +1393,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1431,7 +1431,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1459,7 +1459,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1483,7 +1483,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1511,7 +1511,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1534,11 +1534,11 @@ mod tests { // Insert ascending - we want to ensure the tree is a few levels deep // so we do this to a reasonable number. let mut sb = unsafe { SuperBlock::new() }; - let mut rdr: CursorRead = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in 1..(H_CAPACITY << 4) { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -1558,11 +1558,11 @@ mod tests { fn test_hashmap2_cursor_insert_stress_5() { // Insert descending let mut sb = unsafe { SuperBlock::new() }; - let mut rdr: CursorRead = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in (1..(H_CAPACITY << 4)).rev() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -1586,11 +1586,11 @@ mod tests { ins.shuffle(&mut rng); let mut sb = unsafe { SuperBlock::new() }; - let mut rdr: CursorRead = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); for v in ins.into_iter() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); let r = wcurs.insert(v as u64, v, v); @@ -1610,7 +1610,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1637,7 +1637,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1663,7 +1663,7 @@ mod tests { let lnode = create_leaf_node_full(0); let sb = SuperBlock::new_test(1, lnode); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("{:?}", wcurs); @@ -1689,7 +1689,7 @@ mod tests { let node = create_leaf_node(0); let sb = SuperBlock::new_test(1, node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1718,7 +1718,7 @@ mod tests { unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); println!("{:?}", wcurs); @@ -1747,7 +1747,7 @@ mod tests { unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1776,7 +1776,7 @@ mod tests { unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1809,7 +1809,7 @@ mod tests { unsafe { (*root).add_node(rnode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1842,7 +1842,7 @@ mod tests { unsafe { (*root).add_node(znode) }; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1885,7 +1885,7 @@ mod tests { let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1926,7 +1926,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -1966,7 +1966,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2006,7 +2006,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2046,7 +2046,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2090,7 +2090,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _); let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2134,7 +2134,7 @@ mod tests { // let count = HBV_CAPACITY + 2; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2178,7 +2178,7 @@ mod tests { Node::new_branch(0, lbranch as *mut _, rbranch as *mut _) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2205,7 +2205,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2226,7 +2226,7 @@ mod tests { let root = Node::new_branch(0, lnode, rnode) as *mut Node; let sb = SuperBlock::new_test(1, root as *mut Node); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wcurs.verify()); @@ -2239,7 +2239,7 @@ mod tests { assert_released(); } - fn tree_create_rand() -> (SuperBlock, CursorRead) { + fn tree_create_rand() -> (SuperBlock, CursorRead) { let mut rng = rand::rng(); let mut ins: Vec = (1..(H_CAPACITY << 4)).collect(); ins.shuffle(&mut rng); @@ -2247,7 +2247,7 @@ mod tests { let mut sb = unsafe { SuperBlock::new() }; let rdr = sb.create_reader(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2267,7 +2267,7 @@ mod tests { // so we do this to a reasonable number. let (mut sb, rdr) = tree_create_rand(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2291,7 +2291,7 @@ mod tests { // Insert descending let (mut sb, rdr) = tree_create_rand(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2320,7 +2320,7 @@ mod tests { let (mut sb, rdr) = tree_create_rand(); let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -2348,7 +2348,7 @@ mod tests { for v in 1..(H_CAPACITY << 4) { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -2371,7 +2371,7 @@ mod tests { for v in (1..(H_CAPACITY << 4)).rev() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); // println!("ITER v {}", v); @@ -2399,7 +2399,7 @@ mod tests { for v in ins.into_iter() { let mut wcurs = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); let r = wcurs.remove(v as u64, &v); diff --git a/src/internals/hashtrie/cursor.rs b/src/internals/hashtrie/cursor.rs index 9836f92..00f8e38 100644 --- a/src/internals/hashtrie/cursor.rs +++ b/src/internals/hashtrie/cursor.rs @@ -86,10 +86,10 @@ macro_rules! hash_key { } #[cfg(all(test, not(miri)))] -thread_local!(static ALLOC_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); +thread_local!(static ALLOC_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); #[cfg(all(test, not(miri)))] -thread_local!(static WRITE_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); +thread_local!(static WRITE_LIST: Mutex> = const { Mutex::new(BTreeSet::new()) }); #[cfg(test)] fn assert_released() { @@ -1055,7 +1055,7 @@ impl CursorReadOps for CursorWrite } #[derive(Debug)] -pub(crate) struct CursorRead +pub(crate) struct CursorRead where K: Hash + Eq + Clone + Debug, V: Clone, @@ -1133,7 +1133,7 @@ mod tests { let sb: SuperBlock = unsafe { SuperBlock::new() }; let mut wr = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1156,9 +1156,9 @@ mod tests { #[test] fn test_hashtrie_cursor_insert_max_depth() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; - let rdr: CursorRead = sb.create_reader(); + let rdr: CursorRead = sb.create_reader(); let mut wr = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1195,9 +1195,9 @@ mod tests { #[test] fn test_hashtrie_cursor_insert_broad() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; - let rdr: CursorRead = sb.create_reader(); + let rdr: CursorRead = sb.create_reader(); let mut wr = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); @@ -1233,14 +1233,14 @@ mod tests { #[test] fn test_hashtrie_cursor_insert_multiple_txns() { let mut sb: SuperBlock = unsafe { SuperBlock::new() }; - let mut rdr: CursorRead = sb.create_reader(); + let mut rdr: CursorRead = sb.create_reader(); // Do thing assert!(rdr.len() == 0); for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { let mut wr = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wr.insert(i, i, i).is_none()); @@ -1249,7 +1249,7 @@ mod tests { } { - let rdr2: CursorRead = sb.create_reader(); + let rdr2: CursorRead = sb.create_reader(); assert!(rdr2.len() == (ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) as usize); for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { assert!(rdr2.search(i, &i).is_some()); @@ -1258,7 +1258,7 @@ mod tests { for i in 0..(ABS_MAX_HEIGHT * ABS_MAX_HEIGHT) { let mut wr = as LinCowCellCapable< - CursorRead, + CursorRead, CursorWrite, >>::create_writer(&sb); assert!(wr.remove(i, &i).is_some()); diff --git a/src/internals/lincowcell/mod.rs b/src/internals/lincowcell/mod.rs index 70df5c4..5a3459c 100644 --- a/src/internals/lincowcell/mod.rs +++ b/src/internals/lincowcell/mod.rs @@ -67,6 +67,11 @@ use core::ops::DerefMut; use lock_api::RawMutex; use lock_api::{Mutex, MutexGuard}; + +/// Linear Copy-on-write cell with default Mutex type provided +#[cfg(feature = "std")] +pub type LinCowCell = LinCowCellRaw; + /// Do not implement this. You don't need this negativity in your life. pub trait LinCowCellCapable { /// Create the first reader snapshot for a new instance. @@ -82,13 +87,13 @@ pub trait LinCowCellCapable { } /// A concurrently readable cell with linearised drop behaviour. -pub struct LinCowCell { +pub struct LinCowCellRaw { updater: PhantomData, write: Mutex, active: Mutex>>, } -impl Debug for LinCowCell { +impl Debug for LinCowCellRaw { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut f = f.debug_struct("LinCowCell"); match self.write.try_lock() { @@ -188,7 +193,7 @@ impl Drop for LinCowCellInner { /// A read txn over a linear cell. pub struct LinCowCellReadTxn<'a, T, R, U, M: RawMutex> { // We must outlive the root - _caller: &'a LinCowCell, + _caller: &'a LinCowCellRaw, // We pin the current version. work: Arc>, } @@ -204,7 +209,7 @@ impl Debug for LinCowCellReadTxn<'_, T, R, U /// A write txn over a linear cell. pub struct LinCowCellWriteTxn<'a, T, R, U, M: RawMutex> { // This way we know who to contact for updating our data .... - caller: &'a LinCowCell, + caller: &'a LinCowCellRaw, guard: MutexGuard<'a, M, T>, work: U, } @@ -219,7 +224,7 @@ impl Debug for LinCowCellWriteTxn<'_, } } -impl LinCowCell +impl LinCowCellRaw where T: LinCowCellCapable, M: RawMutex, @@ -227,7 +232,7 @@ where /// Create a new linear 🐄 cell. pub fn new(data: T) -> Self { let r = data.create_reader(); - LinCowCell { + LinCowCellRaw { updater: PhantomData, write: Mutex::new(data), active: Mutex::new(Arc::new(LinCowCellInner::new(r))), diff --git a/src/internals/lincowcell_async/mod.rs b/src/internals/lincowcell_async/mod.rs index 264214d..7d79a45 100644 --- a/src/internals/lincowcell_async/mod.rs +++ b/src/internals/lincowcell_async/mod.rs @@ -65,7 +65,7 @@ use crate::internals::lincowcell::LinCowCellCapable; #[derive(Debug)] /// A concurrently readable cell with linearised drop behaviour. -pub struct LinCowCell { +pub struct LinCowCellRaw { updater: PhantomData, write: Mutex, active: SyncMutex>>, @@ -76,7 +76,7 @@ pub struct LinCowCell { /// A write txn over a linear cell. pub struct LinCowCellWriteTxn<'a, T, R, U, M> { // This way we know who to contact for updating our data .... - caller: &'a LinCowCell, + caller: &'a LinCowCellRaw, guard: MutexGuard<'a, T>, work: U, _phantom: PhantomData, @@ -93,7 +93,7 @@ struct LinCowCellInner { /// A read txn over a linear cell. pub struct LinCowCellReadTxn<'a, T, R, U, M> { // We must outlive the root - _caller: &'a LinCowCell, + _caller: &'a LinCowCellRaw, // We pin the current version. work: Arc>, } @@ -129,14 +129,14 @@ impl Drop for LinCowCellInner { } } -impl LinCowCell +impl LinCowCellRaw where T: LinCowCellCapable, { /// Create a new linear 🐄 cell. pub fn new(data: T) -> Self { let r = data.create_reader(); - LinCowCell { + LinCowCellRaw { updater: PhantomData, write: Mutex::new(data), active: SyncMutex::new(Arc::new(LinCowCellInner::new(r))), @@ -283,7 +283,7 @@ impl AsMut for LinCowCellWriteTxn<'_, T, R, U, M> { #[cfg(test)] mod tests { - use super::LinCowCell; + use super::LinCowCellRaw; use super::LinCowCellCapable; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -327,7 +327,7 @@ mod tests { #[tokio::test] async fn test_simple_create() { let data = TestData { x: 0 }; - let cc: LinCowCell = LinCowCell::new(data); + let cc: LinCowCellRaw = LinCowCellRaw::new(data); let cc_rotxn_a = cc.read(); println!("cc_rotxn_a -> {:?}", cc_rotxn_a); @@ -378,7 +378,7 @@ mod tests { // == mt tests == - async fn mt_writer(cc: Arc>) { + async fn mt_writer(cc: Arc>) { let mut last_value: i64 = 0; while last_value < 500 { let mut cc_wrtxn = cc.write().await; @@ -392,7 +392,7 @@ mod tests { } } - fn rt_writer(cc: Arc>) { + fn rt_writer(cc: Arc>) { let mut last_value: i64 = 0; while last_value < 500 { let cc_rotxn = cc.read(); @@ -411,8 +411,8 @@ mod tests { let start = Instant::now(); // Create the new cowcell. let data = TestData { x: 0 }; - let cc: Arc> = - Arc::new(LinCowCell::new(data)); + let cc: Arc> = + Arc::new(LinCowCellRaw::new(data)); let _ = tokio::join!( tokio::task::spawn_blocking({ @@ -494,7 +494,7 @@ mod tests { async fn test_gc_operation_thread( cc: Arc< - LinCowCell, TestGcWrapperReadTxn, TestGcWrapperWriteTxn>, + LinCowCellRaw, TestGcWrapperReadTxn, TestGcWrapperWriteTxn>, >, ) { while GC_COUNT.load(Ordering::Acquire) < 50 { @@ -516,8 +516,8 @@ mod tests { GC_COUNT.store(0, Ordering::Release); let data = TestGcWrapper { data: 0 }; let cc: Arc< - LinCowCell, TestGcWrapperReadTxn, TestGcWrapperWriteTxn>, - > = Arc::new(LinCowCell::new(data)); + LinCowCellRaw, TestGcWrapperReadTxn, TestGcWrapperWriteTxn>, + > = Arc::new(LinCowCellRaw::new(data)); let _ = tokio::join!( tokio::task::spawn(test_gc_operation_thread(cc.clone())), @@ -533,7 +533,7 @@ mod tests { #[cfg_attr(miri, ignore)] async fn test_long_chain_drop_no_stack_overflow() { let data = TestData { x: 0 }; - let cc: LinCowCell = LinCowCell::new(data); + let cc: LinCowCellRaw = LinCowCellRaw::new(data); // Simulate a read txn that is not dropped. let initial_read = cc.read(); @@ -555,7 +555,7 @@ mod tests { #[cfg(test)] mod tests_linear { - use super::LinCowCell; + use super::LinCowCellRaw; use super::LinCowCellCapable; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -621,11 +621,11 @@ mod tests_linear { GC_COUNT.store(0, Ordering::Release); assert!(GC_COUNT.load(Ordering::Acquire) == 0); let data = TestGcWrapper { data: 0 }; - let cc: LinCowCell< + let cc: LinCowCellRaw< TestGcWrapper, TestGcWrapperReadTxn, TestGcWrapperWriteTxn, - > = LinCowCell::new(data); + > = LinCowCellRaw::new(data); // Open a read A. let cc_rotxn_a = cc.read(); diff --git a/src/lc_tests.rs b/src/lc_tests.rs index 8e701eb..ccbaf51 100644 --- a/src/lc_tests.rs +++ b/src/lc_tests.rs @@ -1,5 +1,5 @@ use crate::internals::bptree::cursor::{CursorRead, CursorWrite, SuperBlock}; -use crate::internals::lincowcell::{LinCowCell, LinCowCellCapable}; +use crate::internals::lincowcell::{LinCowCellRaw, LinCowCellCapable}; struct TestStruct { bptree_map_a: SuperBlock, @@ -62,8 +62,8 @@ impl LinCowCellCapable for TestStruct { #[test] fn test_lc_basic() { - let lcc: LinCowCell = - LinCowCell::new(TestStruct { + let lcc: LinCowCellRaw = + LinCowCellRaw::new(TestStruct { bptree_map_a: unsafe { SuperBlock::new() }, bptree_map_b: unsafe { SuperBlock::new() }, }); diff --git a/src/lib.rs b/src/lib.rs index 2ad2463..311f6eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ static ALLOC: dhat::Alloc = dhat::Alloc; extern crate smallvec; pub mod cowcell; -pub use cowcell::CowCell; +pub use cowcell::CowCellRaw; #[cfg(feature = "ebr")] pub mod ebrcell; diff --git a/src/utils.rs b/src/utils.rs index de6f4b8..2c52a6a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -130,19 +130,4 @@ pub unsafe trait Monotonic { /// but they don't _have_ to changed without `next`. fn current(&self) -> Self::Output; fn next(&self) -> Self::Output; -} - -// provide default locking types -#[cfg(feature = "std")] -#[allow(unused)] -pub type DefaultRawMutex = parking_lot::RawMutex; -/// Provide a defaulkt raw mutex implementation for no_std environments via spinning -#[cfg(not(feature = "std"))] -#[allow(unused)] -pub type DefaultRawMutex = spin::mutex::SpinMutex<()>; - -#[cfg(feature = "std")] -pub type DefaultRawRwLock = parking_lot::RawRwLock; -#[cfg(not(feature = "std"))] -/// Provide a defaulkt raw mutex implementation for no_std environments via spinning -pub type DefaultRawRwLock = spin::RwLock<()>; +} \ No newline at end of file From 112bb79ff9f03cbf32b902cb89aa9a11b280d63f Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Mon, 14 Jul 2025 10:54:41 +0930 Subject: [PATCH 07/10] Remove improper reference to alloc crate. --- src/cowcell/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cowcell/mod.rs b/src/cowcell/mod.rs index 77bd650..e0e47a0 100644 --- a/src/cowcell/mod.rs +++ b/src/cowcell/mod.rs @@ -16,7 +16,7 @@ use core::ops::{Deref, DerefMut}; use lock_api::{Mutex, MutexGuard, RawMutex}; #[cfg(not(feature = "std"))] -use ::alloc::sync::Arc; +use alloc::sync::Arc; #[cfg(feature = "std")] use std::sync::Arc; From a3bcf699fd2f3a3ffdeeacbba5e025c9bcdea5bf Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Mon, 14 Jul 2025 16:57:16 +0930 Subject: [PATCH 08/10] Allow some tests to run with no features and with only the `no_std` feature, and fix a lint on the Monotonic trait definition. --- src/cowcell/mod.rs | 12 +++++++----- src/lib.rs | 2 +- src/utils.rs | 4 +++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/cowcell/mod.rs b/src/cowcell/mod.rs index e0e47a0..3ae18ca 100644 --- a/src/cowcell/mod.rs +++ b/src/cowcell/mod.rs @@ -260,7 +260,9 @@ where #[cfg(test)] mod tests { - use super::CowCell; + use super::CowCellRaw; + + type CowCell = CowCellRaw>; #[test] fn test_deref_mut() { @@ -281,10 +283,10 @@ mod tests { let data: i64 = 0; let cc: CowCell = CowCell::new(data); /* Take a write txn */ - let cc_wrtxn_a = cc.try_write(); + let cc_wrtxn_a: Option>> = cc.try_write(); assert!(cc_wrtxn_a.is_some()); /* Because we already hold the writ, the second is guaranteed to fail */ - let cc_wrtxn_a = cc.try_write(); + let cc_wrtxn_a: Option>> = cc.try_write(); assert!(cc_wrtxn_a.is_none()); } @@ -293,12 +295,12 @@ mod tests { let data: i64 = 0; let cc: CowCell = CowCell::new(data); - let cc_rotxn_a = cc.read(); + let cc_rotxn_a: crate::cowcell::CowCellReadTxn = cc.read(); assert_eq!(*cc_rotxn_a, 0); { /* Take a write txn */ - let mut cc_wrtxn = cc.write(); + let mut cc_wrtxn: crate::cowcell::CowCellWriteTxn<'_, i64, spin::mutex::Mutex<()>> = cc.write(); /* Get the data ... */ { let mut_ptr = cc_wrtxn.get_mut(); diff --git a/src/lib.rs b/src/lib.rs index 311f6eb..696f31b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ #![allow(clippy::needless_lifetimes)] #![cfg_attr(feature = "simd_support", feature(portable_simd))] -#[cfg(not(any(test, feature = "std")))] +#[cfg(not(feature = "std"))] extern crate alloc; #[cfg(any(test, feature = "std"))] diff --git a/src/utils.rs b/src/utils.rs index 2c52a6a..8281466 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -120,7 +120,9 @@ where /// This is intended for comparing the insertion times of items into the ArCache type. /// This would Default to an implementation over the Instant type on std, but could be an atomic counter with a caller-defined bit width in no_std environments. /// -/// SAFETY: This has been marked unsafe as there is a behaviour contract on the `next` function that will not be checked by the caller. Subsequent calls to `next` should +/// # Safety +/// +/// This has been marked unsafe as there is a behaviour contract on the `next` function that will not be checked by the caller. Subsequent calls to `next` should /// ALWAYS return an equal or greater value (based on the type's impl of PartialOrd) pub unsafe trait Monotonic { type Output: PartialOrd + Copy; From 826e39ae9c20487b3d60f02cacdc8dd10d9da9fd Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Wed, 16 Jul 2025 15:22:32 +0930 Subject: [PATCH 09/10] Fix broken benches. --- benches/arccache.rs | 6 ++--- benches/hashmap_benchmark.rs | 43 ++++++++++++++++++------------------ src/bptree/mod.rs | 32 +++++++++++++-------------- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/benches/arccache.rs b/benches/arccache.rs index 8e4ab6d..8199fee 100644 --- a/benches/arccache.rs +++ b/benches/arccache.rs @@ -1,7 +1,7 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use function_name::named; use rand::distributions::uniform::SampleUniform; -use rand::{thread_rng, Rng}; +use rand::{rng, Rng}; use std::collections::HashMap; use std::fmt::Debug; use std::hash::Hash; @@ -54,8 +54,8 @@ where fn next(&self) -> T { match self { AccessPattern::Random(min, max) => { - let mut rng = thread_rng(); - rng.gen_range(min.clone()..max.clone()) + let mut rng = rng(); + rng.random_range(min.clone()..max.clone()) } } } diff --git a/benches/hashmap_benchmark.rs b/benches/hashmap_benchmark.rs index 62dc534..ae259ed 100644 --- a/benches/hashmap_benchmark.rs +++ b/benches/hashmap_benchmark.rs @@ -19,9 +19,10 @@ extern crate criterion; extern crate rand; use concread::hashmap::*; -use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; -use lock_api::RawMutex; -use rand::{thread_rng, Rng}; +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use rand::{rng, Rng}; + +use std::hint::black_box; // ranges of counts for different benchmarks (MINs are inclusive, MAXes exclusive): const INSERT_COUNT_MIN: usize = 120; @@ -166,10 +167,10 @@ criterion_main!(insert, remove, search); // Utility functions: -fn insert_vec( - map: &mut HashMap, +fn insert_vec( + map: &mut HashMap, list: Vec<(u32, V)>, -) -> HashMapWriteTxn { +) -> HashMapWriteTxn { let mut write_txn = map.write(); for (key, val) in list.into_iter() { write_txn.insert(key, val); @@ -177,10 +178,10 @@ fn insert_vec( write_txn } -fn remove_vec<'a, V: Clone + Sync + Send + 'static, M: RawMutex + 'static>( - map: &'a mut HashMap, +fn remove_vec<'a, V: Clone + Sync + Send + 'static>( + map: &'a mut HashMap, list: &Vec, -) -> HashMapWriteTxn<'a, u32, V, M> { +) -> HashMapWriteTxn<'a, u32, V, parking_lot::RawMutex> { let mut write_txn = map.write(); for i in list.iter() { write_txn.remove(i); @@ -243,12 +244,12 @@ struct Struct { } fn prepare_insert(value: V) -> (HashMap, Vec<(u32, V)>) { - let mut rng = thread_rng(); - let count = rng.gen_range(INSERT_COUNT_MIN..INSERT_COUNT_MAX); + let mut rng = rng(); + let count = rng.random_range(INSERT_COUNT_MIN..INSERT_COUNT_MAX); let mut list = Vec::with_capacity(count); for _ in 0..count { list.push(( - rng.gen_range(0..INSERT_COUNT_MAX << 8) as u32, + rng.random_range(0..INSERT_COUNT_MAX << 8) as u32, value.clone(), )); } @@ -257,9 +258,9 @@ fn prepare_insert(value: V) -> (HashMap(value: V) -> (HashMap, Vec) { - let mut rng = thread_rng(); - let insert_count = rng.gen_range(INSERT_COUNT_FOR_REMOVE_MIN..INSERT_COUNT_FOR_REMOVE_MAX); - let remove_count = rng.gen_range(REMOVE_COUNT_MIN..REMOVE_COUNT_MAX); + let mut rng = rng(); + let insert_count = rng.random_range(INSERT_COUNT_FOR_REMOVE_MIN..INSERT_COUNT_FOR_REMOVE_MAX); + let remove_count = rng.random_range(REMOVE_COUNT_MIN..REMOVE_COUNT_MAX); let map = HashMap::new(); let mut write_txn = map.write(); for i in random_order(insert_count, insert_count).iter() { @@ -272,10 +273,10 @@ fn prepare_remove(value: V) -> (HashMap(value: V) -> (HashMap, Vec) { - let mut rng = thread_rng(); - let insert_count = rng.gen_range(INSERT_COUNT_FOR_SEARCH_MIN..INSERT_COUNT_FOR_SEARCH_MAX); + let mut rng = rng(); + let insert_count = rng.random_range(INSERT_COUNT_FOR_SEARCH_MIN..INSERT_COUNT_FOR_SEARCH_MAX); let search_limit = insert_count * SEARCH_SIZE_NUMERATOR / SEARCH_SIZE_DENOMINATOR; - let search_count = rng.gen_range(SEARCH_COUNT_MIN..SEARCH_COUNT_MAX); + let search_count = rng.random_range(SEARCH_COUNT_MIN..SEARCH_COUNT_MAX); // Create a HashMap with elements 0 through insert_count(-1) let map = HashMap::new(); @@ -288,20 +289,20 @@ fn prepare_search(value: V) -> (HashMap Vec { - let mut rng = thread_rng(); + let mut rng = rng(); let mut order = Vec::with_capacity(n); let mut generated = vec![false; up_to]; let mut remaining = n; let mut remaining_elems = up_to; while remaining > 0 { - let mut r = rng.gen_range(0..remaining_elems); + let mut r = rng.random_range(0..remaining_elems); // find the r-th yet nongenerated number: for i in 0..up_to { if generated[i] { diff --git a/src/bptree/mod.rs b/src/bptree/mod.rs index bbd65dd..d388ae9 100644 --- a/src/bptree/mod.rs +++ b/src/bptree/mod.rs @@ -389,7 +389,7 @@ mod tests { /* #[test] fn test_bptree2_map_write_compact() { - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let insa: Vec = (0..(L_CAPACITY << 4)).collect(); let map = BptreeMap::from_iter(insa.into_iter().map(|v| (v, v))); @@ -445,7 +445,7 @@ mod tests { .map(|_| { scope.spawn(move || { println!("Started reader ..."); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let mut proceed = true; while proceed { let m_read = mref.read(); @@ -453,7 +453,7 @@ mod tests { // Get a random number. // Add 10_000 * random // Remove 10_000 * random - let v1 = rng.gen_range(1, 18) * 10_000; + let v1 = rng.random_range(1, 18) * 10_000; let r1 = v1 + 10_000; for i in v1..r1 { m_read.get(&i); @@ -470,7 +470,7 @@ mod tests { .map(|_| { scope.spawn(move || { println!("Started writer ..."); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let mut proceed = true; while proceed { let mut m_write = mref.write(); @@ -478,9 +478,9 @@ mod tests { // Get a random number. // Add 10_000 * random // Remove 10_000 * random - let v1 = rng.gen_range(1, 18) * 10_000; + let v1 = rng.random_range(1, 18) * 10_000; let r1 = v1 + 10_000; - let v2 = rng.gen_range(1, 19) * 10_000; + let v2 = rng.random_range(1, 19) * 10_000; let r2 = v2 + 10_000; for i in v1..r1 { @@ -536,7 +536,7 @@ mod tests { .map(|_| { scope.spawn(move || { println!("Started reader ..."); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let mut proceed = true; while proceed { let m_read = mref.lock().unwrap(); @@ -544,7 +544,7 @@ mod tests { // Get a random number. // Add 10_000 * random // Remove 10_000 * random - let v1 = rng.gen_range(1, 18) * 10_000; + let v1 = rng.random_range(1, 18) * 10_000; let r1 = v1 + 10_000; for i in v1..r1 { m_read.get(&i); @@ -560,7 +560,7 @@ mod tests { .map(|_| { scope.spawn(move || { println!("Started writer ..."); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let mut proceed = true; while proceed { let mut m_write = mref.lock().unwrap(); @@ -568,9 +568,9 @@ mod tests { // Get a random number. // Add 10_000 * random // Remove 10_000 * random - let v1 = rng.gen_range(1, 18) * 10_000; + let v1 = rng.random_range(1, 18) * 10_000; let r1 = v1 + 10_000; - let v2 = rng.gen_range(1, 19) * 10_000; + let v2 = rng.random_range(1, 19) * 10_000; let r2 = v2 + 10_000; for i in v1..r1 { @@ -623,7 +623,7 @@ mod tests { .map(|_| { scope.spawn(move || { println!("Started reader ..."); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let mut proceed = true; while proceed { let m_read = mref.read().unwrap(); @@ -631,7 +631,7 @@ mod tests { // Get a random number. // Add 10_000 * random // Remove 10_000 * random - let v1 = rng.gen_range(1, 18) * 10_000; + let v1 = rng.random_range(1, 18) * 10_000; let r1 = v1 + 10_000; for i in v1..r1 { m_read.get(&i); @@ -647,7 +647,7 @@ mod tests { .map(|_| { scope.spawn(move || { println!("Started writer ..."); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let mut proceed = true; while proceed { let mut m_write = mref.write().unwrap(); @@ -655,9 +655,9 @@ mod tests { // Get a random number. // Add 10_000 * random // Remove 10_000 * random - let v1 = rng.gen_range(1, 18) * 10_000; + let v1 = rng.random_range(1, 18) * 10_000; let r1 = v1 + 10_000; - let v2 = rng.gen_range(1, 19) * 10_000; + let v2 = rng.random_range(1, 19) * 10_000; let r2 = v2 + 10_000; for i in v1..r1 { From 9abc9b0f5eb4d345b77bc65a9541493d417b8a4c Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Sat, 19 Jul 2025 23:04:55 +0930 Subject: [PATCH 10/10] Fix miri build error. --- src/hashtrie/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hashtrie/mod.rs b/src/hashtrie/mod.rs index 1753e26..9ff1969 100644 --- a/src/hashtrie/mod.rs +++ b/src/hashtrie/mod.rs @@ -75,7 +75,7 @@ impl< /// Attempt to create a new write, returns None if another writer /// already exists. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.inner .try_write() .map(|inner| HashTrieWriteTxn { inner })