Skip to main content

sync_cell_slice/
lib.rs

1/*
2 * SPDX-FileCopyrightText: 2024 Sebastiano Vigna
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7#![doc = include_str!("../README.md")]
8#![no_std]
9
10#[cfg(test)]
11#[macro_use]
12extern crate std;
13
14use core::cell::Cell;
15use core::fmt;
16use core::ptr;
17
18/// A mutable memory location that is [`Sync`].
19///
20/// # Memory layout
21///
22/// `SyncCell<T>` has the same memory layout and caveats as [`Cell<T>`], but it
23/// is [`Sync`] if `T` is. In particular, since [`Cell<T>`] has the same
24/// in-memory representation as its inner type `T`, `SyncCell<T>`, too, has the
25/// same in-memory representation as its inner type `T`. `SyncCell<T>` is also
26/// [`Send`] if [`Cell<T>`] is [`Send`].
27///
28/// `SyncCell<T>` is useful when you need to share a mutable memory location
29/// across threads, and you rely on the fact that the intended behavior will not
30/// cause data races. For example, the content will be written once and then
31/// read many times, in this order.
32///
33/// The main goal of `SyncCell<T>` is to make it possible to write to
34/// different locations of a slice in parallel, leaving the control of data
35/// races to the user, without the access cost of an atomic variable. For this
36/// purpose, `SyncCell` implements the [`as_slice_of_cells`] method, which
37/// turns a `&SyncCell<[T]>` into a `&[SyncCell<T>]`, similar to the [analogous
38/// method of `Cell`].
39///
40/// Since this is the most common usage, the extension trait [`SyncSlice`] adds
41/// to slices a method [`as_sync_slice`] that turns a `&mut [T]` into a
42/// `&[SyncCell<T>]`.
43///
44/// # Methods
45///
46/// `SyncCell` painstakingly reimplements the methods of [`Cell`] as unsafe,
47/// since they rely on external synchronization mechanisms to avoid undefined
48/// behavior.
49///
50/// `SyncCell` implements also a few traits implemented by [`Cell`] by
51/// delegation for convenience, but some, such as [`Clone`] or [`PartialOrd`],
52/// cannot be implemented because they would use unsafe methods. The [`Debug`]
53/// implementation is opaque, as reading the contained value would require
54/// external synchronization.
55///
56/// # Safety
57///
58/// Multiple threads can read from and write to the same `SyncCell` at the same
59/// time. It is the responsibility of the user to ensure that there are no data
60/// races, which would cause undefined behavior.
61///
62/// Moreover, the methods that move or copy values in or out of a cell
63/// ([`get`], [`set`], [`swap`], [`replace`], and [`take`]) can transfer a
64/// value of type `T` to a different thread: if `T` is not [`Send`], the
65/// caller must ensure that such values are not moved to, copied to, or
66/// dropped by, a thread different from the thread owning them.
67///
68/// # Examples
69///
70/// In this example, you can see that `SyncCell` enables mutation across
71/// threads:
72///
73/// ```
74/// use sync_cell_slice::SyncCell;
75/// use sync_cell_slice::SyncSlice;
76///
77/// let x = 0;
78/// let c = SyncCell::new(x);
79///
80/// let mut v = vec![1, 2, 3, 4];
81/// let s = v.as_sync_slice();
82///
83/// std::thread::scope(|scope| {
84///     scope.spawn(|| {
85///         // You can use interior mutability in another thread
86///         unsafe { c.set(5) };
87///     });
88///
89///     scope.spawn(|| {
90///         // You can use interior mutability in another thread
91///         unsafe { s[0].set(5) };
92///     });
93///     scope.spawn(|| {
94///         // You can use interior mutability in another thread
95///         // on the same slice
96///         unsafe { s[1].set(10) };
97///     });
98/// });
99/// ```
100///
101/// In this example, we invert a permutation in parallel:
102///
103/// ```
104/// use sync_cell_slice::SyncCell;
105/// use sync_cell_slice::SyncSlice;
106///
107/// let mut perm = vec![0, 2, 3, 1];
108/// let mut inv = vec![0; perm.len()];
109/// let inv_sync = inv.as_sync_slice();
110///
111/// std::thread::scope(|scope| {
112///     scope.spawn(|| { // Invert first half
113///         for i in 0..2 {
114///             unsafe { inv_sync[perm[i]].set(i) };
115///         }
116///     });
117///
118///     scope.spawn(|| { // Invert second half
119///         for i in 2..perm.len() {
120///             unsafe { inv_sync[perm[i]].set(i) };
121///         }
122///     });
123/// });
124///
125/// assert_eq!(inv, vec![0, 3, 1, 2]);
126/// ```
127///
128/// [`as_slice_of_cells`]: SyncCell::as_slice_of_cells
129/// [analogous method of `Cell`]: Cell::as_slice_of_cells
130/// [`as_sync_slice`]: SyncSlice::as_sync_slice
131/// [`get`]: SyncCell::get
132/// [`set`]: SyncCell::set
133/// [`swap`]: SyncCell::swap
134/// [`replace`]: SyncCell::replace
135/// [`take`]: SyncCell::take
136/// [`Debug`]: core::fmt::Debug
137#[repr(transparent)]
138pub struct SyncCell<T: ?Sized>(Cell<T>);
139
140// This impl is equivalent to the automatically derived one, but we make it
141// explicit for clarity: like Cell<T>, SyncCell<T> is Send iff T is Send.
142unsafe impl<T: ?Sized> Send for SyncCell<T> where Cell<T>: Send {}
143// This is where we depart from Cell: SyncCell<T> is Sync if T is Sync.
144unsafe impl<T: ?Sized + Sync> Sync for SyncCell<T> {}
145
146impl<T> SyncCell<T> {
147    /// Creates a new `SyncCell` containing the given value.
148    #[inline]
149    pub const fn new(value: T) -> Self {
150        Self(Cell::new(value))
151    }
152
153    /// Sets the contained value by delegation to [`Cell::set`].
154    ///
155    /// # Safety
156    ///
157    /// Multiple threads can read from and write to the same `SyncCell` at the
158    /// same time. It is the responsibility of the user to ensure that there are no
159    /// data races, which would cause undefined behavior.
160    ///
161    /// Moreover, this method drops the previously contained value: if `T` is
162    /// not [`Send`], the caller must ensure that the calling thread owns that
163    /// value.
164    #[inline]
165    pub unsafe fn set(&self, val: T) {
166        self.0.set(val);
167    }
168
169    /// Swaps the values of two `SyncCell`s by delegation to [`Cell::swap`].
170    ///
171    /// # Panics
172    ///
173    /// This method panics if `self` and `other` are different cells that
174    /// partially overlap (see [`Cell::swap`]).
175    ///
176    /// # Safety
177    ///
178    /// Multiple threads can read from and write to the same `SyncCell` at the
179    /// same time. It is the responsibility of the user to ensure that there are no
180    /// data races, which would cause undefined behavior.
181    ///
182    /// Moreover, this method moves the contained values between cells: if `T`
183    /// is not [`Send`], the caller must ensure that the calling thread owns
184    /// both values.
185    #[inline]
186    pub unsafe fn swap(&self, other: &SyncCell<T>) {
187        self.0.swap(&other.0);
188    }
189
190    /// Replaces the contained value with `val`, and returns the old contained
191    /// value by delegation to [`Cell::replace`].
192    ///
193    /// # Safety
194    ///
195    /// Multiple threads can read from and write to the same `SyncCell` at the
196    /// same time. It is the responsibility of the user to ensure that there are no
197    /// data races, which would cause undefined behavior.
198    ///
199    /// Moreover, this method returns the previously contained value: if `T` is
200    /// not [`Send`], the caller must ensure that the calling thread owns that
201    /// value.
202    #[inline]
203    pub unsafe fn replace(&self, val: T) -> T {
204        self.0.replace(val)
205    }
206
207    /// Unwraps the value, consuming the cell.
208    #[inline]
209    pub fn into_inner(self) -> T {
210        self.0.into_inner()
211    }
212}
213
214impl<T: Copy> SyncCell<T> {
215    /// Returns a copy of the contained value by delegation to [`Cell::get`].
216    ///
217    /// # Safety
218    ///
219    /// Multiple threads can read from and write to the same `SyncCell` at the
220    /// same time. It is the responsibility of the user to ensure that there are no
221    /// data races, which would cause undefined behavior.
222    ///
223    /// Moreover, this method returns a copy of the contained value: if `T` is
224    /// not [`Send`], the caller must ensure that the copy is not used by a
225    /// thread different from the thread owning the original value.
226    #[inline]
227    pub unsafe fn get(&self) -> T {
228        self.0.get()
229    }
230}
231
232impl<T: ?Sized> SyncCell<T> {
233    /// Returns a raw pointer to the underlying data in this cell
234    /// by delegation to [`Cell::as_ptr`].
235    ///
236    /// Dereferencing the returned pointer requires unsafe code: multiple
237    /// threads can read from and write to the same `SyncCell` at the same
238    /// time, and it is the responsibility of the user to ensure that there
239    /// are no data races, which would cause undefined behavior.
240    #[inline]
241    pub const fn as_ptr(&self) -> *mut T {
242        self.0.as_ptr()
243    }
244
245    /// Returns a mutable reference to the underlying data by delegation to
246    /// [`Cell::get_mut`].
247    #[inline]
248    pub fn get_mut(&mut self) -> &mut T {
249        self.0.get_mut()
250    }
251
252    /// Returns a `&SyncCell<T>` from a `&mut T`.
253    #[inline]
254    pub fn from_mut(value: &mut T) -> &Self {
255        // SAFETY: `Cell::from_mut` converts `&mut T` to `&Cell<T>`, and
256        // `SyncCell<T>` has the same memory layout as `Cell<T>` due to
257        // `#[repr(transparent)]`.
258        unsafe { &*(ptr::from_ref(Cell::from_mut(value)) as *const Self) }
259    }
260}
261
262impl<T: Default> SyncCell<T> {
263    /// Takes the value of the cell, leaving [`Default::default`] in its place.
264    ///
265    /// # Safety
266    ///
267    /// Multiple threads can read from and write to the same `SyncCell` at the
268    /// same time. It is the responsibility of the user to ensure that there are no
269    /// data races, which would cause undefined behavior.
270    ///
271    /// Moreover, this method returns the previously contained value: if `T` is
272    /// not [`Send`], the caller must ensure that the calling thread owns that
273    /// value.
274    #[inline]
275    pub unsafe fn take(&self) -> T {
276        self.0.take()
277    }
278}
279
280impl<T> SyncCell<[T]> {
281    /// Returns a `&[SyncCell<T>]` from a `&SyncCell<[T]>`.
282    #[inline]
283    pub fn as_slice_of_cells(&self) -> &[SyncCell<T>] {
284        let slice_of_cells = self.0.as_slice_of_cells();
285        // SAFETY: `SyncCell<T>` has the same memory layout as `Cell<T>`
286        // due to `#[repr(transparent)]`.
287        unsafe { &*(ptr::from_ref(slice_of_cells) as *const [SyncCell<T>]) }
288    }
289}
290
291impl<T: Default> Default for SyncCell<T> {
292    /// Creates a `SyncCell<T>`, with the `Default` value for `T`.
293    #[inline]
294    fn default() -> SyncCell<T> {
295        SyncCell::new(Default::default())
296    }
297}
298
299impl<T> From<T> for SyncCell<T> {
300    /// Creates a new `SyncCell` containing the given value.
301    #[inline]
302    fn from(value: T) -> SyncCell<T> {
303        SyncCell::new(value)
304    }
305}
306
307impl<T: ?Sized> fmt::Debug for SyncCell<T> {
308    /// Formats opaquely, as reading the contained value would require
309    /// external synchronization.
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        f.debug_struct("SyncCell").finish_non_exhaustive()
312    }
313}
314
315/// Extension trait turning a `&mut [T]` into a `&[SyncCell<T>]`.
316///
317/// The result is [`Sync`] if `T` is [`Sync`].
318pub trait SyncSlice<T> {
319    /// Returns a `&[SyncCell<T>]` from a `&mut [T]`.
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// use sync_cell_slice::SyncSlice;
325    ///
326    /// let mut v = vec![1, 2, 3, 4];
327    /// // s can be used to write to v from multiple threads
328    /// let s = v.as_sync_slice();
329    ///
330    /// std::thread::scope(|scope| {
331    ///     scope.spawn(|| {
332    ///         unsafe { s[0].set(5) };
333    ///     });
334    ///     scope.spawn(|| {
335    ///         unsafe { s[1].set(10) };
336    ///     });
337    /// });
338    /// ```
339    fn as_sync_slice(&mut self) -> &[SyncCell<T>];
340}
341
342impl<T> SyncSlice<T> for [T] {
343    fn as_sync_slice(&mut self) -> &[SyncCell<T>] {
344        SyncCell::from_mut(self).as_slice_of_cells()
345    }
346}
347
348#[cfg(test)]
349mod tests {
350    use super::*;
351    use static_assertions::{assert_impl_all, assert_not_impl_any};
352    use std::rc::Rc;
353
354    // The whole point of this crate is the auto-trait surface: pin it down
355    // at compile time so that changes to the unsafe impls cannot slip by.
356    assert_impl_all!(SyncCell<i32>: Send, Sync);
357    assert_impl_all!(SyncCell<[i32]>: Send, Sync);
358    assert_impl_all!(SyncCell<Cell<u8>>: Send);
359    assert_not_impl_any!(SyncCell<Cell<u8>>: Sync);
360    assert_not_impl_any!(SyncCell<Rc<u8>>: Send, Sync);
361
362    #[test]
363    fn test_new_and_into_inner() {
364        let c = SyncCell::new(42);
365        assert_eq!(c.into_inner(), 42);
366    }
367
368    #[test]
369    fn test_set_and_get() {
370        let c = SyncCell::new(0);
371        unsafe { c.set(10) };
372        assert_eq!(unsafe { c.get() }, 10);
373    }
374
375    #[test]
376    fn test_swap() {
377        let a = SyncCell::new(1);
378        let b = SyncCell::new(2);
379        unsafe { a.swap(&b) };
380        assert_eq!(unsafe { a.get() }, 2);
381        assert_eq!(unsafe { b.get() }, 1);
382    }
383
384    #[test]
385    fn test_replace() {
386        let c = SyncCell::new(5);
387        let old = unsafe { c.replace(10) };
388        assert_eq!(old, 5);
389        assert_eq!(unsafe { c.get() }, 10);
390    }
391
392    #[test]
393    fn test_take() {
394        let c = SyncCell::new(42);
395        let val = unsafe { c.take() };
396        assert_eq!(val, 42);
397        assert_eq!(unsafe { c.get() }, 0);
398    }
399
400    #[test]
401    fn test_get_mut() {
402        let mut c = SyncCell::new(3);
403        *c.get_mut() = 7;
404        assert_eq!(unsafe { c.get() }, 7);
405    }
406
407    #[test]
408    fn test_as_ptr() {
409        let c = SyncCell::new(99);
410        let ptr = c.as_ptr();
411        assert_eq!(unsafe { *ptr }, 99);
412    }
413
414    #[test]
415    fn test_from_mut() {
416        let mut val = 10;
417        let c = SyncCell::from_mut(&mut val);
418        unsafe { c.set(20) };
419        assert_eq!(val, 20);
420    }
421
422    #[test]
423    fn test_default() {
424        let c: SyncCell<i32> = SyncCell::default();
425        assert_eq!(unsafe { c.get() }, 0);
426    }
427
428    #[test]
429    fn test_from() {
430        let c: SyncCell<i32> = SyncCell::from(42);
431        assert_eq!(unsafe { c.get() }, 42);
432    }
433
434    #[test]
435    fn test_debug() {
436        let c = SyncCell::new(42);
437        assert_eq!(format!("{:?}", c), "SyncCell { .. }");
438    }
439
440    #[test]
441    fn test_as_slice_of_cells() {
442        let mut v = [1, 2, 3];
443        let sync_slice = v.as_sync_slice();
444        assert_eq!(sync_slice.len(), 3);
445        assert_eq!(unsafe { sync_slice[0].get() }, 1);
446        assert_eq!(unsafe { sync_slice[1].get() }, 2);
447        assert_eq!(unsafe { sync_slice[2].get() }, 3);
448    }
449
450    #[test]
451    fn test_sync_slice_mutation() {
452        let mut v = vec![0; 4];
453        let sync_slice = v.as_sync_slice();
454
455        std::thread::scope(|scope| {
456            for (i, cell) in sync_slice.iter().enumerate() {
457                scope.spawn(move || {
458                    unsafe { cell.set(i * 10) };
459                });
460            }
461        });
462
463        assert_eq!(v, vec![0, 10, 20, 30]);
464    }
465}