Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unbox mutexes and condvars on some platforms #77380

Merged
merged 12 commits into from
Oct 4, 2020
Merged
44 changes: 4 additions & 40 deletions library/std/src/sync/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
mod tests;

use crate::fmt;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::{mutex, MutexGuard, PoisonError};
use crate::sys_common::condvar as sys;
use crate::sys_common::mutex as sys_mutex;
use crate::sys_common::poison::{self, LockResult};
use crate::time::{Duration, Instant};

Expand Down Expand Up @@ -109,8 +107,7 @@ impl WaitTimeoutResult {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Condvar {
inner: Box<sys::Condvar>,
mutex: AtomicUsize,
inner: sys::Condvar,
}

impl Condvar {
Expand All @@ -126,11 +123,7 @@ impl Condvar {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> Condvar {
let mut c = Condvar { inner: box sys::Condvar::new(), mutex: AtomicUsize::new(0) };
unsafe {
c.inner.init();
}
c
Condvar { inner: sys::Condvar::new() }
}

/// Blocks the current thread until this condition variable receives a
Expand Down Expand Up @@ -192,7 +185,6 @@ impl Condvar {
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
let poisoned = unsafe {
let lock = mutex::guard_lock(&guard);
self.verify(lock);
self.inner.wait(lock);
mutex::guard_poison(&guard).get()
};
Expand Down Expand Up @@ -389,7 +381,6 @@ impl Condvar {
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
let (poisoned, result) = unsafe {
let lock = mutex::guard_lock(&guard);
self.verify(lock);
let success = self.inner.wait_timeout(lock, dur);
(mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success))
};
Expand Down Expand Up @@ -510,7 +501,7 @@ impl Condvar {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn notify_one(&self) {
unsafe { self.inner.notify_one() }
self.inner.notify_one()
}

/// Wakes up all blocked threads on this condvar.
Expand Down Expand Up @@ -550,27 +541,7 @@ impl Condvar {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn notify_all(&self) {
unsafe { self.inner.notify_all() }
}

fn verify(&self, mutex: &sys_mutex::MovableMutex) {
let addr = mutex.raw() as *const _ as usize;
match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) {
// If we got out 0, then we have successfully bound the mutex to
// this cvar.
0 => {}

// If we get out a value that's the same as `addr`, then someone
// already beat us to the punch.
n if n == addr => {}

// Anything else and we're using more than one mutex on this cvar,
// which is currently disallowed.
_ => panic!(
"attempted to use a condition variable with two \
mutexes"
),
}
self.inner.notify_all()
}
}

Expand All @@ -588,10 +559,3 @@ impl Default for Condvar {
Condvar::new()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl Drop for Condvar {
fn drop(&mut self) {
unsafe { self.inner.destroy() }
}
}
2 changes: 1 addition & 1 deletion library/std/src/sync/condvar/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ fn wait_timeout_wake() {

#[test]
#[should_panic]
#[cfg_attr(target_os = "emscripten", ignore)]
#[cfg_attr(not(unix), ignore)]
fn two_mutexes() {
let m = Arc::new(Mutex::new(()));
let m2 = m.clone();
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/cloudabi/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub struct Condvar {
condvar: UnsafeCell<AtomicU32>,
}

pub type MovableCondvar = Condvar;

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/cloudabi/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ extern "C" {
// implemented identically.
pub struct Mutex(RWLock);

pub type MovableMutex = Mutex;

pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 {
rwlock::raw(&m.0)
}
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/hermit/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub struct Condvar {
sem2: *const c_void,
}

pub type MovableCondvar = Box<Condvar>;

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/sgx/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub struct Condvar {
inner: SpinMutex<WaitVariable<()>>,
}

pub type MovableCondvar = Box<Condvar>;

impl Condvar {
pub const fn new() -> Condvar {
Condvar { inner: SpinMutex::new(WaitVariable::new(())) }
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/sgx/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub struct Mutex {
inner: SpinMutex<WaitVariable<bool>>,
}

pub type MovableMutex = Box<Mutex>;

// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
impl Mutex {
pub const fn new() -> Mutex {
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/unix/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub struct Condvar {
inner: UnsafeCell<libc::pthread_cond_t>,
}

pub type MovableCondvar = Box<Condvar>;

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/unix/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub struct Mutex {
inner: UnsafeCell<libc::pthread_mutex_t>,
}

pub type MovableMutex = Box<Mutex>;

#[inline]
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
m.inner.get()
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/unsupported/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::time::Duration;

pub struct Condvar {}

pub type MovableCondvar = Condvar;

impl Condvar {
pub const fn new() -> Condvar {
Condvar {}
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/unsupported/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub struct Mutex {
locked: UnsafeCell<bool>,
}

pub type MovableMutex = Mutex;

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {} // no threads on this platform

Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/vxworks/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub struct Condvar {
inner: UnsafeCell<libc::pthread_cond_t>,
}

pub type MovableCondvar = Box<Condvar>;

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/vxworks/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub struct Mutex {
inner: UnsafeCell<libc::pthread_mutex_t>,
}

pub type MovableMutex = Box<Mutex>;

#[inline]
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
m.inner.get()
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/wasm/condvar_atomics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub struct Condvar {
cnt: AtomicUsize,
}

pub type MovableCondvar = Condvar;

// Condition variables are implemented with a simple counter internally that is
// likely to cause spurious wakeups. Blocking on a condition variable will first
// read the value of the internal counter, unlock the given mutex, and then
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/wasm/mutex_atomics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub struct Mutex {
locked: AtomicUsize,
}

pub type MovableMutex = Mutex;

// Mutexes have a pretty simple implementation where they contain an `i32`
// internally that is 0 when unlocked and 1 when the mutex is locked.
// Acquisition has a fast path where it attempts to cmpxchg the 0 to a 1, and
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/windows/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub struct Condvar {
inner: UnsafeCell<c::CONDITION_VARIABLE>,
}

pub type MovableCondvar = Condvar;

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

Expand Down
5 changes: 5 additions & 0 deletions library/std/src/sys/windows/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ pub struct Mutex {
lock: AtomicUsize,
}

// Windows SRW Locks are movable (while not borrowed).
// ReentrantMutexes (in Inner) are not, but those are stored indirectly through
// a Box, so do not move when the Mutex it self is moved.
pub type MovableMutex = Mutex;

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

Expand Down
66 changes: 29 additions & 37 deletions library/std/src/sys_common/condvar.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,64 @@
use crate::sys::condvar as imp;
use crate::sys::mutex as mutex_imp;
use crate::sys_common::mutex::MovableMutex;
use crate::time::Duration;

mod check;

type CondvarCheck = <mutex_imp::MovableMutex as check::CondvarCheck>::Check;

/// An OS-based condition variable.
///
/// This structure is the lowest layer possible on top of the OS-provided
/// condition variables. It is consequently entirely unsafe to use. It is
/// recommended to use the safer types at the top level of this crate instead of
/// this type.
pub struct Condvar(imp::Condvar);
pub struct Condvar {
inner: imp::MovableCondvar,
check: CondvarCheck,
}

impl Condvar {
/// Creates a new condition variable for use.
///
/// Behavior is undefined if the condition variable is moved after it is
/// first used with any of the functions below.
pub const fn new() -> Condvar {
Condvar(imp::Condvar::new())
}

/// Prepares the condition variable for use.
///
/// This should be called once the condition variable is at a stable memory
/// address.
#[inline]
pub unsafe fn init(&mut self) {
self.0.init()
pub fn new() -> Self {
let mut c = imp::MovableCondvar::from(imp::Condvar::new());
unsafe { c.init() };
Self { inner: c, check: CondvarCheck::new() }
}

/// Signals one waiter on this condition variable to wake up.
#[inline]
pub unsafe fn notify_one(&self) {
self.0.notify_one()
pub fn notify_one(&self) {
unsafe { self.inner.notify_one() };
}

/// Awakens all current waiters on this condition variable.
#[inline]
pub unsafe fn notify_all(&self) {
self.0.notify_all()
pub fn notify_all(&self) {
unsafe { self.inner.notify_all() };
}

/// Waits for a signal on the specified mutex.
///
/// Behavior is undefined if the mutex is not locked by the current thread.
/// Behavior is also undefined if more than one mutex is used concurrently
/// on this condition variable.
///
/// May panic if used with more than one mutex.
#[inline]
pub unsafe fn wait(&self, mutex: &MovableMutex) {
self.0.wait(mutex.raw())
self.check.verify(mutex);
self.inner.wait(mutex.raw())
}

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

/// Deallocates all resources associated with this condition variable.
///
/// Behavior is undefined if there are current or will be future users of
/// this condition variable.
#[inline]
pub unsafe fn destroy(&self) {
self.0.destroy()
impl Drop for Condvar {
fn drop(&mut self) {
unsafe { self.inner.destroy() };
}
}
48 changes: 48 additions & 0 deletions library/std/src/sys_common/condvar/check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::mutex as mutex_imp;
use crate::sys_common::mutex::MovableMutex;

pub trait CondvarCheck {
type Check;
}

/// For boxed mutexes, a `Condvar` will check it's only ever used with the same
/// mutex, based on its (stable) address.
impl CondvarCheck for Box<mutex_imp::Mutex> {
type Check = SameMutexCheck;
}

pub struct SameMutexCheck {
addr: AtomicUsize,
}

#[allow(dead_code)]
impl SameMutexCheck {
pub const fn new() -> Self {
Self { addr: AtomicUsize::new(0) }
}
pub fn verify(&self, mutex: &MovableMutex) {
let addr = mutex.raw() as *const mutex_imp::Mutex as usize;
match self.addr.compare_and_swap(0, addr, Ordering::SeqCst) {
0 => {} // Stored the address
n if n == addr => {} // Lost a race to store the same address
_ => panic!("attempted to use a condition variable with two mutexes"),
}
}
}

/// Unboxed mutexes may move, so `Condvar` can not require its address to stay
/// constant.
impl CondvarCheck for mutex_imp::Mutex {
type Check = NoCheck;
}

pub struct NoCheck;

#[allow(dead_code)]
impl NoCheck {
pub const fn new() -> Self {
Self
}
pub fn verify(&self, _: &MovableMutex) {}
}
Loading