Auto merge of #34705 - alexcrichton:clean-deprecated, r=brson
std: Clean out deprecated APIs This primarily removes a lot of `sync::Static*` APIs and rejiggers the associated implementations. While doing this it was discovered that the `is_poisoned` method can actually result in a data race for the Mutex/RwLock primitives, so the inner `Cell<bool>` was changed to an `AtomicBool` to prevent the associated data race. Otherwise the usage/gurantees should be the same they were before.
This commit is contained in:
commit
26fd0117f9
9 changed files with 229 additions and 765 deletions
|
@ -1244,39 +1244,6 @@ impl<I: Iterator> Peekable<I> {
|
|||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the iterator has finished iterating.
|
||||
///
|
||||
/// Returns `true` if there are no more elements in the iterator, and
|
||||
/// `false` if there are.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(peekable_is_empty)]
|
||||
///
|
||||
/// let xs = [1, 2, 3];
|
||||
///
|
||||
/// let mut iter = xs.iter().peekable();
|
||||
///
|
||||
/// // There are still elements to iterate over
|
||||
/// assert_eq!(iter.is_empty(), false);
|
||||
///
|
||||
/// // Let's consume the iterator
|
||||
/// iter.next();
|
||||
/// iter.next();
|
||||
/// iter.next();
|
||||
///
|
||||
/// assert_eq!(iter.is_empty(), true);
|
||||
/// ```
|
||||
#[unstable(feature = "peekable_is_empty", issue = "32111")]
|
||||
#[inline]
|
||||
#[rustc_deprecated(since = "1.10.0", reason = "replaced by .peek().is_none()")]
|
||||
pub fn is_empty(&mut self) -> bool {
|
||||
self.peek().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that rejects elements while `predicate` is true.
|
||||
|
|
|
@ -15,7 +15,7 @@ use sync::{mutex, MutexGuard, PoisonError};
|
|||
use sys_common::condvar as sys;
|
||||
use sys_common::mutex as sys_mutex;
|
||||
use sys_common::poison::{self, LockResult};
|
||||
use time::{Instant, Duration};
|
||||
use time::Duration;
|
||||
|
||||
/// A type indicating whether a timed wait on a condition variable returned
|
||||
/// due to a time out or not.
|
||||
|
@ -72,59 +72,19 @@ impl WaitTimeoutResult {
|
|||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub struct Condvar { inner: Box<StaticCondvar> }
|
||||
|
||||
/// Statically allocated condition variables.
|
||||
///
|
||||
/// This structure is identical to `Condvar` except that it is suitable for use
|
||||
/// in static initializers for other structures.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(static_condvar)]
|
||||
///
|
||||
/// use std::sync::{StaticCondvar, CONDVAR_INIT};
|
||||
///
|
||||
/// static CVAR: StaticCondvar = CONDVAR_INIT;
|
||||
/// ```
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `Condvar::new` in a static should \
|
||||
suffice")]
|
||||
pub struct StaticCondvar {
|
||||
inner: sys::Condvar,
|
||||
pub struct Condvar {
|
||||
inner: Box<sys::Condvar>,
|
||||
mutex: AtomicUsize,
|
||||
}
|
||||
|
||||
/// Constant initializer for a statically allocated condition variable.
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `Condvar::new` in a static should \
|
||||
suffice")]
|
||||
#[allow(deprecated)]
|
||||
pub const CONDVAR_INIT: StaticCondvar = StaticCondvar::new();
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl Condvar {
|
||||
/// Creates a new condition variable which is ready to be waited on and
|
||||
/// notified.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new() -> Condvar {
|
||||
Condvar {
|
||||
inner: box StaticCondvar {
|
||||
inner: sys::Condvar::new(),
|
||||
mutex: AtomicUsize::new(0),
|
||||
}
|
||||
inner: box sys::Condvar::new(),
|
||||
mutex: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,9 +117,16 @@ impl Condvar {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>)
|
||||
-> LockResult<MutexGuard<'a, T>> {
|
||||
unsafe {
|
||||
let me: &'static Condvar = &*(self as *const _);
|
||||
me.inner.wait(guard)
|
||||
let poisoned = unsafe {
|
||||
let lock = mutex::guard_lock(&guard);
|
||||
self.verify(lock);
|
||||
self.inner.wait(lock);
|
||||
mutex::guard_poison(&guard).get()
|
||||
};
|
||||
if poisoned {
|
||||
Err(PoisonError::new(guard))
|
||||
} else {
|
||||
Ok(guard)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,9 +173,16 @@ impl Condvar {
|
|||
pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>,
|
||||
dur: Duration)
|
||||
-> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
|
||||
unsafe {
|
||||
let me: &'static Condvar = &*(self as *const _);
|
||||
me.inner.wait_timeout(guard, dur)
|
||||
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))
|
||||
};
|
||||
if poisoned {
|
||||
Err(PoisonError::new((guard, result)))
|
||||
} else {
|
||||
Ok((guard, result))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,7 +194,9 @@ impl Condvar {
|
|||
///
|
||||
/// To wake up all threads, see `notify_all()`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn notify_one(&self) { unsafe { self.inner.inner.notify_one() } }
|
||||
pub fn notify_one(&self) {
|
||||
unsafe { self.inner.notify_one() }
|
||||
}
|
||||
|
||||
/// Wakes up all blocked threads on this condvar.
|
||||
///
|
||||
|
@ -230,169 +206,8 @@ impl Condvar {
|
|||
///
|
||||
/// To wake up only one thread, see `notify_one()`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn notify_all(&self) { unsafe { self.inner.inner.notify_all() } }
|
||||
}
|
||||
|
||||
#[stable(feature = "condvar_default", since = "1.9.0")]
|
||||
impl Default for Condvar {
|
||||
fn default() -> Condvar {
|
||||
Condvar::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl Drop for Condvar {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.inner.inner.destroy() }
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `Condvar::new` in a static should \
|
||||
suffice")]
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
#[allow(deprecated)]
|
||||
impl StaticCondvar {
|
||||
/// Creates a new condition variable
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
pub const fn new() -> StaticCondvar {
|
||||
StaticCondvar {
|
||||
inner: sys::Condvar::new(),
|
||||
mutex: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until this condition variable receives a
|
||||
/// notification.
|
||||
///
|
||||
/// See `Condvar::wait`.
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
pub fn wait<'a, T>(&'static 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()
|
||||
};
|
||||
if poisoned {
|
||||
Err(PoisonError::new(guard))
|
||||
} else {
|
||||
Ok(guard)
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// See `Condvar::wait_timeout`.
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
pub fn wait_timeout<'a, T>(&'static self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
timeout: Duration)
|
||||
-> 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, timeout);
|
||||
(mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success))
|
||||
};
|
||||
if poisoned {
|
||||
Err(PoisonError::new((guard, result)))
|
||||
} else {
|
||||
Ok((guard, result))
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The implementation will repeatedly wait while the duration has not
|
||||
/// passed and the function returns `false`.
|
||||
///
|
||||
/// See `Condvar::wait_timeout_with`.
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
pub fn wait_timeout_with<'a, T, F>(&'static self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
dur: Duration,
|
||||
mut f: F)
|
||||
-> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)>
|
||||
where F: FnMut(LockResult<&mut T>) -> bool {
|
||||
// This could be made more efficient by pushing the implementation into
|
||||
// sys::condvar
|
||||
let start = Instant::now();
|
||||
let mut guard_result: LockResult<MutexGuard<'a, T>> = Ok(guard);
|
||||
while !f(guard_result
|
||||
.as_mut()
|
||||
.map(|g| &mut **g)
|
||||
.map_err(|e| PoisonError::new(&mut **e.get_mut()))) {
|
||||
let consumed = start.elapsed();
|
||||
let guard = guard_result.unwrap_or_else(|e| e.into_inner());
|
||||
let (new_guard_result, timed_out) = if consumed > dur {
|
||||
(Ok(guard), WaitTimeoutResult(true))
|
||||
} else {
|
||||
match self.wait_timeout(guard, dur - consumed) {
|
||||
Ok((new_guard, timed_out)) => (Ok(new_guard), timed_out),
|
||||
Err(err) => {
|
||||
let (new_guard, no_timeout) = err.into_inner();
|
||||
(Err(PoisonError::new(new_guard)), no_timeout)
|
||||
}
|
||||
}
|
||||
};
|
||||
guard_result = new_guard_result;
|
||||
if timed_out.timed_out() {
|
||||
let result = f(guard_result
|
||||
.as_mut()
|
||||
.map(|g| &mut **g)
|
||||
.map_err(|e| PoisonError::new(&mut **e.get_mut())));
|
||||
let result = WaitTimeoutResult(!result);
|
||||
return poison::map_result(guard_result, |g| (g, result));
|
||||
}
|
||||
}
|
||||
|
||||
poison::map_result(guard_result, |g| (g, WaitTimeoutResult(false)))
|
||||
}
|
||||
|
||||
/// Wakes up one blocked thread on this condvar.
|
||||
///
|
||||
/// See `Condvar::notify_one`.
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } }
|
||||
|
||||
/// Wakes up all blocked threads on this condvar.
|
||||
///
|
||||
/// See `Condvar::notify_all`.
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } }
|
||||
|
||||
/// Deallocates all resources associated with this static condvar.
|
||||
///
|
||||
/// This method is unsafe to call as there is no guarantee that there are no
|
||||
/// active users of the condvar, and this also doesn't prevent any future
|
||||
/// users of the condvar. This method is required to be called to not leak
|
||||
/// memory on all platforms.
|
||||
#[unstable(feature = "static_condvar",
|
||||
reason = "may be merged with Condvar in the future",
|
||||
issue = "27717")]
|
||||
pub unsafe fn destroy(&'static self) {
|
||||
self.inner.destroy()
|
||||
pub fn notify_all(&self) {
|
||||
unsafe { self.inner.notify_all() }
|
||||
}
|
||||
|
||||
fn verify(&self, mutex: &sys_mutex::Mutex) {
|
||||
|
@ -414,15 +229,26 @@ impl StaticCondvar {
|
|||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "condvar_default", since = "1.9.0")]
|
||||
impl Default for Condvar {
|
||||
fn default() -> Condvar {
|
||||
Condvar::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Drop for Condvar {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.inner.destroy() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
use prelude::v1::*;
|
||||
|
||||
use super::StaticCondvar;
|
||||
use sync::mpsc::channel;
|
||||
use sync::{StaticMutex, Condvar, Mutex, Arc};
|
||||
use sync::atomic::{AtomicUsize, Ordering};
|
||||
use sync::{Condvar, Mutex, Arc};
|
||||
use thread;
|
||||
use time::Duration;
|
||||
use u32;
|
||||
|
@ -434,27 +260,20 @@ mod tests {
|
|||
c.notify_all();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_smoke() {
|
||||
static C: StaticCondvar = StaticCondvar::new();
|
||||
C.notify_one();
|
||||
C.notify_all();
|
||||
unsafe { C.destroy(); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notify_one() {
|
||||
static C: StaticCondvar = StaticCondvar::new();
|
||||
static M: StaticMutex = StaticMutex::new();
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
|
||||
let g = M.lock().unwrap();
|
||||
let g = m.lock().unwrap();
|
||||
let _t = thread::spawn(move|| {
|
||||
let _g = M.lock().unwrap();
|
||||
C.notify_one();
|
||||
let _g = m2.lock().unwrap();
|
||||
c2.notify_one();
|
||||
});
|
||||
let g = C.wait(g).unwrap();
|
||||
let g = c.wait(g).unwrap();
|
||||
drop(g);
|
||||
unsafe { C.destroy(); M.destroy(); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -495,84 +314,41 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn wait_timeout_ms() {
|
||||
static C: StaticCondvar = StaticCondvar::new();
|
||||
static M: StaticMutex = StaticMutex::new();
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
|
||||
let g = M.lock().unwrap();
|
||||
let (g, _no_timeout) = C.wait_timeout(g, Duration::from_millis(1)).unwrap();
|
||||
let g = m.lock().unwrap();
|
||||
let (g, _no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap();
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// assert!(!no_timeout);
|
||||
let _t = thread::spawn(move || {
|
||||
let _g = M.lock().unwrap();
|
||||
C.notify_one();
|
||||
let _g = m2.lock().unwrap();
|
||||
c2.notify_one();
|
||||
});
|
||||
let (g, timeout_res) = C.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap();
|
||||
let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap();
|
||||
assert!(!timeout_res.timed_out());
|
||||
drop(g);
|
||||
unsafe { C.destroy(); M.destroy(); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wait_timeout_with() {
|
||||
static C: StaticCondvar = StaticCondvar::new();
|
||||
static M: StaticMutex = StaticMutex::new();
|
||||
static S: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
let g = M.lock().unwrap();
|
||||
let (g, timed_out) = C.wait_timeout_with(g, Duration::new(0, 1000), |_| {
|
||||
false
|
||||
}).unwrap();
|
||||
assert!(timed_out.timed_out());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
rx.recv().unwrap();
|
||||
let g = M.lock().unwrap();
|
||||
S.store(1, Ordering::SeqCst);
|
||||
C.notify_one();
|
||||
drop(g);
|
||||
|
||||
rx.recv().unwrap();
|
||||
let g = M.lock().unwrap();
|
||||
S.store(2, Ordering::SeqCst);
|
||||
C.notify_one();
|
||||
drop(g);
|
||||
|
||||
rx.recv().unwrap();
|
||||
let _g = M.lock().unwrap();
|
||||
S.store(3, Ordering::SeqCst);
|
||||
C.notify_one();
|
||||
});
|
||||
|
||||
let mut state = 0;
|
||||
let day = 24 * 60 * 60;
|
||||
let (_g, timed_out) = C.wait_timeout_with(g, Duration::new(day, 0), |_| {
|
||||
assert_eq!(state, S.load(Ordering::SeqCst));
|
||||
tx.send(()).unwrap();
|
||||
state += 1;
|
||||
match state {
|
||||
1|2 => false,
|
||||
_ => true,
|
||||
}
|
||||
}).unwrap();
|
||||
assert!(!timed_out.timed_out());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn two_mutexes() {
|
||||
static M1: StaticMutex = StaticMutex::new();
|
||||
static M2: StaticMutex = StaticMutex::new();
|
||||
static C: StaticCondvar = StaticCondvar::new();
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
|
||||
let mut g = M1.lock().unwrap();
|
||||
let mut g = m.lock().unwrap();
|
||||
let _t = thread::spawn(move|| {
|
||||
let _g = M1.lock().unwrap();
|
||||
C.notify_one();
|
||||
let _g = m2.lock().unwrap();
|
||||
c2.notify_one();
|
||||
});
|
||||
g = C.wait(g).unwrap();
|
||||
g = c.wait(g).unwrap();
|
||||
drop(g);
|
||||
|
||||
let _ = C.wait(M2.lock().unwrap()).unwrap();
|
||||
let m = Mutex::new(());
|
||||
let _ = c.wait(m.lock().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,23 +25,15 @@ pub use core::sync::atomic;
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::barrier::{Barrier, BarrierWaitResult};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub use self::condvar::{Condvar, StaticCondvar, WaitTimeoutResult, CONDVAR_INIT};
|
||||
pub use self::condvar::{Condvar, WaitTimeoutResult};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub use self::mutex::MUTEX_INIT;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub use self::mutex::{Mutex, MutexGuard, StaticMutex};
|
||||
pub use self::mutex::{Mutex, MutexGuard};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::once::{Once, OnceState, ONCE_INIT};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use sys_common::poison::{PoisonError, TryLockError, TryLockResult, LockResult};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::rwlock::{RwLockReadGuard, RwLockWriteGuard};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub use self::rwlock::{RwLock, StaticRwLock, RW_LOCK_INIT};
|
||||
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
pub mod mpsc;
|
||||
|
||||
|
|
|
@ -113,14 +113,14 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult};
|
|||
/// *guard += 1;
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub struct Mutex<T: ?Sized> {
|
||||
// Note that this static mutex is in a *box*, not inlined into the struct
|
||||
// itself. Once a native mutex has been used once, its address can never
|
||||
// change (it can't be moved). This mutex type can be safely moved at any
|
||||
// time, so to ensure that the native mutex is used correctly we box the
|
||||
// inner lock to give it a constant address.
|
||||
inner: Box<StaticMutex>,
|
||||
// Note that this mutex is in a *box*, not inlined into the struct itself.
|
||||
// Once a native mutex has been used once, its address can never change (it
|
||||
// can't be moved). This mutex type can be safely moved at any time, so to
|
||||
// ensure that the native mutex is used correctly we box the inner lock to
|
||||
// give it a constant address.
|
||||
inner: Box<sys::Mutex>,
|
||||
poison: poison::Flag,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
|
@ -131,42 +131,6 @@ unsafe impl<T: ?Sized + Send> Send for Mutex<T> { }
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> { }
|
||||
|
||||
/// The static mutex type is provided to allow for static allocation of mutexes.
|
||||
///
|
||||
/// Note that this is a separate type because using a Mutex correctly means that
|
||||
/// it needs to have a destructor run. In Rust, statics are not allowed to have
|
||||
/// destructors. As a result, a `StaticMutex` has one extra method when compared
|
||||
/// to a `Mutex`, a `destroy` method. This method is unsafe to call, and
|
||||
/// documentation can be found directly on the method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(static_mutex)]
|
||||
///
|
||||
/// use std::sync::{StaticMutex, MUTEX_INIT};
|
||||
///
|
||||
/// static LOCK: StaticMutex = MUTEX_INIT;
|
||||
///
|
||||
/// {
|
||||
/// let _g = LOCK.lock().unwrap();
|
||||
/// // do some productive work
|
||||
/// }
|
||||
/// // lock is unlocked here.
|
||||
/// ```
|
||||
#[unstable(feature = "static_mutex",
|
||||
reason = "may be merged with Mutex in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `Mutex::new` in a static should \
|
||||
suffice")]
|
||||
pub struct StaticMutex {
|
||||
lock: sys::Mutex,
|
||||
poison: poison::Flag,
|
||||
}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
|
||||
/// dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
|
@ -174,48 +138,32 @@ pub struct StaticMutex {
|
|||
/// `Deref` and `DerefMut` implementations
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub struct MutexGuard<'a, T: ?Sized + 'a> {
|
||||
// funny underscores due to how Deref/DerefMut currently work (they
|
||||
// disregard field privacy).
|
||||
__lock: &'a StaticMutex,
|
||||
__data: &'a mut T,
|
||||
__lock: &'a Mutex<T>,
|
||||
__poison: poison::Guard,
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, T: ?Sized> !marker::Send for MutexGuard<'a, T> {}
|
||||
|
||||
/// Static initialization of a mutex. This constant can be used to initialize
|
||||
/// other mutex constants.
|
||||
#[unstable(feature = "static_mutex",
|
||||
reason = "may be merged with Mutex in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `Mutex::new` in a static should \
|
||||
suffice")]
|
||||
#[allow(deprecated)]
|
||||
pub const MUTEX_INIT: StaticMutex = StaticMutex::new();
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T> Mutex<T> {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(t: T) -> Mutex<T> {
|
||||
let mut m = Mutex {
|
||||
inner: box StaticMutex::new(),
|
||||
inner: box sys::Mutex::new(),
|
||||
poison: poison::Flag::new(),
|
||||
data: UnsafeCell::new(t),
|
||||
};
|
||||
unsafe {
|
||||
m.inner.lock.init();
|
||||
m.inner.init();
|
||||
}
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T: ?Sized> Mutex<T> {
|
||||
/// Acquires a mutex, blocking the current thread until it is able to do so.
|
||||
///
|
||||
|
@ -240,8 +188,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn lock(&self) -> LockResult<MutexGuard<T>> {
|
||||
unsafe {
|
||||
self.inner.lock.lock();
|
||||
MutexGuard::new(&*self.inner, &self.data)
|
||||
self.inner.lock();
|
||||
MutexGuard::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,8 +209,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> {
|
||||
unsafe {
|
||||
if self.inner.lock.try_lock() {
|
||||
Ok(MutexGuard::new(&*self.inner, &self.data)?)
|
||||
if self.inner.try_lock() {
|
||||
Ok(MutexGuard::new(self)?)
|
||||
} else {
|
||||
Err(TryLockError::WouldBlock)
|
||||
}
|
||||
|
@ -277,7 +225,7 @@ impl<T: ?Sized> Mutex<T> {
|
|||
#[inline]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn is_poisoned(&self) -> bool {
|
||||
self.inner.poison.get()
|
||||
self.poison.get()
|
||||
}
|
||||
|
||||
/// Consumes this mutex, returning the underlying data.
|
||||
|
@ -289,21 +237,22 @@ impl<T: ?Sized> Mutex<T> {
|
|||
#[stable(feature = "mutex_into_inner", since = "1.6.0")]
|
||||
pub fn into_inner(self) -> LockResult<T> where T: Sized {
|
||||
// We know statically that there are no outstanding references to
|
||||
// `self` so there's no need to lock the inner StaticMutex.
|
||||
// `self` so there's no need to lock the inner lock.
|
||||
//
|
||||
// To get the inner value, we'd like to call `data.into_inner()`,
|
||||
// but because `Mutex` impl-s `Drop`, we can't move out of it, so
|
||||
// we'll have to destructure it manually instead.
|
||||
unsafe {
|
||||
// Like `let Mutex { inner, data } = self`.
|
||||
let (inner, data) = {
|
||||
let Mutex { ref inner, ref data } = self;
|
||||
(ptr::read(inner), ptr::read(data))
|
||||
// Like `let Mutex { inner, poison, data } = self`.
|
||||
let (inner, poison, data) = {
|
||||
let Mutex { ref inner, ref poison, ref data } = self;
|
||||
(ptr::read(inner), ptr::read(poison), ptr::read(data))
|
||||
};
|
||||
mem::forget(self);
|
||||
inner.lock.destroy(); // Keep in sync with the `Drop` impl.
|
||||
inner.destroy(); // Keep in sync with the `Drop` impl.
|
||||
drop(inner);
|
||||
|
||||
poison::map_result(inner.poison.borrow(), |_| data.into_inner())
|
||||
poison::map_result(poison.borrow(), |_| data.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -319,14 +268,13 @@ impl<T: ?Sized> Mutex<T> {
|
|||
#[stable(feature = "mutex_get_mut", since = "1.6.0")]
|
||||
pub fn get_mut(&mut self) -> LockResult<&mut T> {
|
||||
// We know statically that there are no other references to `self`, so
|
||||
// there's no need to lock the inner StaticMutex.
|
||||
// there's no need to lock the inner lock.
|
||||
let data = unsafe { &mut *self.data.get() };
|
||||
poison::map_result(self.inner.poison.borrow(), |_| data )
|
||||
poison::map_result(self.poison.borrow(), |_| data )
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl<T: ?Sized> Drop for Mutex<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
|
@ -335,7 +283,7 @@ impl<T: ?Sized> Drop for Mutex<T> {
|
|||
// dropped, that's not our job)
|
||||
//
|
||||
// IMPORTANT: This code must be kept in sync with `Mutex::into_inner`.
|
||||
unsafe { self.inner.lock.destroy() }
|
||||
unsafe { self.inner.destroy() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,72 +307,11 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Dummy(UnsafeCell<()>);
|
||||
unsafe impl Sync for Dummy {}
|
||||
static DUMMY: Dummy = Dummy(UnsafeCell::new(()));
|
||||
|
||||
#[unstable(feature = "static_mutex",
|
||||
reason = "may be merged with Mutex in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `Mutex::new` in a static should \
|
||||
suffice")]
|
||||
#[allow(deprecated)]
|
||||
impl StaticMutex {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
pub const fn new() -> StaticMutex {
|
||||
StaticMutex {
|
||||
lock: sys::Mutex::new(),
|
||||
poison: poison::Flag::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires this lock, see `Mutex::lock`
|
||||
#[inline]
|
||||
pub fn lock(&'static self) -> LockResult<MutexGuard<()>> {
|
||||
unsafe {
|
||||
self.lock.lock();
|
||||
MutexGuard::new(self, &DUMMY.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to grab this lock, see `Mutex::try_lock`
|
||||
#[inline]
|
||||
pub fn try_lock(&'static self) -> TryLockResult<MutexGuard<()>> {
|
||||
unsafe {
|
||||
if self.lock.try_lock() {
|
||||
Ok(MutexGuard::new(self, &DUMMY.0)?)
|
||||
} else {
|
||||
Err(TryLockError::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deallocates resources associated with this static mutex.
|
||||
///
|
||||
/// This method is unsafe because it provides no guarantees that there are
|
||||
/// no active users of this mutex, and safety is not guaranteed if there are
|
||||
/// active users of this mutex.
|
||||
///
|
||||
/// This method is required to ensure that there are no memory leaks on
|
||||
/// *all* platforms. It may be the case that some platforms do not leak
|
||||
/// memory if this method is not called, but this is not guaranteed to be
|
||||
/// true on all platforms.
|
||||
pub unsafe fn destroy(&'static self) {
|
||||
self.lock.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
|
||||
unsafe fn new(lock: &'mutex StaticMutex, data: &'mutex UnsafeCell<T>)
|
||||
-> LockResult<MutexGuard<'mutex, T>> {
|
||||
unsafe fn new(lock: &'mutex Mutex<T>) -> LockResult<MutexGuard<'mutex, T>> {
|
||||
poison::map_result(lock.poison.borrow(), |guard| {
|
||||
MutexGuard {
|
||||
__lock: lock,
|
||||
__data: &mut *data.get(),
|
||||
__poison: guard,
|
||||
}
|
||||
})
|
||||
|
@ -435,43 +322,43 @@ impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
|
|||
impl<'mutex, T: ?Sized> Deref for MutexGuard<'mutex, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {self.__data }
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.__lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> {
|
||||
fn deref_mut(&mut self) -> &mut T { self.__data }
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.__lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.__lock.poison.done(&self.__poison);
|
||||
self.__lock.lock.unlock();
|
||||
self.__lock.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
|
||||
&guard.__lock.lock
|
||||
&guard.__lock.inner
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
|
||||
&guard.__lock.poison
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
use prelude::v1::*;
|
||||
|
||||
use sync::mpsc::channel;
|
||||
use sync::{Arc, Mutex, StaticMutex, Condvar};
|
||||
use sync::{Arc, Mutex, Condvar};
|
||||
use sync::atomic::{AtomicUsize, Ordering};
|
||||
use thread;
|
||||
|
||||
|
@ -490,48 +377,34 @@ mod tests {
|
|||
drop(m.lock().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_static() {
|
||||
static M: StaticMutex = StaticMutex::new();
|
||||
unsafe {
|
||||
drop(M.lock().unwrap());
|
||||
drop(M.lock().unwrap());
|
||||
M.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lots_and_lots() {
|
||||
static M: StaticMutex = StaticMutex::new();
|
||||
static mut CNT: u32 = 0;
|
||||
const J: u32 = 1000;
|
||||
const K: u32 = 3;
|
||||
|
||||
fn inc() {
|
||||
let m = Arc::new(Mutex::new(0));
|
||||
|
||||
fn inc(m: &Mutex<u32>) {
|
||||
for _ in 0..J {
|
||||
unsafe {
|
||||
let _g = M.lock().unwrap();
|
||||
CNT += 1;
|
||||
}
|
||||
*m.lock().unwrap() += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..K {
|
||||
let tx2 = tx.clone();
|
||||
thread::spawn(move|| { inc(); tx2.send(()).unwrap(); });
|
||||
let m2 = m.clone();
|
||||
thread::spawn(move|| { inc(&m2); tx2.send(()).unwrap(); });
|
||||
let tx2 = tx.clone();
|
||||
thread::spawn(move|| { inc(); tx2.send(()).unwrap(); });
|
||||
let m2 = m.clone();
|
||||
thread::spawn(move|| { inc(&m2); tx2.send(()).unwrap(); });
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
for _ in 0..2 * K {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
assert_eq!(unsafe {CNT}, J * K * 2);
|
||||
unsafe {
|
||||
M.destroy();
|
||||
}
|
||||
assert_eq!(*m.lock().unwrap(), J * K * 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -66,9 +66,9 @@ use sys_common::rwlock as sys;
|
|||
/// } // write lock is dropped here
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub struct RwLock<T: ?Sized> {
|
||||
inner: Box<StaticRwLock>,
|
||||
inner: Box<sys::RWLock>,
|
||||
poison: poison::Flag,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
|
@ -77,64 +77,12 @@ unsafe impl<T: ?Sized + Send + Sync> Send for RwLock<T> {}
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
|
||||
|
||||
/// Structure representing a statically allocated RwLock.
|
||||
///
|
||||
/// This structure is intended to be used inside of a `static` and will provide
|
||||
/// automatic global access as well as lazy initialization. The internal
|
||||
/// resources of this RwLock, however, must be manually deallocated.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(static_rwlock)]
|
||||
///
|
||||
/// use std::sync::{StaticRwLock, RW_LOCK_INIT};
|
||||
///
|
||||
/// static LOCK: StaticRwLock = RW_LOCK_INIT;
|
||||
///
|
||||
/// {
|
||||
/// let _g = LOCK.read().unwrap();
|
||||
/// // ... shared read access
|
||||
/// }
|
||||
/// {
|
||||
/// let _g = LOCK.write().unwrap();
|
||||
/// // ... exclusive write access
|
||||
/// }
|
||||
/// unsafe { LOCK.destroy() } // free all resources
|
||||
/// ```
|
||||
#[unstable(feature = "static_rwlock",
|
||||
reason = "may be merged with RwLock in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `RwLock::new` in a static should \
|
||||
suffice")]
|
||||
pub struct StaticRwLock {
|
||||
lock: sys::RWLock,
|
||||
poison: poison::Flag,
|
||||
}
|
||||
|
||||
/// Constant initialization for a statically-initialized rwlock.
|
||||
#[unstable(feature = "static_rwlock",
|
||||
reason = "may be merged with RwLock in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `RwLock::new` in a static should \
|
||||
suffice")]
|
||||
#[allow(deprecated)]
|
||||
pub const RW_LOCK_INIT: StaticRwLock = StaticRwLock::new();
|
||||
|
||||
/// RAII structure used to release the shared read access of a lock when
|
||||
/// dropped.
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
|
||||
__lock: &'a StaticRwLock,
|
||||
__data: &'a T,
|
||||
__lock: &'a RwLock<T>,
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -144,17 +92,14 @@ impl<'a, T: ?Sized> !marker::Send for RwLockReadGuard<'a, T> {}
|
|||
/// dropped.
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
|
||||
__lock: &'a StaticRwLock,
|
||||
__data: &'a mut T,
|
||||
__lock: &'a RwLock<T>,
|
||||
__poison: poison::Guard,
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, T: ?Sized> !marker::Send for RwLockWriteGuard<'a, T> {}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T> RwLock<T> {
|
||||
/// Creates a new instance of an `RwLock<T>` which is unlocked.
|
||||
///
|
||||
|
@ -167,11 +112,14 @@ impl<T> RwLock<T> {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(t: T) -> RwLock<T> {
|
||||
RwLock { inner: box StaticRwLock::new(), data: UnsafeCell::new(t) }
|
||||
RwLock {
|
||||
inner: box sys::RWLock::new(),
|
||||
poison: poison::Flag::new(),
|
||||
data: UnsafeCell::new(t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T: ?Sized> RwLock<T> {
|
||||
/// Locks this rwlock with shared read access, blocking the current thread
|
||||
/// until it can be acquired.
|
||||
|
@ -194,8 +142,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn read(&self) -> LockResult<RwLockReadGuard<T>> {
|
||||
unsafe {
|
||||
self.inner.lock.read();
|
||||
RwLockReadGuard::new(&*self.inner, &self.data)
|
||||
self.inner.read();
|
||||
RwLockReadGuard::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,8 +168,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<T>> {
|
||||
unsafe {
|
||||
if self.inner.lock.try_read() {
|
||||
Ok(RwLockReadGuard::new(&*self.inner, &self.data)?)
|
||||
if self.inner.try_read() {
|
||||
Ok(RwLockReadGuard::new(self)?)
|
||||
} else {
|
||||
Err(TryLockError::WouldBlock)
|
||||
}
|
||||
|
@ -246,8 +194,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> {
|
||||
unsafe {
|
||||
self.inner.lock.write();
|
||||
RwLockWriteGuard::new(&*self.inner, &self.data)
|
||||
self.inner.write();
|
||||
RwLockWriteGuard::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,8 +220,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<T>> {
|
||||
unsafe {
|
||||
if self.inner.lock.try_write() {
|
||||
Ok(RwLockWriteGuard::new(&*self.inner, &self.data)?)
|
||||
if self.inner.try_write() {
|
||||
Ok(RwLockWriteGuard::new(self)?)
|
||||
} else {
|
||||
Err(TryLockError::WouldBlock)
|
||||
}
|
||||
|
@ -288,7 +236,7 @@ impl<T: ?Sized> RwLock<T> {
|
|||
#[inline]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn is_poisoned(&self) -> bool {
|
||||
self.inner.poison.get()
|
||||
self.poison.get()
|
||||
}
|
||||
|
||||
/// Consumes this `RwLock`, returning the underlying data.
|
||||
|
@ -302,21 +250,22 @@ impl<T: ?Sized> RwLock<T> {
|
|||
#[stable(feature = "rwlock_into_inner", since = "1.6.0")]
|
||||
pub fn into_inner(self) -> LockResult<T> where T: Sized {
|
||||
// We know statically that there are no outstanding references to
|
||||
// `self` so there's no need to lock the inner StaticRwLock.
|
||||
// `self` so there's no need to lock the inner lock.
|
||||
//
|
||||
// To get the inner value, we'd like to call `data.into_inner()`,
|
||||
// but because `RwLock` impl-s `Drop`, we can't move out of it, so
|
||||
// we'll have to destructure it manually instead.
|
||||
unsafe {
|
||||
// Like `let RwLock { inner, data } = self`.
|
||||
let (inner, data) = {
|
||||
let RwLock { ref inner, ref data } = self;
|
||||
(ptr::read(inner), ptr::read(data))
|
||||
// Like `let RwLock { inner, poison, data } = self`.
|
||||
let (inner, poison, data) = {
|
||||
let RwLock { ref inner, ref poison, ref data } = self;
|
||||
(ptr::read(inner), ptr::read(poison), ptr::read(data))
|
||||
};
|
||||
mem::forget(self);
|
||||
inner.lock.destroy(); // Keep in sync with the `Drop` impl.
|
||||
inner.destroy(); // Keep in sync with the `Drop` impl.
|
||||
drop(inner);
|
||||
|
||||
poison::map_result(inner.poison.borrow(), |_| data.into_inner())
|
||||
poison::map_result(poison.borrow(), |_| data.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,19 +283,18 @@ impl<T: ?Sized> RwLock<T> {
|
|||
#[stable(feature = "rwlock_get_mut", since = "1.6.0")]
|
||||
pub fn get_mut(&mut self) -> LockResult<&mut T> {
|
||||
// We know statically that there are no other references to `self`, so
|
||||
// there's no need to lock the inner StaticRwLock.
|
||||
// there's no need to lock the inner lock.
|
||||
let data = unsafe { &mut *self.data.get() };
|
||||
poison::map_result(self.inner.poison.borrow(), |_| data )
|
||||
poison::map_result(self.poison.borrow(), |_| data)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl<T: ?Sized> Drop for RwLock<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
// IMPORTANT: This code needs to be kept in sync with `RwLock::into_inner`.
|
||||
unsafe { self.inner.lock.destroy() }
|
||||
unsafe { self.inner.destroy() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,114 +318,23 @@ impl<T: Default> Default for RwLock<T> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Dummy(UnsafeCell<()>);
|
||||
unsafe impl Sync for Dummy {}
|
||||
static DUMMY: Dummy = Dummy(UnsafeCell::new(()));
|
||||
|
||||
#[unstable(feature = "static_rwlock",
|
||||
reason = "may be merged with RwLock in the future",
|
||||
issue = "27717")]
|
||||
#[rustc_deprecated(since = "1.10.0",
|
||||
reason = "the lazy-static crate suffices for static sync \
|
||||
primitives and eventually this type shouldn't \
|
||||
be necessary as `RwLock::new` in a static should \
|
||||
suffice")]
|
||||
#[allow(deprecated)]
|
||||
impl StaticRwLock {
|
||||
/// Creates a new rwlock.
|
||||
pub const fn new() -> StaticRwLock {
|
||||
StaticRwLock {
|
||||
lock: sys::RWLock::new(),
|
||||
poison: poison::Flag::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Locks this rwlock with shared read access, blocking the current thread
|
||||
/// until it can be acquired.
|
||||
///
|
||||
/// See `RwLock::read`.
|
||||
#[inline]
|
||||
pub fn read(&'static self) -> LockResult<RwLockReadGuard<'static, ()>> {
|
||||
unsafe {
|
||||
self.lock.read();
|
||||
RwLockReadGuard::new(self, &DUMMY.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock with shared read access.
|
||||
///
|
||||
/// See `RwLock::try_read`.
|
||||
#[inline]
|
||||
pub fn try_read(&'static self)
|
||||
-> TryLockResult<RwLockReadGuard<'static, ()>> {
|
||||
unsafe {
|
||||
if self.lock.try_read(){
|
||||
Ok(RwLockReadGuard::new(self, &DUMMY.0)?)
|
||||
} else {
|
||||
Err(TryLockError::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Locks this rwlock with exclusive write access, blocking the current
|
||||
/// thread until it can be acquired.
|
||||
///
|
||||
/// See `RwLock::write`.
|
||||
#[inline]
|
||||
pub fn write(&'static self) -> LockResult<RwLockWriteGuard<'static, ()>> {
|
||||
unsafe {
|
||||
self.lock.write();
|
||||
RwLockWriteGuard::new(self, &DUMMY.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to lock this rwlock with exclusive write access.
|
||||
///
|
||||
/// See `RwLock::try_write`.
|
||||
#[inline]
|
||||
pub fn try_write(&'static self)
|
||||
-> TryLockResult<RwLockWriteGuard<'static, ()>> {
|
||||
unsafe {
|
||||
if self.lock.try_write() {
|
||||
Ok(RwLockWriteGuard::new(self, &DUMMY.0)?)
|
||||
} else {
|
||||
Err(TryLockError::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deallocates all resources associated with this static lock.
|
||||
///
|
||||
/// This method is unsafe to call as there is no guarantee that there are no
|
||||
/// active users of the lock, and this also doesn't prevent any future users
|
||||
/// of this lock. This method is required to be called to not leak memory on
|
||||
/// all platforms.
|
||||
pub unsafe fn destroy(&'static self) {
|
||||
self.lock.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> {
|
||||
unsafe fn new(lock: &'rwlock StaticRwLock, data: &'rwlock UnsafeCell<T>)
|
||||
-> LockResult<RwLockReadGuard<'rwlock, T>> {
|
||||
unsafe fn new(lock: &'rwlock RwLock<T>)
|
||||
-> LockResult<RwLockReadGuard<'rwlock, T>> {
|
||||
poison::map_result(lock.poison.borrow(), |_| {
|
||||
RwLockReadGuard {
|
||||
__lock: lock,
|
||||
__data: &*data.get(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
|
||||
unsafe fn new(lock: &'rwlock StaticRwLock, data: &'rwlock UnsafeCell<T>)
|
||||
-> LockResult<RwLockWriteGuard<'rwlock, T>> {
|
||||
unsafe fn new(lock: &'rwlock RwLock<T>)
|
||||
-> LockResult<RwLockWriteGuard<'rwlock, T>> {
|
||||
poison::map_result(lock.poison.borrow(), |guard| {
|
||||
RwLockWriteGuard {
|
||||
__lock: lock,
|
||||
__data: &mut *data.get(),
|
||||
__poison: guard,
|
||||
}
|
||||
})
|
||||
|
@ -488,42 +345,43 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
|
|||
impl<'rwlock, T: ?Sized> Deref for RwLockReadGuard<'rwlock, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T { self.__data }
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.__lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'rwlock, T: ?Sized> Deref for RwLockWriteGuard<'rwlock, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T { self.__data }
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.__lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'rwlock, T: ?Sized> DerefMut for RwLockWriteGuard<'rwlock, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
self.__data
|
||||
unsafe { &mut *self.__lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, T: ?Sized> Drop for RwLockReadGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.__lock.lock.read_unlock(); }
|
||||
unsafe { self.__lock.inner.read_unlock(); }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
self.__lock.poison.done(&self.__poison);
|
||||
unsafe { self.__lock.lock.write_unlock(); }
|
||||
unsafe { self.__lock.inner.write_unlock(); }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
#![allow(deprecated)] // rand
|
||||
|
||||
|
@ -532,7 +390,7 @@ mod tests {
|
|||
use rand::{self, Rng};
|
||||
use sync::mpsc::channel;
|
||||
use thread;
|
||||
use sync::{Arc, RwLock, StaticRwLock, TryLockError};
|
||||
use sync::{Arc, RwLock, TryLockError};
|
||||
use sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
|
@ -547,32 +405,24 @@ mod tests {
|
|||
drop(l.write().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_smoke() {
|
||||
static R: StaticRwLock = StaticRwLock::new();
|
||||
drop(R.read().unwrap());
|
||||
drop(R.write().unwrap());
|
||||
drop((R.read().unwrap(), R.read().unwrap()));
|
||||
drop(R.write().unwrap());
|
||||
unsafe { R.destroy(); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frob() {
|
||||
static R: StaticRwLock = StaticRwLock::new();
|
||||
const N: usize = 10;
|
||||
const M: usize = 1000;
|
||||
|
||||
let r = Arc::new(RwLock::new(()));
|
||||
|
||||
let (tx, rx) = channel::<()>();
|
||||
for _ in 0..N {
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move|| {
|
||||
let r = r.clone();
|
||||
thread::spawn(move || {
|
||||
let mut rng = rand::thread_rng();
|
||||
for _ in 0..M {
|
||||
if rng.gen_weighted_bool(N) {
|
||||
drop(R.write().unwrap());
|
||||
drop(r.write().unwrap());
|
||||
} else {
|
||||
drop(R.read().unwrap());
|
||||
drop(r.read().unwrap());
|
||||
}
|
||||
}
|
||||
drop(tx);
|
||||
|
@ -580,7 +430,6 @@ mod tests {
|
|||
}
|
||||
drop(tx);
|
||||
let _ = rx.recv();
|
||||
unsafe { R.destroy(); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -8,22 +8,28 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use cell::Cell;
|
||||
use error::{Error};
|
||||
use fmt;
|
||||
use marker::Reflect;
|
||||
use sync::atomic::{AtomicBool, Ordering};
|
||||
use thread;
|
||||
|
||||
pub struct Flag { failed: Cell<bool> }
|
||||
pub struct Flag { failed: AtomicBool }
|
||||
|
||||
// This flag is only ever accessed with a lock previously held. Note that this
|
||||
// a totally private structure.
|
||||
unsafe impl Send for Flag {}
|
||||
unsafe impl Sync for Flag {}
|
||||
// Note that the Ordering uses to access the `failed` field of `Flag` below is
|
||||
// always `Relaxed`, and that's because this isn't actually protecting any data,
|
||||
// it's just a flag whether we've panicked or not.
|
||||
//
|
||||
// The actual location that this matters is when a mutex is **locked** which is
|
||||
// where we have external synchronization ensuring that we see memory
|
||||
// reads/writes to this flag.
|
||||
//
|
||||
// As a result, if it matters, we should see the correct value for `failed` in
|
||||
// all cases.
|
||||
|
||||
impl Flag {
|
||||
pub const fn new() -> Flag {
|
||||
Flag { failed: Cell::new(false) }
|
||||
Flag { failed: AtomicBool::new(false) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -39,13 +45,13 @@ impl Flag {
|
|||
#[inline]
|
||||
pub fn done(&self, guard: &Guard) {
|
||||
if !guard.panicking && thread::panicking() {
|
||||
self.failed.set(true);
|
||||
self.failed.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> bool {
|
||||
self.failed.get()
|
||||
self.failed.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ use io;
|
|||
use libc::c_void;
|
||||
use mem;
|
||||
use ptr;
|
||||
use sync::StaticMutex;
|
||||
use sys::c;
|
||||
use sys::dynamic_lib::DynamicLibrary;
|
||||
use sys::mutex::Mutex;
|
||||
|
||||
macro_rules! sym {
|
||||
($lib:expr, $e:expr, $t:ident) => (
|
||||
|
@ -101,53 +101,59 @@ impl Drop for Cleanup {
|
|||
pub fn write(w: &mut Write) -> io::Result<()> {
|
||||
// According to windows documentation, all dbghelp functions are
|
||||
// single-threaded.
|
||||
static LOCK: StaticMutex = StaticMutex::new();
|
||||
let _g = LOCK.lock();
|
||||
static LOCK: Mutex = Mutex::new();
|
||||
unsafe {
|
||||
LOCK.lock();
|
||||
let res = _write(w);
|
||||
LOCK.unlock();
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn _write(w: &mut Write) -> io::Result<()> {
|
||||
let dbghelp = match DynamicLibrary::open("dbghelp.dll") {
|
||||
Ok(lib) => lib,
|
||||
Err(..) => return Ok(()),
|
||||
};
|
||||
unsafe {
|
||||
// Fetch the symbols necessary from dbghelp.dll
|
||||
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn);
|
||||
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn);
|
||||
let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
|
||||
|
||||
// Allocate necessary structures for doing the stack walk
|
||||
let process = c::GetCurrentProcess();
|
||||
let thread = c::GetCurrentThread();
|
||||
let mut context: c::CONTEXT = mem::zeroed();
|
||||
c::RtlCaptureContext(&mut context);
|
||||
let mut frame: c::STACKFRAME64 = mem::zeroed();
|
||||
let image = init_frame(&mut frame, &context);
|
||||
// Fetch the symbols necessary from dbghelp.dll
|
||||
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn);
|
||||
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn);
|
||||
let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn);
|
||||
|
||||
// Initialize this process's symbols
|
||||
let ret = SymInitialize(process, ptr::null_mut(), c::TRUE);
|
||||
if ret != c::TRUE { return Ok(()) }
|
||||
let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
|
||||
// Allocate necessary structures for doing the stack walk
|
||||
let process = c::GetCurrentProcess();
|
||||
let thread = c::GetCurrentThread();
|
||||
let mut context: c::CONTEXT = mem::zeroed();
|
||||
c::RtlCaptureContext(&mut context);
|
||||
let mut frame: c::STACKFRAME64 = mem::zeroed();
|
||||
let image = init_frame(&mut frame, &context);
|
||||
|
||||
// And now that we're done with all the setup, do the stack walking!
|
||||
// Start from -1 to avoid printing this stack frame, which will
|
||||
// always be exactly the same.
|
||||
let mut i = -1;
|
||||
write!(w, "stack backtrace:\n")?;
|
||||
while StackWalk64(image, process, thread, &mut frame, &mut context,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut()) == c::TRUE {
|
||||
let addr = frame.AddrPC.Offset;
|
||||
if addr == frame.AddrReturn.Offset || addr == 0 ||
|
||||
frame.AddrReturn.Offset == 0 { break }
|
||||
// Initialize this process's symbols
|
||||
let ret = SymInitialize(process, ptr::null_mut(), c::TRUE);
|
||||
if ret != c::TRUE { return Ok(()) }
|
||||
let _c = Cleanup { handle: process, SymCleanup: SymCleanup };
|
||||
|
||||
i += 1;
|
||||
// And now that we're done with all the setup, do the stack walking!
|
||||
// Start from -1 to avoid printing this stack frame, which will
|
||||
// always be exactly the same.
|
||||
let mut i = -1;
|
||||
write!(w, "stack backtrace:\n")?;
|
||||
while StackWalk64(image, process, thread, &mut frame, &mut context,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut()) == c::TRUE {
|
||||
let addr = frame.AddrPC.Offset;
|
||||
if addr == frame.AddrReturn.Offset || addr == 0 ||
|
||||
frame.AddrReturn.Offset == 0 { break }
|
||||
|
||||
if i >= 0 {
|
||||
printing::print(w, i, addr - 1, process, &dbghelp)?;
|
||||
}
|
||||
i += 1;
|
||||
|
||||
if i >= 0 {
|
||||
printing::print(w, i, addr - 1, process, &dbghelp)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
// This test makes sure that the compiler doesn't crash when trying to assign
|
||||
// debug locations to const-expressions.
|
||||
|
||||
use std::sync::StaticMutex;
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
const CONSTANT: u64 = 3 + 4;
|
||||
|
@ -63,6 +62,5 @@ fn main() {
|
|||
let mut _string = STRING;
|
||||
let mut _vec = VEC;
|
||||
let mut _nested = NESTED;
|
||||
let mut _extern = StaticMutex::new();
|
||||
let mut _unsafe_cell = UNSAFE_CELL;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,6 @@ use std::sync;
|
|||
fn assert_both<T: Sync + Send>() {}
|
||||
|
||||
fn main() {
|
||||
assert_both::<sync::StaticMutex>();
|
||||
assert_both::<sync::StaticCondvar>();
|
||||
assert_both::<sync::StaticRwLock>();
|
||||
assert_both::<sync::Mutex<()>>();
|
||||
assert_both::<sync::Condvar>();
|
||||
assert_both::<sync::RwLock<()>>();
|
||||
|
|
Loading…
Add table
Reference in a new issue