Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ name = "atomic_refcell"
version = "0.1.12"
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
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]
serde_json = "1.0"

[features]
default = []
portable-atomic = ["dep:portable-atomic"]
serde = ["dep:serde"]
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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<T>`. |

## Minimum Supported Rust Version (MSRV)

Rust **1.60** or later is required.
2 changes: 1 addition & 1 deletion benches/basic.rs
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
35 changes: 22 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -120,7 +123,7 @@ impl<T> AtomicRefCell<T> {
impl<T: ?Sized> AtomicRefCell<T> {
/// Immutably borrows the wrapped value.
#[inline]
pub fn borrow(&self) -> AtomicRef<T> {
pub fn borrow(&self) -> AtomicRef<'_, T> {
match AtomicBorrowRef::try_new(&self.borrow) {
Ok(borrow) => AtomicRef {
value: unsafe { NonNull::new_unchecked(self.value.get()) },
Expand All @@ -133,7 +136,7 @@ impl<T: ?Sized> AtomicRefCell<T> {
/// Attempts to immutably borrow the wrapped value, but instead of panicking
/// on a failed borrow, returns `Err`.
#[inline]
pub fn try_borrow(&self) -> Result<AtomicRef<T>, BorrowError> {
pub fn try_borrow(&self) -> Result<AtomicRef<'_, T>, BorrowError> {
match AtomicBorrowRef::try_new(&self.borrow) {
Ok(borrow) => Ok(AtomicRef {
value: unsafe { NonNull::new_unchecked(self.value.get()) },
Expand All @@ -145,7 +148,7 @@ impl<T: ?Sized> AtomicRefCell<T> {

/// Mutably borrows the wrapped value.
#[inline]
pub fn borrow_mut(&self) -> AtomicRefMut<T> {
pub fn borrow_mut(&self) -> AtomicRefMut<'_, T> {
match AtomicBorrowRefMut::try_new(&self.borrow) {
Ok(borrow) => AtomicRefMut {
value: unsafe { NonNull::new_unchecked(self.value.get()) },
Expand All @@ -159,7 +162,7 @@ impl<T: ?Sized> AtomicRefCell<T> {
/// 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<AtomicRefMut<T>, BorrowMutError> {
pub fn try_borrow_mut(&self) -> Result<AtomicRefMut<'_, T>, BorrowMutError> {
match AtomicBorrowRefMut::try_new(&self.borrow) {
Ok(borrow) => Ok(AtomicRefMut {
value: unsafe { NonNull::new_unchecked(self.value.get()) },
Expand Down Expand Up @@ -194,7 +197,7 @@ impl<T: ?Sized> AtomicRefCell<T> {
// 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> {
Expand All @@ -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 })
}
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
<T as Debug>::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 {
<T as Debug>::fmt(self, f)
}
}

impl<T: ?Sized + Debug> Debug for AtomicRefCell<T> {
impl<T: ?Sized + Debug> Debug for AtomicRefCell<T> {
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.
Expand All @@ -523,7 +530,9 @@ impl<T: ?Sized + Debug> Debug for AtomicRefCell<T> {
}
}

f.debug_struct("AtomicRefCell").field("value", &BorrowedPlaceholder).finish()
f.debug_struct("AtomicRefCell")
.field("value", &BorrowedPlaceholder)
.finish()
}
}
}
Expand Down
5 changes: 0 additions & 5 deletions tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
extern crate atomic_refcell;

#[cfg(feature = "serde")]
extern crate serde;

use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};

#[derive(Debug)]
Expand Down