diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8d0a96..21667ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Build project - run: cargo build --all-targets --all-features + run: cargo build --all-targets --features full docs: name: "Docs" @@ -56,9 +56,9 @@ jobs: - name: Run clippy run: cargo clippy - name: Run clippy with all features - run: cargo clippy --all-targets --all-features + run: cargo clippy --all-targets --features full - name: Run clippy on tests - run: cargo clippy --tests --all-targets --all-features + run: cargo clippy --tests --all-targets --features full tests: name: "Tests" @@ -70,9 +70,9 @@ jobs: - name: Run tests run: cargo test - name: Run tests with all features - run: cargo test --all-features + run: cargo test --features full - name: Run tests with all features in release mode - run: cargo test --all-features --release + run: cargo test --features full --release loom: name: "Loom" @@ -98,4 +98,4 @@ jobs: cargo install cargo-binstall cargo binstall cargo-nextest --no-confirm --secure - name: Run tests with Miri - run: cargo miri nextest run --features serde --test-threads 4 \ No newline at end of file + run: cargo miri nextest run --features full --test-threads 4 \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 5308b1d..42b5623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ readme = "README.md" edition = "2021" [lints.rust] -rust_2018_idioms = "warn" +rust_2018_idioms = { level = "warn", priority = 1 } unreachable_pub = "warn" missing_docs = "warn" missing_debug_implementations = "warn" @@ -25,13 +25,11 @@ missing_debug_implementations = "warn" pedantic = { level = "warn", priority = -1 } [dependencies] +loom = { version = "0.7", optional = true } once_cell = "1" parking_lot = "0.12" serde = { version = "1", optional = true, features = ["derive"] } -[target.'cfg(loom)'.dependencies] -loom = { version = "0.7", features = ["checkpoint"] } - [dev-dependencies] rand = "0.8" serde_json = "1" @@ -39,4 +37,6 @@ static_assertions = "1" [features] miri_action_log = [] -serde = ["dep:serde"] \ No newline at end of file +serde = ["dep:serde"] +full = ["serde"] +loom = ["dep:loom"] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a6fb9d8..1c868d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ /// Collection types. pub mod collections; /// Utilities for testing the codebase with [`loom`](crate::external::loom). -#[cfg(loom)] +#[cfg(feature = "loom")] pub mod loom; #[doc(hidden)] pub mod sync; @@ -33,7 +33,7 @@ pub mod external { #[cfg(feature = "serde")] pub use serde; - #[cfg(loom)] + #[cfg(feature = "loom")] pub use loom; pub use once_cell; pub use parking_lot; diff --git a/src/sync.rs b/src/sync.rs index e37da78..d903c3d 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,7 +1,7 @@ -#[cfg(loom)] +#[cfg(feature = "loom")] pub use loom::sync::{atomic::AtomicUsize, atomic::Ordering, RwLock, RwLockReadGuard}; -#[cfg(not(loom))] +#[cfg(not(feature = "loom"))] pub use { parking_lot::RwLock, std::sync::atomic::{AtomicUsize, Ordering}, diff --git a/src/type_gen.rs b/src/type_gen.rs index 0cdff96..d20c7a9 100644 --- a/src/type_gen.rs +++ b/src/type_gen.rs @@ -4,7 +4,7 @@ mod key_wrapper; mod key_wrapper_bounded; mod plain_id; -#[cfg(all(test, not(loom)))] +#[cfg(all(test, not(feature = "loom")))] mod tests { use crate::{ define_key_wrapper, define_key_wrapper_bounded, define_plain_id, prelude::BlazeMapId, diff --git a/src/type_gen/key_wrapper.rs b/src/type_gen/key_wrapper.rs index 54fcaa9..2fc1f4c 100644 --- a/src/type_gen/key_wrapper.rs +++ b/src/type_gen/key_wrapper.rs @@ -107,7 +107,7 @@ macro_rules! key_wrapper_inner { #[repr(transparent)] $vis struct $new_type($crate::utils::OffsetProvider); - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] impl $new_type { #[doc = ::std::concat!("Creates a new instance of [`", ::std::stringify!($new_type), "`].")] @@ -134,7 +134,7 @@ macro_rules! key_wrapper_inner { } } - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] impl $crate::traits::BlazeMapIdStatic for $new_type { #[inline] diff --git a/src/type_gen/key_wrapper_bounded.rs b/src/type_gen/key_wrapper_bounded.rs index 03091d1..7737847 100644 --- a/src/type_gen/key_wrapper_bounded.rs +++ b/src/type_gen/key_wrapper_bounded.rs @@ -112,7 +112,7 @@ macro_rules! key_wrapper_bounded_inner { #[repr(transparent)] $vis struct $new_type($crate::utils::OffsetProvider); - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] impl $new_type { #[doc = ::std::concat!("Creates a new instance of [`", ::std::stringify!($new_type), "`].")] @@ -152,7 +152,7 @@ macro_rules! key_wrapper_bounded_inner { } } - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] impl $crate::traits::BlazeMapIdStatic for $new_type { #[inline] diff --git a/src/type_gen/plain_id.rs b/src/type_gen/plain_id.rs index ccf2b27..951e9f6 100644 --- a/src/type_gen/plain_id.rs +++ b/src/type_gen/plain_id.rs @@ -73,7 +73,7 @@ macro_rules! plain_id_inner { { #[doc = ::std::concat!("Creates a new instance of [`", ::std::stringify!($new_type), "`].")] #[inline] - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] $vis fn new() -> Self { let next_id = ::static_container().next_id(); Self(unsafe { $crate::utils::OffsetProvider::::new(next_id) }) @@ -81,7 +81,7 @@ macro_rules! plain_id_inner { #[doc = ::std::concat!("Creates a new instance of [`", ::std::stringify!($new_type), "`].")] #[inline] - #[cfg(loom)] + #[cfg(feature = "loom")] $vis fn new(type_info_container: &::TypeInfoContainer) -> Self { let next_id = type_info_container.next_id(); Self(unsafe { $crate::utils::OffsetProvider::::new(next_id) }) @@ -104,7 +104,7 @@ macro_rules! plain_id_inner { } } - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] impl $crate::traits::BlazeMapIdStatic for $new_type { #[inline] diff --git a/src/type_info_containers/key_wrapper.rs b/src/type_info_containers/key_wrapper.rs index 3a9407f..47ba2e7 100644 --- a/src/type_info_containers/key_wrapper.rs +++ b/src/type_info_containers/key_wrapper.rs @@ -5,7 +5,7 @@ use std::{ ops::Deref, }; -#[cfg(not(loom))] +#[cfg(not(feature = "loom"))] use once_cell::sync::Lazy; use crate::{ @@ -16,7 +16,7 @@ use crate::{ /// Global, statically initialized container with correspondence mapping /// between blazemap offset wrappers and original keys. -#[cfg(not(loom))] +#[cfg(not(feature = "loom"))] #[doc(hidden)] #[derive(Debug)] pub struct StaticContainer { @@ -28,7 +28,7 @@ pub struct StaticContainer { /// Note that it cannot be static /// due to the [`loom` inability](https://github.com/tokio-rs/loom/issues/290) /// to test statically initialized code. -#[cfg(loom)] +#[cfg(feature = "loom")] #[doc(hidden)] #[derive(Debug)] pub struct StaticContainer { @@ -36,11 +36,18 @@ pub struct StaticContainer { orig_to_offset: HashMap, } +impl Default for StaticContainer { + #[inline] + fn default() -> Self { + Self::new() + } +} + impl StaticContainer { /// Creates a new instance of [`StaticContainer`]. #[inline] #[must_use] - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] pub const fn new() -> Self { Self { offset_to_orig: vec![], @@ -57,7 +64,7 @@ impl StaticContainer { /// different containers of the same type. #[inline] #[must_use] - #[cfg(loom)] + #[cfg(feature = "loom")] pub fn new() -> Self { Self { offset_to_orig: vec![], @@ -73,17 +80,17 @@ where { #[inline] fn wrap_key(&self, key: K) -> I { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let offset = self.read().orig_to_offset.get(&key).copied(); - #[cfg(loom)] + #[cfg(feature = "loom")] let offset = self.read().unwrap().orig_to_offset.get(&key).copied(); unsafe { if let Some(offset) = offset { I::from_offset_unchecked(offset) } else { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let mut guard = self.write(); - #[cfg(loom)] + #[cfg(feature = "loom")] let mut guard = self.write().unwrap(); let container = &mut *guard; let offset = match container.orig_to_offset.entry(key) { @@ -110,9 +117,9 @@ where #[inline] fn capacity_info_provider(&self) -> impl Deref { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let result = self.read(); - #[cfg(loom)] + #[cfg(feature = "loom")] let result = self.read().unwrap(); result } @@ -121,9 +128,9 @@ where fn key_by_offset_provider( &self, ) -> impl Deref> { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let result = self.read(); - #[cfg(loom)] + #[cfg(feature = "loom")] let result = self.read().unwrap(); result } @@ -139,9 +146,9 @@ impl CapacityInfoProvider for StaticContainer { impl KeyByOffsetProvider for StaticContainer { #[inline] unsafe fn key_by_offset_unchecked(&self, offset: usize) -> impl Borrow { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let result = self.offset_to_orig.get_unchecked(offset); - #[cfg(loom)] + #[cfg(feature = "loom")] let result = self.offset_to_orig.get(offset).unwrap(); result } diff --git a/src/type_info_containers/key_wrapper_bounded.rs b/src/type_info_containers/key_wrapper_bounded.rs index 6574e84..751588d 100644 --- a/src/type_info_containers/key_wrapper_bounded.rs +++ b/src/type_info_containers/key_wrapper_bounded.rs @@ -4,7 +4,7 @@ use std::{ }; use std::{borrow::Borrow, ops::Deref}; -#[cfg(not(loom))] +#[cfg(not(feature = "loom"))] use std::{ cell::UnsafeCell, mem::{needs_drop, MaybeUninit}, @@ -17,7 +17,7 @@ use crate::{ traits::{CapacityInfoProvider, KeyByOffsetProvider, TypeInfoContainer, WrapKey}, }; -#[cfg(loom)] +#[cfg(feature = "loom")] use crate::sync::RwLockReadGuard; /// Global, statically initialized container with correspondence mapping @@ -28,7 +28,7 @@ use crate::sync::RwLockReadGuard; /// for the case when the user could statically guarantee /// that the number of unique keys doesn't exceed `CAP`, it's optimized for read /// operations so that they don't create any multi-thread contention. -#[cfg(not(loom))] +#[cfg(not(feature = "loom"))] #[doc(hidden)] #[derive(Debug)] pub struct StaticContainer { @@ -41,7 +41,7 @@ pub struct StaticContainer { /// Note that it cannot be static /// due to the [`loom` inability](https://github.com/tokio-rs/loom/issues/290) /// to test statically initialized code. -#[cfg(loom)] +#[cfg(feature = "loom")] #[doc(hidden)] #[derive(Debug)] pub struct StaticContainer { @@ -50,7 +50,7 @@ pub struct StaticContainer { next_offset: AtomicUsize, } -#[cfg(not(loom))] +#[cfg(not(feature = "loom"))] impl Default for StaticContainer { #[inline] fn default() -> Self { @@ -64,11 +64,19 @@ impl Default for StaticContainer { } } +#[cfg(feature = "loom")] +impl Default for StaticContainer { + #[inline] + fn default() -> Self { + Self::new() + } +} + impl StaticContainer { /// Creates a new instance of [`StaticContainer`]. #[inline] #[must_use] - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] pub fn new() -> Self { Self::default() } @@ -82,7 +90,7 @@ impl StaticContainer { /// different containers of the same type. #[inline] #[must_use] - #[cfg(loom)] + #[cfg(feature = "loom")] pub fn new() -> Self { Self { offset_to_orig: std::iter::repeat_with(|| RwLock::new(None)) @@ -95,14 +103,14 @@ impl StaticContainer { #[inline] #[doc(hidden)] - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] pub unsafe fn key_by_offset_unchecked(&self, offset: usize) -> &K { (*self.offset_to_orig.get_unchecked(offset).get()).assume_init_ref() } #[inline] #[doc(hidden)] - #[cfg(loom)] + #[cfg(feature = "loom")] pub unsafe fn key_by_offset_unchecked(&self, offset: usize) -> RwLockReadGuard<'_, Option> { self.offset_to_orig.get(offset).unwrap().read().unwrap() } @@ -115,17 +123,17 @@ where { #[inline] fn wrap_key(&self, key: K) -> I { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let offset = self.orig_to_offset.read().get(&key).copied(); - #[cfg(loom)] + #[cfg(feature = "loom")] let offset = self.orig_to_offset.read().unwrap().get(&key).copied(); unsafe { if let Some(offset) = offset { I::from_offset_unchecked(offset) } else { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let mut guard = self.orig_to_offset.write(); - #[cfg(loom)] + #[cfg(feature = "loom")] let mut guard = self.orig_to_offset.write().unwrap(); let offset = match guard.entry(key) { Entry::Vacant(entry) => { @@ -134,15 +142,13 @@ where .offset_to_orig .get(offset) .unwrap_or_else(|| panic!("capacity {CAP} overflow")); - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] (*cell.get()).write(entry.key().clone()); - #[cfg(loom)] + #[cfg(feature = "loom")] { let mut guard = cell.try_write().unwrap(); let value = &mut *guard; - if value.is_some() { - panic!("value is already set") - } + assert!(value.is_none(), "value is already set"); *value = Some(entry.key().clone()); } entry.insert(offset); @@ -161,22 +167,22 @@ where impl Drop for StaticContainer { #[inline] fn drop(&mut self) { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] if !needs_drop::() { return; } - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] let num_init = *self.next_offset.get_mut(); - #[cfg(loom)] + #[cfg(feature = "loom")] let num_init = self.next_offset.load(Ordering::Acquire); self.offset_to_orig.as_mut_slice()[..num_init] .iter_mut() .for_each(|cell| { - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] unsafe { cell.get_mut().assume_init_drop(); }; - #[cfg(loom)] + #[cfg(feature = "loom")] let _ = cell.try_write().unwrap().take(); }); } @@ -207,10 +213,10 @@ impl CapacityInfoProvider for StaticContainer { } } -#[cfg(loom)] +#[cfg(feature = "loom")] struct BorrowGuard<'a, K>(RwLockReadGuard<'a, Option>); -#[cfg(loom)] +#[cfg(feature = "loom")] impl Borrow for BorrowGuard<'_, K> { fn borrow(&self) -> &K { self.0.as_ref().unwrap() @@ -221,7 +227,7 @@ impl KeyByOffsetProvider for StaticContainer { #[inline] unsafe fn key_by_offset_unchecked(&self, offset: usize) -> impl Borrow { let result = StaticContainer::key_by_offset_unchecked(self, offset); - #[cfg(loom)] + #[cfg(feature = "loom")] let result = BorrowGuard(result); result } diff --git a/src/type_info_containers/plain_id.rs b/src/type_info_containers/plain_id.rs index ada73bb..4ffb3e7 100644 --- a/src/type_info_containers/plain_id.rs +++ b/src/type_info_containers/plain_id.rs @@ -10,11 +10,18 @@ pub struct StaticContainer { next_offset: AtomicUsize, } +impl Default for StaticContainer { + #[inline] + fn default() -> Self { + Self::new() + } +} + impl StaticContainer { /// Creates a new instance of [`StaticContainer`]. #[inline] #[must_use] - #[cfg(not(loom))] + #[cfg(not(feature = "loom"))] pub const fn new() -> Self { Self { next_offset: AtomicUsize::new(0), @@ -30,7 +37,7 @@ impl StaticContainer { /// different containers of the same type. #[inline] #[must_use] - #[cfg(loom)] + #[cfg(feature = "loom")] pub fn new() -> Self { Self { next_offset: AtomicUsize::new(0), @@ -39,6 +46,7 @@ impl StaticContainer { /// Returns the next identifier. #[inline] + #[must_use] pub fn next_id(&self) -> usize { self.next_offset .fetch_update(Ordering::Release, Ordering::Acquire, |next_id| {