Skip to content

Commit 32cbc65

Browse files
committed
Auto merge of rust-lang#77380 - fusion-engineering-forks:unbox-the-mutex, r=dtolnay
Unbox mutexes and condvars on some platforms Both mutexes and condition variables contained a Box containing the actual os-specific object. This was done because moving these objects may cause undefined behaviour on some platforms. However, this is not needed on Windows[1], Wasm[2], cloudabi[2], and 'unsupported'[3], were the box was only needlessly making them less efficient. This change gets rid of the box on those platforms. On those platforms, `Condvar` can no longer verify it is only used with one `Mutex`, as mutexes no longer have a stable address. This was addressed and considered acceptable in rust-lang#76932. [1]\: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock [2]\: These are just a single atomic integer together with futex wait/wake calls/instructions. [3]\: The `unsupported` platform doesn't support multiple threads at all.
2 parents 2251766 + b1ce7a3 commit 32cbc65

File tree

20 files changed

+121
-83
lines changed

20 files changed

+121
-83
lines changed

library/std/src/sync/condvar.rs

+4-40
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
mod tests;
33

44
use crate::fmt;
5-
use crate::sync::atomic::{AtomicUsize, Ordering};
65
use crate::sync::{mutex, MutexGuard, PoisonError};
76
use crate::sys_common::condvar as sys;
8-
use crate::sys_common::mutex as sys_mutex;
97
use crate::sys_common::poison::{self, LockResult};
108
use crate::time::{Duration, Instant};
119

