Skip to content

Commit deeef12

Browse files
committed
Auto merge of #98730 - matthiaskrgr:rollup-2c4d4x5, r=matthiaskrgr
Rollup of 10 pull requests Successful merges: - #97629 ([core] add `Exclusive` to sync) - #98503 (fix data race in thread::scope) - #98670 (llvm-wrapper: adapt for LLVMConstExtractValue removal) - #98671 (Fix source sidebar bugs) - #98677 (For diagnostic information of Boolean, remind it as use the type: 'bool') - #98684 (add test for 72793) - #98688 (interpret: add From<&MplaceTy> for PlaceTy) - #98695 (use "or pattern") - #98709 (Remove unneeded methods declaration for old web browsers) - #98717 (get rid of tidy 'unnecessarily ignored' warnings) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents 45db64d + 05db9d6 commit deeef12

File tree

6 files changed

+196
-10
lines changed

6 files changed

+196
-10
lines changed

core/src/sync/exclusive.rs

Lines changed: 173 additions & 0 deletions
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+
}

core/src/sync/mod.rs

Lines changed: 3 additions & 0 deletions
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;

std/src/lib.rs

Lines changed: 1 addition & 0 deletions
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)]

std/src/sync/mod.rs

Lines changed: 2 additions & 0 deletions
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};

std/src/thread/mod.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ use crate::cell::UnsafeCell;
159159
use crate::ffi::{CStr, CString};
160160
use crate::fmt;
161161
use crate::io;
162+
use crate::marker::PhantomData;
162163
use crate::mem;
163164
use crate::num::NonZeroU64;
164165
use crate::num::NonZeroUsize;
@@ -462,7 +463,7 @@ impl Builder {
462463
unsafe fn spawn_unchecked_<'a, 'scope, F, T>(
463464
self,
464465
f: F,
465-
scope_data: Option<&'scope scoped::ScopeData>,
466+
scope_data: Option<Arc<scoped::ScopeData>>,
466467
) -> io::Result<JoinInner<'scope, T>>
467468
where
468469
F: FnOnce() -> T,
@@ -479,8 +480,11 @@ impl Builder {
479480
}));
480481
let their_thread = my_thread.clone();
481482

482-
let my_packet: Arc<Packet<'scope, T>> =
483-
Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None) });
483+
let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
484+
scope: scope_data,
485+
result: UnsafeCell::new(None),
486+
_marker: PhantomData,
487+
});
484488
let their_packet = my_packet.clone();
485489

486490
let output_capture = crate::io::set_output_capture(None);
@@ -507,7 +511,7 @@ impl Builder {
507511
unsafe { *their_packet.result.get() = Some(try_result) };
508512
};
509513

510-
if let Some(scope_data) = scope_data {
514+
if let Some(scope_data) = &my_packet.scope {
511515
scope_data.increment_num_running_threads();
512516
}
513517

@@ -1298,8 +1302,9 @@ pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
12981302
// An Arc to the packet is stored into a `JoinInner` which in turns is placed
12991303
// in `JoinHandle`.
13001304
struct Packet<'scope, T> {
1301-
scope: Option<&'scope scoped::ScopeData>,
1305+
scope: Option<Arc<scoped::ScopeData>>,
13021306
result: UnsafeCell<Option<Result<T>>>,
1307+
_marker: PhantomData<Option<&'scope scoped::ScopeData>>,
13031308
}
13041309

13051310
// Due to the usage of `UnsafeCell` we need to manually implement Sync.
@@ -1330,7 +1335,7 @@ impl<'scope, T> Drop for Packet<'scope, T> {
13301335
rtabort!("thread result panicked on drop");
13311336
}
13321337
// Book-keeping so the scope knows when it's done.
1333-
if let Some(scope) = self.scope {
1338+
if let Some(scope) = &self.scope {
13341339
// Now that there will be no more user code running on this thread
13351340
// that can use 'scope, mark the thread as 'finished'.
13361341
// It's important we only do this after the `result` has been dropped,

std/src/thread/scoped.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::sync::Arc;
1111
/// See [`scope`] for details.
1212
#[stable(feature = "scoped_threads", since = "1.63.0")]
1313
pub struct Scope<'scope, 'env: 'scope> {
14-
data: ScopeData,
14+
data: Arc<ScopeData>,
1515
/// Invariance over 'scope, to make sure 'scope cannot shrink,
1616
/// which is necessary for soundness.
1717
///
@@ -130,12 +130,14 @@ pub fn scope<'env, F, T>(f: F) -> T
130130
where
131131
F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
132132
{
133+
// We put the `ScopeData` into an `Arc` so that other threads can finish their
134+
// `decrement_num_running_threads` even after this function returns.
133135
let scope = Scope {
134-
data: ScopeData {
136+
data: Arc::new(ScopeData {
135137
num_running_threads: AtomicUsize::new(0),
136138
main_thread: current(),
137139
a_thread_panicked: AtomicBool::new(false),
138-
},
140+
}),
139141
env: PhantomData,
140142
scope: PhantomData,
141143
};
@@ -250,7 +252,7 @@ impl Builder {
250252
F: FnOnce() -> T + Send + 'scope,
251253
T: Send + 'scope,
252254
{
253-
Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(&scope.data)) }?))
255+
Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(scope.data.clone())) }?))
254256
}
255257
}
256258

0 commit comments

Comments
 (0)