Skip to main content

commonware_utils/sync/
exclusive.rs

1//! A wrapper that is [`Sync`] for any value by granting only exclusive access to it.
2
3use core::fmt;
4
5/// A value reachable only through exclusive access, which makes the wrapper [`Sync`] whatever
6/// the value is.
7///
8/// Every accessor takes `&mut self` or `self`, so a shared reference to the wrapper cannot reach
9/// the value. That is all `Sync` requires, which lets a value that is `Send` but not `Sync`,
10/// such as a boxed future, live in a type that must be `Sync`.
11pub struct Exclusive<T: ?Sized> {
12    inner: T,
13}
14
15// SAFETY: The value is reachable only through `&mut Exclusive<T>` or by consuming the wrapper,
16// never through `&Exclusive<T>`, so sharing the wrapper between threads cannot share the value.
17unsafe impl<T: ?Sized> Sync for Exclusive<T> {}
18
19impl<T> Exclusive<T> {
20    /// Wrap `inner`.
21    pub const fn new(inner: T) -> Self {
22        Self { inner }
23    }
24
25    /// Unwrap the value.
26    pub fn into_inner(self) -> T {
27        self.inner
28    }
29}
30
31impl<T: ?Sized> Exclusive<T> {
32    /// Access the value, which requires exclusive access to the wrapper.
33    pub const fn get_mut(&mut self) -> &mut T {
34        &mut self.inner
35    }
36}
37
38impl<T: ?Sized> fmt::Debug for Exclusive<T> {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.debug_struct("Exclusive").finish_non_exhaustive()
41    }
42}
43
44#[cfg(test)]
45#[commonware_macros::test_group("miri")]
46mod tests {
47    use super::Exclusive;
48    use std::cell::Cell;
49
50    #[test]
51    fn test_exclusive_is_sync_and_grants_exclusive_access() {
52        fn assert_sync<T: Sync>(_: &T) {}
53
54        // Cell is Send but not Sync; the wrapper is both, so it can be shared across threads.
55        let mut exclusive = Exclusive::new(Cell::new(1u32));
56        assert_sync(&exclusive);
57        std::thread::scope(|scope| {
58            let shared = &exclusive;
59            scope.spawn(move || {
60                assert_eq!(format!("{shared:?}"), "Exclusive { .. }");
61            });
62        });
63
64        exclusive.get_mut().set(2);
65        assert_eq!(exclusive.into_inner().into_inner(), 2);
66    }
67}