Move boxing and mutex checking logic of condvar into sys_common.

This commit is contained in:
Mara Bos 2020-10-01 00:57:46 +02:00
parent f283d3f02c
commit a8c2d4fc3d
4 changed files with 57 additions and 80 deletions

View File

@ -2,10 +2,8 @@
mod tests; mod tests;
use crate::fmt; use crate::fmt;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::{mutex, MutexGuard, PoisonError}; use crate::sync::{mutex, MutexGuard, PoisonError};
use crate::sys_common::condvar as sys; use crate::sys_common::condvar as sys;
use crate::sys_common::mutex as sys_mutex;
use crate::sys_common::poison::{self, LockResult}; use crate::sys_common::poison::{self, LockResult};
use crate::time::{Duration, Instant}; use crate::time::{Duration, Instant};
@ -109,8 +107,7 @@ impl WaitTimeoutResult {
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub struct Condvar { pub struct Condvar {
inner: Box<sys::Condvar>, inner: sys::Condvar,
mutex: AtomicUsize,
} }
impl Condvar { impl Condvar {
@ -126,11 +123,7 @@ impl Condvar {
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> Condvar { pub fn new() -> Condvar {
let mut c = Condvar { inner: box sys::Condvar::new(), mutex: AtomicUsize::new(0) }; Condvar { inner: sys::Condvar::new() }
unsafe {
c.inner.init();
}
c
} }
/// Blocks the current thread until this condition variable receives a /// Blocks the current thread until this condition variable receives a
@ -192,7 +185,6 @@ impl Condvar {
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> { pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
let poisoned = unsafe { let poisoned = unsafe {
let lock = mutex::guard_lock(&guard); let lock = mutex::guard_lock(&guard);
self.verify(lock);
self.inner.wait(lock); self.inner.wait(lock);
mutex::guard_poison(&guard).get() mutex::guard_poison(&guard).get()
}; };
@ -389,7 +381,6 @@ impl Condvar {
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
let (poisoned, result) = unsafe { let (poisoned, result) = unsafe {
let lock = mutex::guard_lock(&guard); let lock = mutex::guard_lock(&guard);
self.verify(lock);
let success = self.inner.wait_timeout(lock, dur); let success = self.inner.wait_timeout(lock, dur);
(mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success))
}; };
@ -510,7 +501,7 @@ impl Condvar {
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn notify_one(&self) { pub fn notify_one(&self) {
unsafe { self.inner.notify_one() } self.inner.notify_one()
} }
/// Wakes up all blocked threads on this condvar. /// Wakes up all blocked threads on this condvar.
@ -550,27 +541,7 @@ impl Condvar {
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn notify_all(&self) { pub fn notify_all(&self) {
unsafe { self.inner.notify_all() } 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"
),
}
} }
} }
@ -588,10 +559,3 @@ impl Default for Condvar {
Condvar::new() Condvar::new()
} }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl Drop for Condvar {
fn drop(&mut self) {
unsafe { self.inner.destroy() }
}
}

View File

@ -1,72 +1,62 @@
use crate::sys::condvar as imp; use crate::sys::condvar as imp;
use crate::sys_common::mutex::MovableMutex; use crate::sys_common::mutex::MovableMutex;
use crate::time::Duration; use crate::time::Duration;
use check::CondvarCheck;
mod check;
/// An OS-based condition variable. /// An OS-based condition variable.
/// pub struct Condvar {
/// This structure is the lowest layer possible on top of the OS-provided inner: Box<imp::Condvar>,
/// condition variables. It is consequently entirely unsafe to use. It is check: CondvarCheck,
/// recommended to use the safer types at the top level of this crate instead of }
/// this type.
pub struct Condvar(imp::Condvar);
impl Condvar { impl Condvar {
/// Creates a new condition variable for use. /// Creates a new condition variable for use.
/// pub fn new() -> Self {
/// Behavior is undefined if the condition variable is moved after it is let mut c = box imp::Condvar::new();
/// first used with any of the functions below. unsafe { c.init() };
pub const fn new() -> Condvar { Self { inner: c, check: CondvarCheck::new() }
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()
} }
/// Signals one waiter on this condition variable to wake up. /// Signals one waiter on this condition variable to wake up.
#[inline] #[inline]
pub unsafe fn notify_one(&self) { pub fn notify_one(&self) {
self.0.notify_one() unsafe { self.inner.notify_one() };
} }
/// Awakens all current waiters on this condition variable. /// Awakens all current waiters on this condition variable.
#[inline] #[inline]
pub unsafe fn notify_all(&self) { pub fn notify_all(&self) {
self.0.notify_all() unsafe { self.inner.notify_all() };
} }
/// Waits for a signal on the specified mutex. /// Waits for a signal on the specified mutex.
/// ///
/// Behavior is undefined if the mutex is not locked by the current thread. /// 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] #[inline]
pub unsafe fn wait(&self, mutex: &MovableMutex) { 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 /// Waits for a signal on the specified mutex with a timeout duration
/// specified by `dur` (a relative time into the future). /// specified by `dur` (a relative time into the future).
/// ///
/// Behavior is undefined if the mutex is not locked by the current thread. /// 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] #[inline]
pub unsafe fn wait_timeout(&self, mutex: &MovableMutex, dur: Duration) -> bool { 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 impl Drop for Condvar {
/// this condition variable. fn drop(&mut self) {
#[inline] unsafe { self.inner.destroy() };
pub unsafe fn destroy(&self) {
self.0.destroy()
} }
} }

View File

@ -0,0 +1,23 @@
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::mutex as mutex_imp;
use crate::sys_common::mutex::MovableMutex;
/// A `Condvar` will check it's only ever used with the same mutex, based on
/// its (stable) address.
pub struct CondvarCheck {
addr: AtomicUsize,
}
impl CondvarCheck {
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"),
}
}
}

View File

@ -72,7 +72,7 @@ impl MovableMutex {
Self(mutex) Self(mutex)
} }
pub(crate) fn raw(&self) -> &imp::Mutex { pub(super) fn raw(&self) -> &imp::Mutex {
&self.0 &self.0
} }