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, if Cell<T> has the same in-memory representation as its inner type T, then SyncCell<T> has the same in-memory representation as its inner type T (but the code does not rely on this). 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 that of 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.

§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.

§Examples

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

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

let mut 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 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.

Source

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

Swaps the values of two SyncCells by delegation to 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.

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.

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.

Source§

impl<T: ?Sized> SyncCell<T>

Source

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

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

§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.

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.

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: 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> 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.