sync_cell_slice/lib.rs
1/*
2 * SPDX-FileCopyrightText: 2024 Sebastiano Vigna
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
5 */
6
7#![doc = include_str!("../README.md")]
8
9use std::cell::Cell;
10
11/// A mutable memory location that is [`Sync`].
12///
13/// # Memory layout
14///
15/// `SyncCell<T>` has the same memory layout and caveats as [`Cell<T>`], but it
16/// is [`Sync`] if `T` is. In particular, if [`Cell<T>`] has the same in-memory
17/// representation as its inner type `T`, then `SyncCell<T>` has the same
18/// in-memory representation as its inner type `T` (but the code does not rely
19/// on this). `SyncCell<T>` is also [`Send`] if [`Cell<T>`] is [`Send`].
20///
21/// `SyncCell<T>` is useful when you need to share a mutable memory location
22/// across threads, and you rely on the fact that the intended behavior will not
23/// cause data races. For example, the content will be written once and then
24/// read many times, in this order.
25///
26/// The main goal of `SyncCell<T>` is that of make it possible to write to
27/// different locations of a slice in parallel, leaving the control of data
28/// races to the user, without the access cost of an atomic variable. For this
29/// purpose, `SyncCell` implements the
30/// [`as_slice_of_cells`](SyncCell::as_slice_of_cells) method, which turns a
31/// `&SyncCell<[T]>` into a `&[SyncCell<T>]`, similar to the [analogous method
32/// of `Cell`](Cell::as_slice_of_cells).
33///
34/// Since this is the most common usage, the extension trait [`SyncSlice`] adds
35/// to slices a method [`as_sync_slice`](SyncSlice::as_sync_slice) that turns a
36/// `&mut [T]` into a `&[SyncCell<T>]`.
37///
38/// # Methods
39///
40/// `SyncCell` painstakingly reimplements the methods of [`Cell`] as unsafe,
41/// since they rely on external synchronization mechanisms to avoid undefined
42/// behavior.
43///
44/// `SyncCell` implements also a few traits implemented by [`Cell`] by
45/// delegation for convenience, but some, such as [`Clone`] or [`PartialOrd`],
46/// cannot be implemented because they would use unsafe methods.
47///
48/// # Safety
49///
50/// Multiple threads can read from and write to the same `SyncCell` at the same
51/// time. It is the responsibility of the user to ensure that there are no data
52/// races, which would cause undefined behavior.
53///
54/// # Examples
55///
56/// In this example, you can see that `SyncCell` enables mutation across
57/// threads:
58///
59/// ```
60/// use sync_cell_slice::SyncCell;
61/// use sync_cell_slice::SyncSlice;
62///
63/// let mut x = 0;
64/// let c = SyncCell::new(x);
65///
66/// let mut v = vec![1, 2, 3, 4];
67/// let s = v.as_sync_slice();
68///
69/// std::thread::scope(|scope| {
70/// scope.spawn(|| {
71/// // You can use interior mutability in another thread
72/// unsafe { c.set(5) };
73/// });
74///
75/// scope.spawn(|| {
76/// // You can use interior mutability in another thread
77/// unsafe { s[0].set(5) };
78/// });
79/// scope.spawn(|| {
80/// // You can use interior mutability in another thread
81/// // on the same slice
82/// unsafe { s[1].set(10) };
83/// });
84/// });
85/// ```
86///
87/// In this example, we invert a permutation in parallel:
88///
89/// ```
90/// use sync_cell_slice::SyncCell;
91/// use sync_cell_slice::SyncSlice;
92///
93/// let mut perm = vec![0, 2, 3, 1];
94/// let mut inv = vec![0; perm.len()];
95/// let inv_sync = inv.as_sync_slice();
96///
97/// std::thread::scope(|scope| {
98/// scope.spawn(|| { // Invert first half
99/// for i in 0..2 {
100/// unsafe { inv_sync[perm[i]].set(i) };
101/// }
102/// });
103///
104/// scope.spawn(|| { // Invert second half
105/// for i in 2..perm.len() {
106/// unsafe { inv_sync[perm[i]].set(i) };
107/// }
108/// });
109/// });
110///
111/// assert_eq!(inv, vec![0, 3, 1, 2]);
112
113#[repr(transparent)]
114pub struct SyncCell<T: ?Sized>(Cell<T>);
115
116// This is where we depart from Cell.
117unsafe impl<T: ?Sized> Send for SyncCell<T> where Cell<T>: Send {}
118unsafe impl<T: ?Sized + Sync> Sync for SyncCell<T> {}
119
120impl<T: Default> Default for SyncCell<T> {
121 /// Creates a `SyncCell<T>`, with the `Default` value for `T`.
122 #[inline]
123 fn default() -> SyncCell<T> {
124 SyncCell::new(Default::default())
125 }
126}
127
128impl<T> From<T> for SyncCell<T> {
129 /// Creates a new `SyncCell` containing the given value.
130 fn from(value: T) -> SyncCell<T> {
131 SyncCell::new(value)
132 }
133}
134
135impl<T> SyncCell<T> {
136 /// Creates a new `SyncCell` containing the given value.
137 #[inline]
138 pub fn new(value: T) -> Self {
139 Self(Cell::new(value))
140 }
141
142 /// Sets the contained value by delegation to [`Cell::set`].
143 ///
144 /// # Safety
145 ///
146 /// Multiple threads can read from and write to the same `SyncCell` at the
147 /// same time. It is the responsibility of the user to ensure that there are no
148 /// data races, which would cause undefined behavior.
149 #[inline]
150 pub unsafe fn set(&self, val: T) {
151 self.0.set(val);
152 }
153
154 /// Swaps the values of two `SyncCell`s by delegation to [`Cell::swap`].
155 ///
156 /// # Safety
157 ///
158 /// Multiple threads can read from and write to the same `SyncCell` at the
159 /// same time. It is the responsibility of the user to ensure that there are no
160 /// data races, which would cause undefined behavior.
161 #[inline]
162 pub unsafe fn swap(&self, other: &SyncCell<T>) {
163 self.0.swap(&other.0);
164 }
165
166 /// Replaces the contained value with `val`, and returns the old contained
167 /// value by delegation to [`Cell::replace`].
168 ///
169 /// # Safety
170 ///
171 /// Multiple threads can read from and write to the same `SyncCell` at the
172 /// same time. It is the responsibility of the user to ensure that there are no
173 /// data races, which would cause undefined behavior.
174 #[inline]
175 pub unsafe fn replace(&self, val: T) -> T {
176 self.0.replace(val)
177 }
178
179 /// Unwraps the value, consuming the cell.
180 #[inline]
181 pub fn into_inner(self) -> T {
182 self.0.into_inner()
183 }
184}
185
186impl<T: Copy> SyncCell<T> {
187 /// Returns a copy of the contained value by delegation to [`Cell::get`].
188 ///
189 /// # Safety
190 ///
191 /// Multiple threads can read from and write to the same `SyncCell` at the
192 /// same time. It is the responsibility of the user to ensure that there are no
193 /// data races, which would cause undefined behavior.
194 #[inline]
195 pub unsafe fn get(&self) -> T {
196 self.0.get()
197 }
198}
199
200impl<T: ?Sized> SyncCell<T> {
201 /// Returns a raw pointer to the underlying data in this cell
202 /// by delegation to [`Cell::as_ptr`].
203 ///
204 /// # Safety
205 ///
206 /// Multiple threads can read from and write to the same `SyncCell` at the
207 /// same time. It is the responsibility of the user to ensure that there are no
208 /// data races, which would cause undefined behavior.
209 #[inline]
210 pub const unsafe fn as_ptr(&self) -> *mut T {
211 self.0.as_ptr()
212 }
213
214 /// Returns a mutable reference to the underlying data by delegation to
215 /// [`Cell::get_mut`].
216 #[inline]
217 pub fn get_mut(&mut self) -> &mut T {
218 self.0.get_mut()
219 }
220
221 /// Returns a `&SyncCell<T>` from a `&mut T`.
222 #[allow(trivial_casts)]
223 #[inline]
224 pub fn from_mut(value: &mut T) -> &Self {
225 // SAFETY: `SyncCell<T>` has the same memory layout as `Cell<T>`.
226 unsafe { &*(Cell::from_mut(value) as *const Cell<T> as *const Self) }
227 }
228}
229
230impl<T: Default> SyncCell<T> {
231 /// Takes the value of the cell, leaving [`Default::default`] in its place.
232 ///
233 /// # Safety
234 ///
235 /// Multiple threads can read from and write to the same `SyncCell` at the
236 /// same time. It is the responsibility of the user to ensure that there are no
237 /// data races, which would cause undefined behavior.
238 #[inline]
239 pub unsafe fn take(&self) -> T {
240 self.0.replace(Default::default())
241 }
242}
243
244#[allow(trivial_casts)]
245impl<T> SyncCell<[T]> {
246 /// Returns a `&[SyncCell<T>]` from a `&SyncCell<[T]>`.
247 #[inline]
248 pub fn as_slice_of_cells(&self) -> &[SyncCell<T>] {
249 let slice_of_cells = self.0.as_slice_of_cells();
250 // SAFETY: `SyncCell<T>` has the same memory layout as `Cell<T>`
251 unsafe { &*(slice_of_cells as *const [Cell<T>] as *const [SyncCell<T>]) }
252 }
253}
254
255/// Extension trait turning a `&mut [T]` into a `&[SyncCell<T>]`.
256///
257/// The result [`Sync`] if `T` is [`Sync`].
258pub trait SyncSlice<T> {
259 /// Returns a `&[SyncCell<T>]` from a `&mut [T]`.
260 ///
261 /// # Examples
262 ///
263 /// ```
264 /// use sync_cell_slice::SyncSlice;
265 ///
266 /// let mut v = vec![1, 2, 3, 4];
267 /// // s can be used to write to v from multiple threads
268 /// let s = v.as_sync_slice();
269 ///
270 /// std::thread::scope(|scope| {
271 /// scope.spawn(|| {
272 /// unsafe { s[0].set(5) };
273 /// });
274 /// scope.spawn(|| {
275 /// unsafe { s[1].set(10) };
276 /// });
277 /// });
278 /// ```
279 fn as_sync_slice(&mut self) -> &[SyncCell<T>];
280}
281
282impl<T> SyncSlice<T> for [T] {
283 fn as_sync_slice(&mut self) -> &[SyncCell<T>] {
284 SyncCell::from_mut(self).as_slice_of_cells()
285 }
286}