Crates.io | atomic_ptr_cell |
lib.rs | atomic_ptr_cell |
version | |
source | src |
created_at | 2024-12-19 01:05:33.354789+00 |
updated_at | 2024-12-19 01:05:33.354789+00 |
description | Safe no_std repr(transparent) wrapper for AtomicPtr and &AtomicPtr with an api similar to a cell. |
homepage | |
repository | https://github.com/AlexanderSchuetz97/atomic_ptr_cell |
max_upload_size | |
id | 1488651 |
Cargo.toml error: | TOML parse error at line 18, column 1 | 18 | autolib = false | ^^^^^^^ unknown field `autolib`, expected one of `name`, `version`, `edition`, `authors`, `description`, `readme`, `license`, `repository`, `homepage`, `documentation`, `build`, `resolver`, `links`, `default-run`, `default_dash_run`, `rust-version`, `rust_dash_version`, `rust_version`, `license-file`, `license_dash_file`, `license_file`, `licenseFile`, `license_capital_file`, `forced-target`, `forced_dash_target`, `autobins`, `autotests`, `autoexamples`, `autobenches`, `publish`, `metadata`, `keywords`, `categories`, `exclude`, `include` |
size | 0 |
Safe no_std repr(transparent) wrapper for AtomicPtr and &AtomicPtr with an api similar to a cell.
The crate requires an allocator using extern crate alloc;
if used without std.
This crate supports all targets that satisfy #[cfg(target_has_atomic = "ptr")]
.
On other targets this crate is a noop and does nothing.
Borrow | Borrow Mut | Exclusive Borrow (*1) | Sound (*2) | repr(transparent) | Borrowing solves ABA Problem | Supports &AtomicPtr |
|
---|---|---|---|---|---|---|---|
atomic_ptr_cell | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ |
atomiccell | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ |
ptr_cell | ✗ | ✗ | _ | ✗ (*3) | ✓ | _ | ✗ |
(*1) While a value is borrowed from the cell other threads cannot interact with the cell in a way that would violate borrowing rules if this were a normal RefCell:
✓ other threads are prevented from modifying/accessing the cell
✗ other threads can overwrite the value in the cell while it is borrowed.
(*2) Can you cause UB using only safe functions from the crate?
✓ UB is not possible using only safe functions of the crate.
✗ UB is possible with only using unsafe functions.
(*3) ptr_cell v2.1.1 replace_ptr fn should be unsafe as it allows the user to place an arbitrary pointer into its cell which is directly fed into Box::from_raw from various other safe fn's. This can cause UB if the user calls it with a non-null ptr not created by Box::into_raw making the entire fn unsound. I refrain from creating an issue in that crate as comments in the source code indicate that the author of that crate is aware of it.
A thread that borrows a value atomically moves it out of the cell and transfers ownership of it to the guard. When the guard is dropped the value is atomically written back into the cell if the cell happens to be empty at that time.
Because the value is always temporarily owned by the guard it is both Deref and DerefMut of the cells type.
As should be obvious this does absolutely nothing to prevent ABA
from occurring with the cell while the value is borrowed.
If you require this then consider using the atomiccell
crate.
Aside from the really obvious new/set/take/borrow functions this crate provides the following fn's for its cell types.
from(&[&AtomicPtr]) -> &[&Cell]
constructor (essentially mem transmute thanks to repr(transparent))
const new() -> Cell
constructor
swap(T) -> Option<T>
set_if_absent(T) -> Option<T>
set_if_present(T) -> enum WasPresent(T) or WasAbsent(T)
borrow_swap(T) -> enum WasPresent(Guard(T)) or WasAbsent(T)
borrow_or_else(FnOnce() -> T) -> Guard(T)
Into/From implementations where they make sense. (ex: From<T> for Cell<T>
)
Discard a Guard(T) so that its value is not written back into the cell.
Force the value of a Guard(T) to be written back into the cell even if the cell contains another value in the meantime.
This crate is lock free.
It is not entirely wait free. The set_if_present
fn is not wait free and contains the usual CAS loop.
use std::thread;
use atomic_ptr_cell::*;
static CELL: AtomicPointerCell<String> = AtomicPointerCell::new();
fn test() {
CELL.set("Hello".to_string());
let guard = CELL.borrow().unwrap();
assert_eq!("Hello", guard.as_str());
let jh = thread::spawn(|| {
CELL.set("World".to_string());
let guard = CELL.borrow().unwrap();
assert!("Hello" == guard.as_str() || "World" == guard.as_str());
drop(guard);
});
drop(guard);
//This small example already has a surprising amount of possible outcomes :D
let Some(value) = CELL.take() else {
_= jh.join();
let value = CELL.take().unwrap();
assert!("Hello" == value.as_str() || "World" == value.as_str());
return;
};
assert!("Hello" == value.as_str() || "World" == value.as_str());
_= jh.join();
if let Some(value2) = CELL.take() {
//Depending on the order of execution CELL.take() may return None here.
assert_ne!(value, value2);
}
}
use atomic_ptr_cell::*;
use std::ptr::null_mut;
use std::sync::atomic::AtomicPtr;
use std::thread;
static CELL: AtomicPtr<String> = AtomicPtr::new(null_mut());
#[test]
fn test() {
// Safety: Caller must guarantee that the AtomicPtr never contains a non-null pointer that is not from Box::into_raw
let cell: AtomicPtrCell<String> = unsafe { AtomicPtrCell::new(&CELL) };
cell.set("Hello".to_string());
let guard = cell.borrow().unwrap();
assert_eq!("Hello", guard.as_str());
let jh = thread::spawn(move || {
//The AtomicPtrCell is copy as its layout is equivalent to &AtomicPtr
// making it easy to use in closures and threads.
// You just need to ensure the lifetime of &AtomicPtr outlives the scope.
// In this example the lifetime is static.
cell.set("World".to_string());
let guard = cell.borrow().unwrap();
assert!("Hello" == guard.as_str() || "World" == guard.as_str());
drop(guard);
});
drop(guard);
//This small example already has a surprising amount of possible outcomes :D
let Some(value) = cell.take() else {
_ = jh.join();
let value = cell.take().unwrap();
assert!("Hello" == value.as_str() || "World" == value.as_str());
return;
};
assert!("Hello" == value.as_str() || "World" == value.as_str());
_ = jh.join();
if let Some(value2) = cell.take() {
//Depending on the order of execution CELL.take() may return None here.
assert_ne!(value, value2);
}
}