Skip to content

Commit 0e71d1f

Browse files
authored
Rollup merge of #97629 - guswynn:exclusive_struct, r=m-ou-se
[core] add `Exclusive` to sync (discussed here: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Adding.20.60SyncWrapper.60.20to.20std) `Exclusive` is a wrapper that exclusively allows mutable access to the inner value if you have exclusive access to the wrapper. It acts like a compile time mutex, and hold an unconditional `Sync` implementation. ## Justification for inclusion into std - This wrapper unblocks actual problems: - The example that I hit was a vector of `futures::future::BoxFuture`'s causing a central struct in a script to be non-`Sync`. To work around it, you either write really difficult code, or wrap the futures in a needless mutex. - Easy to maintain: this struct is as simple as a wrapper can get, and its `Sync` implementation has very clear reasoning - Fills a gap: `&/&mut` are to `RwLock` as `Exclusive` is to `Mutex` ## Public Api ```rust // core::sync #[derive(Default)] struct Exclusive<T: ?Sized> { ... } impl<T: ?Sized> Sync for Exclusive {} impl<T> Exclusive<T> { pub const fn new(t: T) -> Self; pub const fn into_inner(self) -> T; } impl<T: ?Sized> Exclusive<T> { pub const fn get_mut(&mut self) -> &mut T; pub const fn get_pin_mut(Pin<&mut self>) -> Pin<&mut T>; pub const fn from_mut(&mut T) -> &mut Exclusive<T>; pub const fn from_pin_mut(Pin<&mut T>) -> Pin<&mut Exclusive<T>>; } impl<T: Future> Future for Exclusive { ... } impl<T> From<T> for Exclusive<T> { ... } impl<T: ?Sized> Debug for Exclusive { ... } ``` ## Naming This is a big bikeshed, but I felt that `Exclusive` captured its general purpose quite well. ## Stability and location As this is so simple, it can be in `core`. I feel that it can be stabilized quite soon after it is merged, if the libs teams feels its reasonable to add. Also, I don't really know how unstable feature work in std/core's codebases, so I might need help fixing them ## Tips for review The docs probably are the thing that needs to be reviewed! I tried my best, but I'm sure people have more experience than me writing docs for `Core` ### Implementation: The API is mostly pulled from https://docs.rs/sync_wrapper/latest/sync_wrapper/struct.SyncWrapper.html (which is apache 2.0 licenesed), and the implementation is trivial: - its an unsafe justification for pinning - its an unsafe justification for the `Sync` impl (mostly reasoned about by ````@danielhenrymantilla```` here: Actyx/sync_wrapper#2) - and forwarding impls, starting with derivable ones and `Future`
2 parents 7425fb2 + 029f9aa commit 0e71d1f

File tree

4 files changed

+179
-0
lines changed

4 files changed

+179
-0
lines changed

