Skip to main content

SyncCell

Struct SyncCell 

Source
pub struct SyncCell<T: ?Sized>(/* private fields */);
Expand description

A mutable memory location that is Sync.

§Memory layout

SyncCell<T> has the same memory layout and caveats as Cell<T>, but it is Sync if T is. In particular, since Cell<T> has the same in-memory representation as its inner type T, SyncCell<T>, too, has the same in-memory representation as its inner type T. SyncCell<T> is also Send if Cell<T> is Send.

SyncCell<T> is useful when you need to share a mutable memory location across threads, and you rely on the fact that the intended behavior will not cause data races. For example, the content will be written once and then read many times, in this order.

The main goal of SyncCell<T> is to make it possible to write to different locations of a slice in parallel, leaving the control of data races to the user, without the access cost of an atomic variable. For this purpose, SyncCell implements the as_slice_of_cells method, which turns a &SyncCell<[T]> into a &[SyncCell<T>], similar to the analogous method of Cell.

Since this is the most common usage, the extension trait SyncSlice adds to slices a method as_sync_slice that turns a &mut [T] into a &[SyncCell<T>].

§Methods

SyncCell painstakingly reimplements the methods of Cell as unsafe, since they rely on external synchronization mechanisms to avoid undefined behavior.

SyncCell implements also a few traits implemented by Cell by delegation for convenience, but some, such as Clone or PartialOrd, cannot be implemented because they would use unsafe methods. The Debug implementation is opaque, as reading the contained value would require external synchronization.

§Safety

Multiple threads can read from and write to the same SyncCell at the same time. It is the responsibility of the user to ensure that there are no data races, which would cause undefined behavior.

Moreover, the methods that move or copy values in or out of a cell (get, set, swap, replace, and take) can transfer a value of type T to a different thread: if T is not Send, the caller must ensure that such values are not moved to, copied to, or dropped by, a thread different from the thread owning them.

§Examples

In this example, you can see that SyncCell enables mutation across threads:

use sync_cell_slice::SyncCell;
use sync_cell_slice::SyncSlice;

let x = 0;
let c = SyncCell::new(x);

let mut v = vec![1, 2, 3, 4];
let s = v.as_sync_slice();

std::thread::scope(|scope| {
    scope.spawn(|| {
        // You can use interior mutability in another thread
        unsafe { c.set(5) };
    });

    scope.spawn(|| {
        // You can use interior mutability in another thread
        unsafe { s[0].set(5) };
    });
    scope.spawn(|| {
        // You can use interior mutability in another thread
        // on the same slice
        unsafe { s[1].set(10) };
    });
});

In this example, we invert a permutation in parallel:

use sync_cell_slice::SyncCell;
use sync_cell_slice::SyncSlice;

let mut perm = vec![0, 2, 3, 1];
let mut inv = vec![0; perm.len()];
let inv_sync = inv.as_sync_slice();

std::thread::scope(|scope| {
    scope.spawn(|| { // Invert first half
        for i in 0..2 {
            unsafe { inv_sync[perm[i]].set(i) };
        }
    });

    scope.spawn(|| { // Invert second half
        for i in 2..perm.len() {
            unsafe { inv_sync[perm[i]].set(i) };
        }
    });
});

assert_eq!(inv, vec![0, 3, 1, 2]);

Implementations§

Source§

impl<T> SyncCell<T>

Source

pub const fn new(value: T) -> Self

Creates a new SyncCell containing the given value.

Source

pub unsafe fn set(&self, val: T)

Sets the contained value by delegation to Cell::set.

§Safety

Multiple threads can read from and write to the same SyncCell at the same time. It is the responsibility of the user to ensure that there are no data races, which would cause undefined behavior.

Moreover, this method drops the previously contained value: if T is not Send, the caller must ensure that the calling thread owns that value.

Source

pub unsafe fn swap(&self, other: &SyncCell<T>)

Swaps the values of two SyncCells by delegation to Cell::swap.

§Panics

This method panics if self and other are different cells that partially overlap (see Cell::swap).

§Safety

Multiple threads can read from and write to the same SyncCell at the same time. It is the responsibility of the user to ensure that there are no data races, which would cause undefined behavior.

Moreover, this method moves the contained values between cells: if T is not Send, the caller must ensure that the calling thread owns both values.

Source

pub unsafe fn replace(&self, val: T) -> T

Replaces the contained value with val, and returns the old contained value by delegation to Cell::replace.

§Safety

Multiple threads can read from and write to the same SyncCell at the same time. It is the responsibility of the user to ensure that there are no data races, which would cause undefined behavior.

Moreover, this method returns the previously contained value: if T is not Send, the caller must ensure that the calling thread owns that value.

Source

pub fn into_inner(self) -> T

Unwraps the value, consuming the cell.

Source§

impl<T: Copy> SyncCell<T>

Source

pub unsafe fn get(&self) -> T

Returns a copy of the contained value by delegation to Cell::get.

§Safety

Multiple threads can read from and write to the same SyncCell at the same time. It is the responsibility of the user to ensure that there are no data races, which would cause undefined behavior.

Moreover, this method returns a copy of the contained value: if T is not Send, the caller must ensure that the copy is not used by a thread different from the thread owning the original value.

Source§

impl<T: ?Sized> SyncCell<T>

Source

pub const fn as_ptr(&self) -> *mut T

Returns a raw pointer to the underlying data in this cell by delegation to Cell::as_ptr.

Dereferencing the returned pointer requires unsafe code: multiple threads can read from and write to the same SyncCell at the same time, and it is the responsibility of the user to ensure that there are no data races, which would cause undefined behavior.

Source

pub fn get_mut(&mut self) -> &mut T

Returns a mutable reference to the underlying data by delegation to Cell::get_mut.

Source

pub fn from_mut(value: &mut T) -> &Self

Returns a &SyncCell<T> from a &mut T.

Source§

impl<T: Default> SyncCell<T>

Source

pub unsafe fn take(&self) -> T

Takes the value of the cell, leaving Default::default in its place.

§Safety

Multiple threads can read from and write to the same SyncCell at the same time. It is the responsibility of the user to ensure that there are no data races, which would cause undefined behavior.

Moreover, this method returns the previously contained value: if T is not Send, the caller must ensure that the calling thread owns that value.

Source§

impl<T> SyncCell<[T]>

Source

pub fn as_slice_of_cells(&self) -> &[SyncCell<T>]

Returns a &[SyncCell<T>] from a &SyncCell<[T]>.

Trait Implementations§

Source§

impl<T: ?Sized> Debug for SyncCell<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats opaquely, as reading the contained value would require external synchronization.

Source§

impl<T: Default> Default for SyncCell<T>

Source§

fn default() -> SyncCell<T>

Creates a SyncCell<T>, with the Default value for T.

Source§

impl<T> From<T> for SyncCell<T>

Source§

fn from(value: T) -> SyncCell<T>

Creates a new SyncCell containing the given value.

Source§

impl<T: ?Sized> Send for SyncCell<T>
where Cell<T>: Send,

Source§

impl<T: ?Sized + Sync> Sync for SyncCell<T>

Auto Trait Implementations§

§

impl<T> !Freeze for SyncCell<T>

§

impl<T> !RefUnwindSafe for SyncCell<T>

§

impl<T> Unpin for SyncCell<T>
where T: Unpin + ?Sized,

§

impl<T> UnsafeUnpin for SyncCell<T>
where T: UnsafeUnpin + ?Sized,

§

impl<T> UnwindSafe for SyncCell<T>
where T: UnwindSafe + ?Sized,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<!> for T

Source§

fn from(t: !) -> T

Converts to this type from the input type.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.