@@ -109,8 +107,7 @@ impl WaitTimeoutResult {
109107
/// ```
110108
#[stable(feature = "rust1", since = "1.0.0")]
111109
pub struct Condvar {
112-
inner: Box<sys::Condvar>,
113-
mutex: AtomicUsize,
110+
inner: sys::Condvar,
114111
}
115112

116113
impl Condvar {
@@ -126,11 +123,7 @@ impl Condvar {
126123
/// ```
127124
#[stable(feature = "rust1", since = "1.0.0")]
128125
pub fn new() -> Condvar {
129-
let mut c = Condvar { inner: box sys::Condvar::new(), mutex: AtomicUsize::new(0) };
130-
unsafe {
131-
c.inner.init();
132-
}
133-
c
126+
Condvar { inner: sys::Condvar::new() }
134127
}
135128

136129
/// Blocks the current thread until this condition variable receives a
@@ -192,7 +185,6 @@ impl Condvar {
192185
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
193186
let poisoned = unsafe {
194187
let lock = mutex::guard_lock(&guard);
195-
self.verify(lock);
196188
self.inner.wait(lock);
197189
mutex::guard_poison(&guard).get()
198190
};
@@ -389,7 +381,6 @@ impl Condvar {
389381
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
390382
let (poisoned, result) = unsafe {
391383
let lock = mutex::guard_lock(&guard);
392-
self.verify(lock);
393384
let success = self.inner.wait_timeout(lock, dur);
394385
(mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success))
395386
};
@@ -510,7 +501,7 @@ impl Condvar {
510501
/// ```
511502
#[stable(feature = "rust1", since = "1.0.0")]
512503
pub fn notify_one(&self) {
513-
unsafe { self.inner.notify_one() }
504+
self.inner.notify_one()
514505
}
515506

516507
/// Wakes up all blocked threads on this condvar.
@@ -550,27 +541,7 @@ impl Condvar {
550541
/// ```
551542
#[stable(feature = "rust1", since = "1.0.0")]
552543
pub fn notify_all(&self) {
553-
unsafe { self.inner.notify_all() }
554-
}
555-
556-
fn verify(&self, mutex: &sys_mutex::MovableMutex) {
557-
let addr = mutex.raw() as *const _ as usize;
558-
match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) {
559-
// If we got out 0, then we have successfully bound the mutex to
560-
// this cvar.
561-
0 => {}
562-
563-
// If we get out a value that's the same as `addr`, then someone
564-
// already beat us to the punch.
565-
n if n == addr => {}
566-
567-
// Anything else and we're using more than one mutex on this cvar,
568-
// which is currently disallowed.
569-
_ => panic!(
570-
"attempted to use a condition variable with two \
571-
mutexes"
572-
),
573-
}
544+
self.inner.notify_all()
574545
}
575546
}
576547

@@ -588,10 +559,3 @@ impl Default for Condvar {
588559
Condvar::new()
589560
}
590561
}
591-
592-
#[stable(feature = "rust1", since = "1.0.0")]
593-
impl Drop for Condvar {
594-
fn drop(&mut self) {
595-
unsafe { self.inner.destroy() }
596-
}
597-
}

library/std/src/sync/condvar/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ fn wait_timeout_wake() {
191191

192192
#[test]
193193
#[should_panic]
194-
#[cfg_attr(target_os = "emscripten", ignore)]
194+
#[cfg_attr(not(unix), ignore)]
195195
fn two_mutexes() {
196196
let m = Arc::new(Mutex::new(()));
197197
let m2 = m.clone();

library/std/src/sys/cloudabi/condvar.rs

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub struct Condvar {
1515
condvar: UnsafeCell<AtomicU32>,
1616
}
1717

18+
pub type MovableCondvar = Condvar;
19+
1820
unsafe impl Send for Condvar {}
1921
unsafe impl Sync for Condvar {}
2022

library/std/src/sys/cloudabi/mutex.rs

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ extern "C" {
1515
// implemented identically.
1616
pub struct Mutex(RWLock);
1717

18+
pub type MovableMutex = Mutex;
19+
1820
pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 {
1921
rwlock::raw(&m.0)
2022
}

library/std/src/sys/hermit/condvar.rs

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ pub struct Condvar {
1414
sem2: *const c_void,
1515
}
1616

17+
pub type MovableCondvar = Box<Condvar>;
18+
1719
unsafe impl Send for Condvar {}
1820
unsafe impl Sync for Condvar {}
1921

library/std/src/sys/sgx/condvar.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pub struct Condvar {
77
inner: SpinMutex<WaitVariable<()>>,
88
}
99

10+
pub type MovableCondvar = Box<Condvar>;
11+
1012
impl Condvar {
1113
pub const fn new() -> Condvar {
1214
Condvar { inner: SpinMutex::new(WaitVariable::new(())) }

library/std/src/sys/sgx/mutex.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub struct Mutex {
88
inner: SpinMutex<WaitVariable<bool>>,
99
}
1010

11+
pub type MovableMutex = Box<Mutex>;
12+
1113
// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
1214
impl Mutex {
1315
pub const fn new() -> Mutex {

library/std/src/sys/unix/condvar.rs

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub struct Condvar {
66
inner: UnsafeCell<libc::pthread_cond_t>,
77
}
88

9+
pub type MovableCondvar = Box<Condvar>;
10+
911
unsafe impl Send for Condvar {}
1012
unsafe impl Sync for Condvar {}
1113

library/std/src/sys/unix/mutex.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ pub struct Mutex {
55
inner: UnsafeCell<libc::pthread_mutex_t>,
66
}
77

8+
pub type MovableMutex = Box<Mutex>;
9+
810
#[inline]
911
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
1012
m.inner.get()

library/std/src/sys/unsupported/condvar.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use crate::time::Duration;
33

44
pub struct Condvar {}
55

6+
pub type MovableCondvar = Condvar;
7+
68
impl Condvar {
79
pub const fn new() -> Condvar {
810
Condvar {}

library/std/src/sys/unsupported/mutex.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ pub struct Mutex {
44
locked: UnsafeCell<bool>,
55
}
66

7+
pub type MovableMutex = Mutex;
8+
79
unsafe impl Send for Mutex {}
810
unsafe impl Sync for Mutex {} // no threads on this platform
911

library/std/src/sys/vxworks/condvar.rs

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub struct Condvar {
66
inner: UnsafeCell<libc::pthread_cond_t>,
77
}
88

9+
pub type MovableCondvar = Box<Condvar>;
10+
911
unsafe impl Send for Condvar {}
1012
unsafe impl Sync for Condvar {}
1113

library/std/src/sys/vxworks/mutex.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ pub struct Mutex {
55
inner: UnsafeCell<libc::pthread_mutex_t>,
66
}
77

8+
pub type MovableMutex = Box<Mutex>;
9+
810
#[inline]
911
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
1012
m.inner.get()

library/std/src/sys/wasm/condvar_atomics.rs

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub struct Condvar {
99
cnt: AtomicUsize,
1010
}
1111

12+
pub type MovableCondvar = Condvar;
13+
1214
// Condition variables are implemented with a simple counter internally that is
1315
// likely to cause spurious wakeups. Blocking on a condition variable will first
1416
// read the value of the internal counter, unlock the given mutex, and then

library/std/src/sys/wasm/mutex_atomics.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub struct Mutex {
88
locked: AtomicUsize,
99
}
1010

11+
pub type MovableMutex = Mutex;
12+
1113
// Mutexes have a pretty simple implementation where they contain an `i32`
1214
// internally that is 0 when unlocked and 1 when the mutex is locked.
1315
// Acquisition has a fast path where it attempts to cmpxchg the 0 to a 1, and

library/std/src/sys/windows/condvar.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub struct Condvar {
88
inner: UnsafeCell<c::CONDITION_VARIABLE>,
99
}
1010

11+
pub type MovableCondvar = Condvar;
12+
1113
unsafe impl Send for Condvar {}
1214
unsafe impl Sync for Condvar {}
1315

library/std/src/sys/windows/mutex.rs

+5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ pub struct Mutex {
2929
lock: AtomicUsize,
3030
}
3131

32+
// Windows SRW Locks are movable (while not borrowed).
33+
// ReentrantMutexes (in Inner) are not, but those are stored indirectly through
34+
// a Box, so do not move when the Mutex it self is moved.
35+
pub type MovableMutex = Mutex;
36+
3237
unsafe impl Send for Mutex {}
3338
unsafe impl Sync for Mutex {}
3439

library/std/src/sys_common/condvar.rs

+29-37
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,64 @@
11
use crate::sys::condvar as imp;
2+
use crate::sys::mutex as mutex_imp;
23
use crate::sys_common::mutex::MovableMutex;
34
use crate::time::Duration;
45

6+
mod check;
7+
8+
type CondvarCheck = <mutex_imp::MovableMutex as check::CondvarCheck>::Check;
9+
510
/// An OS-based condition variable.
6-
///
7-
/// This structure is the lowest layer possible on top of the OS-provided
8-
/// condition variables. It is consequently entirely unsafe to use. It is
9-
/// recommended to use the safer types at the top level of this crate instead of
10-
/// this type.
11-
pub struct Condvar(imp::Condvar);
11+
pub struct Condvar {
12+
inner: imp::MovableCondvar,
13+
check: CondvarCheck,
14+
}
1215

1316
impl Condvar {
1417
/// Creates a new condition variable for use.
15-
///
16-
/// Behavior is undefined if the condition variable is moved after it is
17-
/// first used with any of the functions below.
18-
pub const fn new() -> Condvar {
19-
Condvar(imp::Condvar::new())
20-
}
21-
22-
/// Prepares the condition variable for use.
23-
///
24-
/// This should be called once the condition variable is at a stable memory
25-
/// address.
26-
#[inline]
27-
pub unsafe fn init(&mut self) {
28-
self.0.init()
18+
pub fn new() -> Self {
19+
let mut c = imp::MovableCondvar::from(imp::Condvar::new());
20+
unsafe { c.init() };
21+
Self { inner: c, check: CondvarCheck::new() }
2922
}
3023

3124
/// Signals one waiter on this condition variable to wake up.
3225
#[inline]
33-
pub unsafe fn notify_one(&self) {
34-
self.0.notify_one()
26+
pub fn notify_one(&self) {
27+
unsafe { self.inner.notify_one() };
3528
}
3629

3730
/// Awakens all current waiters on this condition variable.
3831
#[inline]
39-
pub unsafe fn notify_all(&self) {
40-
self.0.notify_all()
32+
pub fn notify_all(&self) {
33+
unsafe { self.inner.notify_all() };
4134
}
4235

4336
/// Waits for a signal on the specified mutex.
4437
///
4538
/// Behavior is undefined if the mutex is not locked by the current thread.
46-
/// Behavior is also undefined if more than one mutex is used concurrently
47-
/// on this condition variable.
39+
///
40+
/// May panic if used with more than one mutex.
4841
#[inline]
4942
pub unsafe fn wait(&self, mutex: &MovableMutex) {
50-
self.0.wait(mutex.raw())
43+
self.check.verify(mutex);
44+
self.inner.wait(mutex.raw())
5145
}
5246

5347
/// Waits for a signal on the specified mutex with a timeout duration
5448
/// specified by `dur` (a relative time into the future).
5549
///
5650
/// Behavior is undefined if the mutex is not locked by the current thread.
57-
/// Behavior is also undefined if more than one mutex is used concurrently
58-
/// on this condition variable.
51+
///
52+
/// May panic if used with more than one mutex.
5953
#[inline]
6054
pub unsafe fn wait_timeout(&self, mutex: &MovableMutex, dur: Duration) -> bool {
61-
self.0.wait_timeout(mutex.raw(), dur)
55+
self.check.verify(mutex);
56+
self.inner.wait_timeout(mutex.raw(), dur)
6257
}
58+
}
6359

64-
/// Deallocates all resources associated with this condition variable.
65-
///
66-
/// Behavior is undefined if there are current or will be future users of
67-
/// this condition variable.
68-
#[inline]
69-
pub unsafe fn destroy(&self) {
70-
self.0.destroy()
60+
impl Drop for Condvar {
61+
fn drop(&mut self) {
62+
unsafe { self.inner.destroy() };
7163
}
7264
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use crate::sync::atomic::{AtomicUsize, Ordering};
2+
use crate::sys::mutex as mutex_imp;
3+
use crate::sys_common::mutex::MovableMutex;
4+
5+
pub trait CondvarCheck {
6+
type Check;
7+
}
8+
9+
/// For boxed mutexes, a `Condvar` will check it's only ever used with the same
10+
/// mutex, based on its (stable) address.
11+
impl CondvarCheck for Box<mutex_imp::Mutex> {
12+
type Check = SameMutexCheck;
13+
}
14+
15+
pub struct SameMutexCheck {
16+
addr: AtomicUsize,
17+
}
18+
19+
#[allow(dead_code)]
20+
impl SameMutexCheck {
21+
pub const fn new() -> Self {
22+
Self { addr: AtomicUsize::new(0) }
23+
}
24+
pub fn verify(&self, mutex: &MovableMutex) {
25+
let addr = mutex.raw() as *const mutex_imp::Mutex as usize;
26+
match self.addr.compare_and_swap(0, addr, Ordering::SeqCst) {
27+
0 => {} // Stored the address
28+
n if n == addr => {} // Lost a race to store the same address
29+
_ => panic!("attempted to use a condition variable with two mutexes"),
30+
}
31+
}
32+
}
33+
34+
/// Unboxed mutexes may move, so `Condvar` can not require its address to stay
35+
/// constant.
36+
impl CondvarCheck for mutex_imp::Mutex {
37+
type Check = NoCheck;
38+
}
39+
40+
pub struct NoCheck;
41+
42+
#[allow(dead_code)]
43+
impl NoCheck {
44+
pub const fn new() -> Self {
45+
Self
46+
}
47+
pub fn verify(&self, _: &MovableMutex) {}
48+
}

0 commit comments

Comments
 (0)