diff --git a/Cargo.toml b/Cargo.toml index cf28d62..5a52b01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,12 +3,16 @@ name = "atomic_refcell" version = "0.1.12" authors = ["Bobby Holley "] description = "Threadsafe RefCell" -license = "Apache-2.0/MIT" +license = "Apache-2.0 OR MIT" repository = "https://github.com/bholley/atomic_refcell" documentation = "https://docs.rs/atomic_refcell/" -edition = "2018" +edition = "2021" +rust-version = "1.60" [dependencies] +portable-atomic = { version = "1", optional = true, default-features = false, features = [ + "require-cas", +] } serde = { version = "1.0", optional = true } [dev-dependencies] @@ -16,4 +20,5 @@ serde_json = "1.0" [features] default = [] +portable-atomic = ["dep:portable-atomic"] serde = ["dep:serde"] diff --git a/README.md b/README.md index de72c55..09f370f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,20 @@ # atomic_refcell -Threadsafe RefCell for Rust. + +A thread-safe analogue of [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) for Rust. + +## Overview + +[`AtomicRefCell`](https://docs.rs/atomic_refcell/) provides `RefCell`-like borrow semantics (checked at runtime, with immutable and mutable borrows) for values shared across threads. It is designed for use cases where the caller can guarantee that mutable and immutable borrows never overlap concurrently, but still requires a `Sync` type. + +The crate is `no_std` compatible. + +## Features + +| Feature | Description | +|---|---| +| `portable-atomic` | Use [`portable-atomic`](https://crates.io/crates/portable-atomic) instead of `core::sync::atomic`. Enables support for targets without native atomic compare-and-swap instructions (e.g. `thumbv6m-none-eabi`). | +| `serde` | Implement `Serialize` and `Deserialize` for `AtomicRefCell`. | + +## Minimum Supported Rust Version (MSRV) + +Rust **1.60** or later is required. diff --git a/benches/basic.rs b/benches/basic.rs index 3902a98..7988f5c 100644 --- a/benches/basic.rs +++ b/benches/basic.rs @@ -1,12 +1,12 @@ #![feature(test)] -extern crate atomic_refcell; extern crate test; use atomic_refcell::AtomicRefCell; use test::Bencher; #[derive(Default)] +#[allow(dead_code)] struct Bar(u32); #[bench] diff --git a/src/lib.rs b/src/lib.rs index 1f50069..a9ea84d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,10 +52,13 @@ use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; use core::sync::atomic; + +#[cfg(not(feature = "portable-atomic"))] use core::sync::atomic::AtomicUsize; -#[cfg(feature = "serde")] -extern crate serde; +#[cfg(feature = "portable-atomic")] +use portable_atomic::AtomicUsize; + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -120,7 +123,7 @@ impl AtomicRefCell { impl AtomicRefCell { /// Immutably borrows the wrapped value. #[inline] - pub fn borrow(&self) -> AtomicRef { + pub fn borrow(&self) -> AtomicRef<'_, T> { match AtomicBorrowRef::try_new(&self.borrow) { Ok(borrow) => AtomicRef { value: unsafe { NonNull::new_unchecked(self.value.get()) }, @@ -133,7 +136,7 @@ impl AtomicRefCell { /// Attempts to immutably borrow the wrapped value, but instead of panicking /// on a failed borrow, returns `Err`. #[inline] - pub fn try_borrow(&self) -> Result, BorrowError> { + pub fn try_borrow(&self) -> Result, BorrowError> { match AtomicBorrowRef::try_new(&self.borrow) { Ok(borrow) => Ok(AtomicRef { value: unsafe { NonNull::new_unchecked(self.value.get()) }, @@ -145,7 +148,7 @@ impl AtomicRefCell { /// Mutably borrows the wrapped value. #[inline] - pub fn borrow_mut(&self) -> AtomicRefMut { + pub fn borrow_mut(&self) -> AtomicRefMut<'_, T> { match AtomicBorrowRefMut::try_new(&self.borrow) { Ok(borrow) => AtomicRefMut { value: unsafe { NonNull::new_unchecked(self.value.get()) }, @@ -159,7 +162,7 @@ impl AtomicRefCell { /// Attempts to mutably borrow the wrapped value, but instead of panicking /// on a failed borrow, returns `Err`. #[inline] - pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { + pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { match AtomicBorrowRefMut::try_new(&self.borrow) { Ok(borrow) => Ok(AtomicRefMut { value: unsafe { NonNull::new_unchecked(self.value.get()) }, @@ -194,7 +197,7 @@ impl AtomicRefCell { // Core synchronization logic. Keep this section small and easy to audit. // -const HIGH_BIT: usize = !(::core::usize::MAX >> 1); +const HIGH_BIT: usize = !(usize::MAX >> 1); const MAX_FAILED_BORROWS: usize = HIGH_BIT + (HIGH_BIT >> 1); struct AtomicBorrowRef<'b> { @@ -218,7 +221,7 @@ impl<'b> AtomicBorrowRef<'b> { Self::check_overflow(borrow, new); Err("already mutably borrowed") } else { - Ok(AtomicBorrowRef { borrow: borrow }) + Ok(AtomicBorrowRef { borrow }) } } @@ -399,6 +402,7 @@ impl<'b, T: ?Sized> AtomicRef<'b, T> { /// /// Like its [std-counterpart](core::cell::Ref::clone), this type does not implement `Clone` /// to not interfere with cloning the contained type. + #[allow(clippy::should_implement_trait)] #[inline] pub fn clone(orig: &AtomicRef<'b, T>) -> AtomicRef<'b, T> { AtomicRef { @@ -497,21 +501,24 @@ impl<'b, T: ?Sized> DerefMut for AtomicRefMut<'b, T> { } impl<'b, T: ?Sized + Debug + 'b> Debug for AtomicRef<'b, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::fmt(self, f) } } impl<'b, T: ?Sized + Debug + 'b> Debug for AtomicRefMut<'b, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::fmt(self, f) } } -impl Debug for AtomicRefCell { +impl Debug for AtomicRefCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_borrow() { - Ok(borrow) => f.debug_struct("AtomicRefCell").field("value", &borrow).finish(), + Ok(borrow) => f + .debug_struct("AtomicRefCell") + .field("value", &borrow) + .finish(), Err(_) => { // The RefCell is mutably borrowed so we can't look at its value // here. Show a placeholder instead. @@ -523,7 +530,9 @@ impl Debug for AtomicRefCell { } } - f.debug_struct("AtomicRefCell").field("value", &BorrowedPlaceholder).finish() + f.debug_struct("AtomicRefCell") + .field("value", &BorrowedPlaceholder) + .finish() } } } diff --git a/tests/basic.rs b/tests/basic.rs index 49eaa77..49bd8a9 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -1,8 +1,3 @@ -extern crate atomic_refcell; - -#[cfg(feature = "serde")] -extern crate serde; - use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; #[derive(Debug)]