library/core/src/sync/exclusive.rs

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
//! Defines [`Exclusive`].
2+
3+
use core::fmt;
4+
use core::future::Future;
5+
use core::pin::Pin;
6+
use core::task::{Context, Poll};
7+
8+
/// `Exclusive` provides only _mutable_ access, also referred to as _exclusive_
9+
/// access to the underlying value. It provides no _immutable_, or _shared_
10+
/// access to the underlying value.
11+
///
12+
/// While this may seem not very useful, it allows `Exclusive` to _unconditionally_
13+
/// implement [`Sync`]. Indeed, the safety requirements of `Sync` state that for `Exclusive`
14+
/// to be `Sync`, it must be sound to _share_ across threads, that is, it must be sound
15+
/// for `&Exclusive` to cross thread boundaries. By design, a `&Exclusive` has no API
16+
/// whatsoever, making it useless, thus harmless, thus memory safe.
17+
///
18+
/// Certain constructs like [`Future`]s can only be used with _exclusive_ access,
19+
/// and are often `Send` but not `Sync`, so `Exclusive` can be used as hint to the
20+
/// rust compiler that something is `Sync` in practice.
21+
///
22+
/// ## Examples
23+
/// Using a non-`Sync` future prevents the wrapping struct from being `Sync`
24+
/// ```compile_fail
25+
/// use core::cell::Cell;
26+
///
27+
/// async fn other() {}
28+
/// fn assert_sync<T: Sync>(t: T) {}
29+
/// struct State<F> {
30+
/// future: F
31+
/// }
32+
///
33+
/// assert_sync(State {
34+
/// future: async {
35+
/// let cell = Cell::new(1);
36+
/// let cell_ref = &cell;
37+
/// other().await;
38+
/// let value = cell_ref.get();
39+
/// }
40+
/// });
41+
/// ```
42+
///
43+
/// `Exclusive` ensures the struct is `Sync` without stripping the future of its
44+
/// functionality.
45+
/// ```
46+
/// #![feature(exclusive_wrapper)]
47+
/// use core::cell::Cell;
48+
/// use core::sync::Exclusive;
49+
///
50+
/// async fn other() {}
51+
/// fn assert_sync<T: Sync>(t: T) {}
52+
/// struct State<F> {
53+
/// future: Exclusive<F>
54+
/// }
55+
///
56+
/// assert_sync(State {
57+
/// future: Exclusive::new(async {
58+
/// let cell = Cell::new(1);
59+
/// let cell_ref = &cell;
60+
/// other().await;
61+
/// let value = cell_ref.get();
62+
/// })
63+
/// });
64+
/// ```
65+
///
66+
/// ## Parallels with a mutex
67+
/// In some sense, `Exclusive` can be thought of as a _compile-time_ version of
68+
/// a mutex, as the borrow-checker guarantees that only one `&mut` can exist
69+
/// for any value. This is a parallel with the fact that
70+
/// `&` and `&mut` references together can be thought of as a _compile-time_
71+
/// version of a read-write lock.
72+
///
73+
///
74+
/// [`Sync`]: core::marker::Sync
75+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
76+
#[doc(alias = "SyncWrapper")]
77+
#[doc(alias = "SyncCell")]
78+
#[doc(alias = "Unique")]
79+
// `Exclusive` can't have `PartialOrd`, `Clone`, etc. impls as they would
80+
// use `&` access to the inner value, violating the `Sync` impl's safety
81+
// requirements.
82+
#[derive(Default)]
83+
#[repr(transparent)]
84+
pub struct Exclusive<T: ?Sized> {
85+
inner: T,
86+
}
87+
88+
// See `Exclusive`'s docs for justification.
89+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
90+
unsafe impl<T: ?Sized> Sync for Exclusive<T> {}
91+
92+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
93+
impl<T: ?Sized> fmt::Debug for Exclusive<T> {
94+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
95+
f.debug_struct("Exclusive").finish_non_exhaustive()
96+
}
97+
}
98+
99+
impl<T: Sized> Exclusive<T> {
100+
/// Wrap a value in an `Exclusive`
101+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
102+
#[must_use]
103+
pub const fn new(t: T) -> Self {
104+
Self { inner: t }
105+
}
106+
107+
/// Unwrap the value contained in the `Exclusive`
108+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
109+
#[must_use]
110+
pub const fn into_inner(self) -> T {
111+
self.inner
112+
}
113+
}
114+
115+
impl<T: ?Sized> Exclusive<T> {
116+
/// Get exclusive access to the underlying value.
117+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
118+
#[must_use]
119+
pub const fn get_mut(&mut self) -> &mut T {
120+
&mut self.inner
121+
}
122+
123+
/// Get pinned exclusive access to the underlying value.
124+
///
125+
/// `Exclusive` is considered to _structurally pin_ the underlying
126+
/// value, which means _unpinned_ `Exclusive`s can produce _unpinned_
127+
/// access to the underlying value, but _pinned_ `Exclusive`s only
128+
/// produce _pinned_ access to the underlying value.
129+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
130+
#[must_use]
131+
pub const fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
132+
// SAFETY: `Exclusive` can only produce `&mut T` if itself is unpinned
133+
// `Pin::map_unchecked_mut` is not const, so we do this conversion manually
134+
unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }
135+
}
136+
137+
/// Build a _mutable_ references to an `Exclusive<T>` from
138+
/// a _mutable_ reference to a `T`. This allows you to skip
139+
/// building an `Exclusive` with [`Exclusive::new`].
140+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
141+
#[must_use]
142+
pub const fn from_mut(r: &'_ mut T) -> &'_ mut Exclusive<T> {
143+
// SAFETY: repr is ≥ C, so refs have the same layout; and `Exclusive` properties are `&mut`-agnostic
144+
unsafe { &mut *(r as *mut T as *mut Exclusive<T>) }
145+
}
146+
147+
/// Build a _pinned mutable_ references to an `Exclusive<T>` from
148+
/// a _pinned mutable_ reference to a `T`. This allows you to skip
149+
/// building an `Exclusive` with [`Exclusive::new`].
150+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
151+
#[must_use]
152+
pub const fn from_pin_mut(r: Pin<&'_ mut T>) -> Pin<&'_ mut Exclusive<T>> {
153+
// SAFETY: `Exclusive` can only produce `&mut T` if itself is unpinned
154+
// `Pin::map_unchecked_mut` is not const, so we do this conversion manually
155+
unsafe { Pin::new_unchecked(Self::from_mut(r.get_unchecked_mut())) }
156+
}
157+
}
158+
159+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
160+
impl<T> From<T> for Exclusive<T> {
161+
fn from(t: T) -> Self {
162+
Self::new(t)
163+
}
164+
}
165+
166+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
167+
impl<T: Future + ?Sized> Future for Exclusive<T> {
168+
type Output = T::Output;
169+
170+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
171+
self.get_pin_mut().poll(cx)
172+
}
173+
}

library/core/src/sync/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
#![stable(feature = "rust1", since = "1.0.0")]
44

55
pub mod atomic;
6+
mod exclusive;
7+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
8+
pub use exclusive::Exclusive;

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
#![feature(duration_checked_float)]
271271
#![feature(duration_constants)]
272272
#![feature(exact_size_is_empty)]
273+
#![feature(exclusive_wrapper)]
273274
#![feature(extend_one)]
274275
#![feature(float_minimum_maximum)]
275276
#![feature(hasher_prefixfree_extras)]

library/std/src/sync/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@
155155
pub use alloc_crate::sync::{Arc, Weak};
156156
#[stable(feature = "rust1", since = "1.0.0")]
157157
pub use core::sync::atomic;
158+
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
159+
pub use core::sync::Exclusive;
158160

159161
#[stable(feature = "rust1", since = "1.0.0")]
160162
pub use self::barrier::{Barrier, BarrierWaitResult};

0 commit comments

Comments
 (0)