From 63d1c8623000782ebc1bb6ae662a10af9f067cab Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Wed, 1 Jun 2022 11:36:20 -0700 Subject: [PATCH 1/2] [core] add Exclusive to sync --- library/core/src/sync/exclusive.rs | 173 +++++++++++++++++++++++++++++ library/core/src/sync/mod.rs | 3 + library/std/src/lib.rs | 1 + library/std/src/sync/mod.rs | 2 + 4 files changed, 179 insertions(+) create mode 100644 library/core/src/sync/exclusive.rs diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs new file mode 100644 index 00000000000..6ada07fd576 --- /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: T) {} +/// struct State { +/// 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: T) {} +/// struct State { +/// future: Exclusive +/// } +/// +/// 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 = "none")] +#[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 { + inner: T, +} + +// See `Exclusive`'s docs for justification. +#[unstable(feature = "exclusive_wrapper", issue = "none")] +unsafe impl Sync for Exclusive {} + +#[unstable(feature = "exclusive_wrapper", issue = "none")] +impl fmt::Debug for Exclusive { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_struct("Exclusive").finish_non_exhaustive() + } +} + +impl Exclusive { + /// Wrap a value in an `Exclusive` + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn new(t: T) -> Self { + Self { inner: t } + } + + /// Unwrap the value contained in the `Exclusive` + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn into_inner(self) -> T { + self.inner + } +} + +impl Exclusive { + /// Get exclusive access to the underlying value. + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[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 = "none")] + #[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` from + /// a _mutable_ reference to a `T`. This allows you to skip + /// building an `Exclusive` with [`Exclusive::new`]. + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn from_mut(r: &'_ mut T) -> &'_ mut Exclusive { + // 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) } + } + + /// Build a _pinned mutable_ references to an `Exclusive` from + /// a _pinned mutable_ reference to a `T`. This allows you to skip + /// building an `Exclusive` with [`Exclusive::new`]. + #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[must_use] + pub const fn from_pin_mut(r: Pin<&'_ mut T>) -> Pin<&'_ mut Exclusive> { + // 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 = "none")] +impl From for Exclusive { + fn from(t: T) -> Self { + Self::new(t) + } +} + +#[unstable(feature = "exclusive_wrapper", issue = "none")] +impl Future for Exclusive { + type Output = T::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.get_pin_mut().poll(cx) + } +} diff --git a/library/core/src/sync/mod.rs b/library/core/src/sync/mod.rs index b635bae0a47..88d87e8ac40 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 = "none")] +pub use exclusive::Exclusive; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 71ea5f1a1f0..f22264c2bf1 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -269,6 +269,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 87d01daeafc..c70dcf7f66c 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 = "none")] +pub use core::sync::Exclusive; #[stable(feature = "rust1", since = "1.0.0")] pub use self::barrier::{Barrier, BarrierWaitResult}; From 029f9aa3bf2aa25a88df76fb34f332981ebfef7d Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Thu, 23 Jun 2022 08:52:13 -0700 Subject: [PATCH 2/2] add tracking issue for exclusive --- library/core/src/sync/exclusive.rs | 22 +++++++++++----------- library/core/src/sync/mod.rs | 2 +- library/std/src/sync/mod.rs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs index 6ada07fd576..a7519ab5ab6 100644 --- a/library/core/src/sync/exclusive.rs +++ b/library/core/src/sync/exclusive.rs @@ -72,7 +72,7 @@ /// /// /// [`Sync`]: core::marker::Sync -#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[unstable(feature = "exclusive_wrapper", issue = "98407")] #[doc(alias = "SyncWrapper")] #[doc(alias = "SyncCell")] #[doc(alias = "Unique")] @@ -86,10 +86,10 @@ pub struct Exclusive { } // See `Exclusive`'s docs for justification. -#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[unstable(feature = "exclusive_wrapper", issue = "98407")] unsafe impl Sync for Exclusive {} -#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[unstable(feature = "exclusive_wrapper", issue = "98407")] impl fmt::Debug for Exclusive { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("Exclusive").finish_non_exhaustive() @@ -98,14 +98,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { impl Exclusive { /// Wrap a value in an `Exclusive` - #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[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 = "none")] + #[unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] pub const fn into_inner(self) -> T { self.inner @@ -114,7 +114,7 @@ pub const fn into_inner(self) -> T { impl Exclusive { /// Get exclusive access to the underlying value. - #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] pub const fn get_mut(&mut self) -> &mut T { &mut self.inner @@ -126,7 +126,7 @@ pub const fn get_mut(&mut self) -> &mut T { /// 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 = "none")] + #[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 @@ -137,7 +137,7 @@ pub const fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { /// Build a _mutable_ references to an `Exclusive` from /// a _mutable_ reference to a `T`. This allows you to skip /// building an `Exclusive` with [`Exclusive::new`]. - #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] pub const fn from_mut(r: &'_ mut T) -> &'_ mut Exclusive { // SAFETY: repr is ≥ C, so refs have the same layout; and `Exclusive` properties are `&mut`-agnostic @@ -147,7 +147,7 @@ pub const fn from_mut(r: &'_ mut T) -> &'_ mut Exclusive { /// Build a _pinned mutable_ references to an `Exclusive` from /// a _pinned mutable_ reference to a `T`. This allows you to skip /// building an `Exclusive` with [`Exclusive::new`]. - #[unstable(feature = "exclusive_wrapper", issue = "none")] + #[unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] pub const fn from_pin_mut(r: Pin<&'_ mut T>) -> Pin<&'_ mut Exclusive> { // SAFETY: `Exclusive` can only produce `&mut T` if itself is unpinned @@ -156,14 +156,14 @@ pub const fn from_pin_mut(r: Pin<&'_ mut T>) -> Pin<&'_ mut Exclusive> { } } -#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[unstable(feature = "exclusive_wrapper", issue = "98407")] impl From for Exclusive { fn from(t: T) -> Self { Self::new(t) } } -#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[unstable(feature = "exclusive_wrapper", issue = "98407")] impl Future for Exclusive { type Output = T::Output; diff --git a/library/core/src/sync/mod.rs b/library/core/src/sync/mod.rs index 88d87e8ac40..4365e4cb250 100644 --- a/library/core/src/sync/mod.rs +++ b/library/core/src/sync/mod.rs @@ -4,5 +4,5 @@ pub mod atomic; mod exclusive; -#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[unstable(feature = "exclusive_wrapper", issue = "98407")] pub use exclusive::Exclusive; diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index c70dcf7f66c..0c2ac8e10ce 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -155,7 +155,7 @@ pub use alloc_crate::sync::{Arc, Weak}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::sync::atomic; -#[unstable(feature = "exclusive_wrapper", issue = "none")] +#[unstable(feature = "exclusive_wrapper", issue = "98407")] pub use core::sync::Exclusive; #[stable(feature = "rust1", since = "1.0.0")]