#![doc = include_str!("../README.md")]
#![no_std]
#[cfg(test)]
#[macro_use]
extern crate std;
use core::cell::Cell;
use core::fmt;
use core::ptr;
#[repr(transparent)]
pub struct SyncCell<T: ?Sized>(Cell<T>);
unsafe impl<T: ?Sized> Send for SyncCell<T> where Cell<T>: Send {}
unsafe impl<T: ?Sized + Sync> Sync for SyncCell<T> {}
impl<T> SyncCell<T> {
#[inline]
pub const fn new(value: T) -> Self {
Self(Cell::new(value))
}
#[inline]
pub unsafe fn set(&self, val: T) {
self.0.set(val);
}
#[inline]
pub unsafe fn swap(&self, other: &SyncCell<T>) {
self.0.swap(&other.0);
}
#[inline]
pub unsafe fn replace(&self, val: T) -> T {
self.0.replace(val)
}
#[inline]
pub fn into_inner(self) -> T {
self.0.into_inner()
}
}
impl<T: Copy> SyncCell<T> {
#[inline]
pub unsafe fn get(&self) -> T {
self.0.get()
}
}
impl<T: ?Sized> SyncCell<T> {
#[inline]
pub const fn as_ptr(&self) -> *mut T {
self.0.as_ptr()
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.0.get_mut()
}
#[inline]
pub fn from_mut(value: &mut T) -> &Self {
unsafe { &*(ptr::from_ref(Cell::from_mut(value)) as *const Self) }
}
}
impl<T: Default> SyncCell<T> {
#[inline]
pub unsafe fn take(&self) -> T {
self.0.take()
}
}
impl<T> SyncCell<[T]> {
#[inline]
pub fn as_slice_of_cells(&self) -> &[SyncCell<T>] {
let slice_of_cells = self.0.as_slice_of_cells();
unsafe { &*(ptr::from_ref(slice_of_cells) as *const [SyncCell<T>]) }
}
}
impl<T: Default> Default for SyncCell<T> {
#[inline]
fn default() -> SyncCell<T> {
SyncCell::new(Default::default())
}
}
impl<T> From<T> for SyncCell<T> {
#[inline]
fn from(value: T) -> SyncCell<T> {
SyncCell::new(value)
}
}
impl<T: ?Sized> fmt::Debug for SyncCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SyncCell").finish_non_exhaustive()
}
}
pub trait SyncSlice<T> {
fn as_sync_slice(&mut self) -> &[SyncCell<T>];
}
impl<T> SyncSlice<T> for [T] {
fn as_sync_slice(&mut self) -> &[SyncCell<T>] {
SyncCell::from_mut(self).as_slice_of_cells()
}
}
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::{assert_impl_all, assert_not_impl_any};
use std::rc::Rc;
assert_impl_all!(SyncCell<i32>: Send, Sync);
assert_impl_all!(SyncCell<[i32]>: Send, Sync);
assert_impl_all!(SyncCell<Cell<u8>>: Send);
assert_not_impl_any!(SyncCell<Cell<u8>>: Sync);
assert_not_impl_any!(SyncCell<Rc<u8>>: Send, Sync);
#[test]
fn test_new_and_into_inner() {
let c = SyncCell::new(42);
assert_eq!(c.into_inner(), 42);
}
#[test]
fn test_set_and_get() {
let c = SyncCell::new(0);
unsafe { c.set(10) };
assert_eq!(unsafe { c.get() }, 10);
}
#[test]
fn test_swap() {
let a = SyncCell::new(1);
let b = SyncCell::new(2);
unsafe { a.swap(&b) };
assert_eq!(unsafe { a.get() }, 2);
assert_eq!(unsafe { b.get() }, 1);
}
#[test]
fn test_replace() {
let c = SyncCell::new(5);
let old = unsafe { c.replace(10) };
assert_eq!(old, 5);
assert_eq!(unsafe { c.get() }, 10);
}
#[test]
fn test_take() {
let c = SyncCell::new(42);
let val = unsafe { c.take() };
assert_eq!(val, 42);
assert_eq!(unsafe { c.get() }, 0);
}
#[test]
fn test_get_mut() {
let mut c = SyncCell::new(3);
*c.get_mut() = 7;
assert_eq!(unsafe { c.get() }, 7);
}
#[test]
fn test_as_ptr() {
let c = SyncCell::new(99);
let ptr = c.as_ptr();
assert_eq!(unsafe { *ptr }, 99);
}
#[test]
fn test_from_mut() {
let mut val = 10;
let c = SyncCell::from_mut(&mut val);
unsafe { c.set(20) };
assert_eq!(val, 20);
}
#[test]
fn test_default() {
let c: SyncCell<i32> = SyncCell::default();
assert_eq!(unsafe { c.get() }, 0);
}
#[test]
fn test_from() {
let c: SyncCell<i32> = SyncCell::from(42);
assert_eq!(unsafe { c.get() }, 42);
}
#[test]
fn test_debug() {
let c = SyncCell::new(42);
assert_eq!(format!("{:?}", c), "SyncCell { .. }");
}
#[test]
fn test_as_slice_of_cells() {
let mut v = [1, 2, 3];
let sync_slice = v.as_sync_slice();
assert_eq!(sync_slice.len(), 3);
assert_eq!(unsafe { sync_slice[0].get() }, 1);
assert_eq!(unsafe { sync_slice[1].get() }, 2);
assert_eq!(unsafe { sync_slice[2].get() }, 3);
}
#[test]
fn test_sync_slice_mutation() {
let mut v = vec![0; 4];
let sync_slice = v.as_sync_slice();
std::thread::scope(|scope| {
for (i, cell) in sync_slice.iter().enumerate() {
scope.spawn(move || {
unsafe { cell.set(i * 10) };
});
}
});
assert_eq!(v, vec![0, 10, 20, 30]);
}
}