about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/sync/exclusive.rs173
-rw-r--r--library/core/src/sync/mod.rs3
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/sync/mod.rs2
4 files changed, 179 insertions, 0 deletions
diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs
new file mode 100644
index 00000000000..a7519ab5ab6
--- /dev/null
+++ b/library/core/src/sync/exclusive.rs
@@ -0,0 +1,173 @@
+//! Defines [`Exclusive`].
+
+use core::fmt;
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+/// `Exclusive` provides only _mutable_ access, also referred to as _exclusive_
+/// access to the underlying value. It provides no _immutable_, or _shared_
+/// access to the underlying value.
+///
+/// While this may seem not very useful, it allows `Exclusive` to _unconditionally_
+/// implement [`Sync`]. Indeed, the safety requirements of `Sync` state that for `Exclusive`
+/// to be `Sync`, it must be sound to _share_ across threads, that is, it must be sound
+/// for `&Exclusive` to cross thread boundaries. By design, a `&Exclusive` has no API
+/// whatsoever, making it useless, thus harmless, thus memory safe.
+///
+/// Certain constructs like [`Future`]s can only be used with _exclusive_ access,
+/// and are often `Send` but not `Sync`, so `Exclusive` can be used as hint to the
+/// rust compiler that something is `Sync` in practice.
+///
+/// ## Examples
+/// Using a non-`Sync` future prevents the wrapping struct from being `Sync`
+/// ```compile_fail
+/// use core::cell::Cell;
+///
+/// async fn other() {}
+/// fn assert_sync<T: Sync>(t: T) {}
+/// struct State<F> {
+///     future: F
+/// }
+///
+/// assert_sync(State {
+///     future: async {
+///         let cell = Cell::new(1);
+///         let cell_ref = &cell;
+///         other().await;
+///         let value = cell_ref.get();
+///     }
+/// });
+/// ```
+///
+/// `Exclusive` ensures the struct is `Sync` without stripping the future of its
+/// functionality.
+/// ```
+/// #![feature(exclusive_wrapper)]
+/// use core::cell::Cell;
+/// use core::sync::Exclusive;
+///
+/// async fn other() {}
+/// fn assert_sync<T: Sync>(t: T) {}
+/// struct State<F> {
+///     future: Exclusive<F>
+/// }
+///
+/// assert_sync(State {
+///     future: Exclusive::new(async {
+///         let cell = Cell::new(1);
+///         let cell_ref = &cell;
+///         other().await;
+///         let value = cell_ref.get();
+///     })
+/// });
+/// ```
+///
+/// ## Parallels with a mutex
+/// In some sense, `Exclusive` can be thought of as a _compile-time_ version of
+/// a mutex, as the borrow-checker guarantees that only one `&mut` can exist
+/// for any value. This is a parallel with the fact that
+/// `&` and `&mut` references together can be thought of as a _compile-time_
+/// version of a read-write lock.
+///
+///
+/// [`Sync`]: core::marker::Sync
+#[unstable(feature = "exclusive_wrapper", issue = "98407")]
+#[doc(alias = "SyncWrapper")]
+#[doc(alias = "SyncCell")]
+#[doc(alias = "Unique")]
+// `Exclusive` can't have `PartialOrd`, `Clone`, etc. impls as they would
+// use `&` access to the inner value, violating the `Sync` impl's safety
+// requirements.
+#[derive(Default)]
+#[repr(transparent)]
+pub struct Exclusive<T: ?Sized> {
+    inner: T,
+}
+
+// See `Exclusive`'s docs for justification.
+#[unstable(feature = "exclusive_wrapper", issue = "98407")]
+unsafe impl<T: ?Sized> Sync for Exclusive<T> {}
+
+#[unstable(feature = "exclusive_wrapper", issue = "98407")]
+impl<T: ?Sized> fmt::Debug for Exclusive<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        f.debug_struct("Exclusive").finish_non_exhaustive()
+    }
+}
+
+impl<T: Sized> Exclusive<T> {
+    /// Wrap a value in an `Exclusive`
+    #[unstable(feature = "exclusive_wrapper", issue = "98407")]
+    #[must_use]
+    pub const fn new(t: T) -> Self {
+        Self { inner: t }
+    }
+
+    /// Unwrap the value contained in the `Exclusive`
+    #[unstable(feature = "exclusive_wrapper", issue = "98407")]
+    #[must_use]
+    pub const fn into_inner(self) -> T {
+        self.inner
+    }
+}
+
+impl<T: ?Sized> Exclusive<T> {
+    /// Get exclusive access to the underlying value.
+    #[unstable(feature = "exclusive_wrapper", issue = "98407")]
+    #[must_use]
+    pub const fn get_mut(&mut self) -> &mut T {
+        &mut self.inner
+    }
+
+    /// Get pinned exclusive access to the underlying value.
+    ///
+    /// `Exclusive` is considered to _structurally pin_ the underlying
+    /// value, which means _unpinned_ `Exclusive`s can produce _unpinned_
+    /// access to the underlying value, but _pinned_ `Exclusive`s only
+    /// produce _pinned_ access to the underlying value.
+    #[unstable(feature = "exclusive_wrapper", issue = "98407")]
+    #[must_use]
+    pub const fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
+        // SAFETY: `Exclusive` can only produce `&mut T` if itself is unpinned
+        // `Pin::map_unchecked_mut` is not const, so we do this conversion manually
+        unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }
+    }
+
+    /// Build a _mutable_ references to an `Exclusive<T>` from
+    /// a _mutable_ reference to a `T`. This allows you to skip
+    /// building an `Exclusive` with [`Exclusive::new`].
+    #[unstable(feature = "exclusive_wrapper", issue = "98407")]
+    #[must_use]
+    pub const fn from_mut(r: &'_ mut T) -> &'_ mut Exclusive<T> {
+        // SAFETY: repr is ≥ C, so refs have the same layout; and `Exclusive` properties are `&mut`-agnostic
+        unsafe { &mut *(r as *mut T as *mut Exclusive<T>) }
+    }
+
+    /// Build a _pinned mutable_ references to an `Exclusive<T>` from
+    /// a _pinned mutable_ reference to a `T`. This allows you to skip
+    /// building an `Exclusive` with [`Exclusive::new`].
+    #[unstable(feature = "exclusive_wrapper", issue = "98407")]
+    #[must_use]
+    pub const fn from_pin_mut(r: Pin<&'_ mut T>) -> Pin<&'_ mut Exclusive<T>> {
+        // SAFETY: `Exclusive` can only produce `&mut T` if itself is unpinned
+        // `Pin::map_unchecked_mut` is not const, so we do this conversion manually
+        unsafe { Pin::new_unchecked(Self::from_mut(r.get_unchecked_mut())) }
+    }
+}
+
+#[unstable(feature = "exclusive_wrapper", issue = "98407")]
+impl<T> From<T> for Exclusive<T> {
+    fn from(t: T) -> Self {
+        Self::new(t)
+    }
+}
+
+#[unstable(feature = "exclusive_wrapper", issue = "98407")]
+impl<T: Future + ?Sized> Future for Exclusive<T> {
+    type Output = T::Output;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        self.get_pin_mut().poll(cx)
+    }
+}
diff --git a/library/core/src/sync/mod.rs b/library/core/src/sync/mod.rs
index b635bae0a47..4365e4cb250 100644
--- a/library/core/src/sync/mod.rs
+++ b/library/core/src/sync/mod.rs
@@ -3,3 +3,6 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 pub mod atomic;
+mod exclusive;
+#[unstable(feature = "exclusive_wrapper", issue = "98407")]
+pub use exclusive::Exclusive;
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 65b8df42996..ba76ee31b42 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -270,6 +270,7 @@
 #![feature(duration_checked_float)]
 #![feature(duration_constants)]
 #![feature(exact_size_is_empty)]
+#![feature(exclusive_wrapper)]
 #![feature(extend_one)]
 #![feature(float_minimum_maximum)]
 #![feature(hasher_prefixfree_extras)]
diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs
index 5fc18fda6a8..7b507a169b3 100644
--- a/library/std/src/sync/mod.rs
+++ b/library/std/src/sync/mod.rs
@@ -155,6 +155,8 @@
 pub use alloc_crate::sync::{Arc, Weak};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::sync::atomic;
+#[unstable(feature = "exclusive_wrapper", issue = "98407")]
+pub use core::sync::Exclusive;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::barrier::{Barrier, BarrierWaitResult};