diff --git a/src/doc/intro.md b/src/doc/intro.md index 5be97034357eb..a4e9d85bffdf8 100644 --- a/src/doc/intro.md +++ b/src/doc/intro.md @@ -483,7 +483,7 @@ fn main() { for i in range(0u, 3u) { let number = numbers.clone(); Thread::spawn(move || { - let mut array = number.lock(); + let mut array = number.lock().unwrap(); (*array)[i] += 1; diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 3e235caab18ad..13ee8c26075f3 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -58,7 +58,7 @@ //! let five = five.clone(); //! //! Thread::spawn(move || { -//! let mut number = five.lock(); +//! let mut number = five.lock().unwrap(); //! //! *number += 1; //! @@ -722,7 +722,7 @@ mod tests { let a = Arc::new(Cycle { x: Mutex::new(None) }); let b = a.clone().downgrade(); - *a.x.lock() = Some(b); + *a.x.lock().unwrap() = Some(b); // hopefully we don't double-free (or leak)... } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 513b955da3f58..99e11bf520205 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -98,7 +98,7 @@ impl SharedEmitter { } fn dump(&mut self, handler: &Handler) { - let mut buffer = self.buffer.lock(); + let mut buffer = self.buffer.lock().unwrap(); for diag in buffer.iter() { match diag.code { Some(ref code) => { @@ -123,7 +123,7 @@ impl Emitter for SharedEmitter { msg: &str, code: Option<&str>, lvl: Level) { assert!(cmsp.is_none(), "SharedEmitter doesn't support spans"); - self.buffer.lock().push(Diagnostic { + self.buffer.lock().unwrap().push(Diagnostic { msg: msg.to_string(), code: code.map(|s| s.to_string()), lvl: lvl, @@ -915,7 +915,7 @@ fn run_work_multithreaded(sess: &Session, loop { // Avoid holding the lock for the entire duration of the match. - let maybe_work = work_items_arc.lock().pop(); + let maybe_work = work_items_arc.lock().unwrap().pop(); match maybe_work { Some(work) => { execute_work_item(&cgcx, work); diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs index 1022694e634f6..3f23ec5dc6659 100644 --- a/src/libstd/comm/shared.rs +++ b/src/libstd/comm/shared.rs @@ -86,7 +86,7 @@ impl Packet { // and that could cause problems on platforms where it is // represented by opaque data structure pub fn postinit_lock(&self) -> MutexGuard<()> { - self.select_lock.lock() + self.select_lock.lock().unwrap() } // This function is used at the creation of a shared packet to inherit a @@ -435,7 +435,7 @@ impl Packet { // about looking at and dealing with to_wake. Once we have acquired the // lock, we are guaranteed that inherit_blocker is done. { - let _guard = self.select_lock.lock(); + let _guard = self.select_lock.lock().unwrap(); } // Like the stream implementation, we want to make sure that the count diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs index 88338849965b0..82ec1814ebde7 100644 --- a/src/libstd/comm/sync.rs +++ b/src/libstd/comm/sync.rs @@ -121,9 +121,9 @@ fn wait<'a, 'b, T: Send>(lock: &'a Mutex>, NoneBlocked => {} _ => unreachable!(), } - drop(guard); // unlock - wait_token.wait(); // block - lock.lock() // relock + drop(guard); // unlock + wait_token.wait(); // block + lock.lock().unwrap() // relock } /// Wakes up a thread, dropping the lock at the correct time @@ -161,7 +161,7 @@ impl Packet { fn acquire_send_slot(&self) -> MutexGuard> { let mut node = Node { token: None, next: 0 as *mut Node }; loop { - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); // are we ready to go? if guard.disconnected || guard.buf.size() < guard.buf.cap() { return guard; @@ -202,7 +202,7 @@ impl Packet { } pub fn try_send(&self, t: T) -> Result<(), super::TrySendError> { - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); if guard.disconnected { Err(super::RecvDisconnected(t)) } else if guard.buf.size() == guard.buf.cap() { @@ -239,7 +239,7 @@ impl Packet { // When reading this, remember that there can only ever be one receiver at // time. pub fn recv(&self) -> Result { - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); // Wait for the buffer to have something in it. No need for a while loop // because we're the only receiver. @@ -258,7 +258,7 @@ impl Packet { } pub fn try_recv(&self) -> Result { - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); // Easy cases first if guard.disconnected { return Err(Disconnected) } @@ -315,7 +315,7 @@ impl Packet { } // Not much to do other than wake up a receiver if one's there - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); if guard.disconnected { return } guard.disconnected = true; match mem::replace(&mut guard.blocker, NoneBlocked) { @@ -326,7 +326,7 @@ impl Packet { } pub fn drop_port(&self) { - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); if guard.disconnected { return } guard.disconnected = true; @@ -372,14 +372,14 @@ impl Packet { // If Ok, the value is whether this port has data, if Err, then the upgraded // port needs to be checked instead of this one. pub fn can_recv(&self) -> bool { - let guard = self.lock.lock(); + let guard = self.lock.lock().unwrap(); guard.disconnected || guard.buf.size() > 0 } // Attempts to start selection on this port. This can either succeed or fail // because there is data waiting. pub fn start_selection(&self, token: SignalToken) -> StartResult { - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); if guard.disconnected || guard.buf.size() > 0 { Abort } else { @@ -397,7 +397,7 @@ impl Packet { // // The return value indicates whether there's data on this port. pub fn abort_selection(&self) -> bool { - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => true, BlockedSender(token) => { @@ -413,7 +413,7 @@ impl Packet { impl Drop for Packet { fn drop(&mut self) { assert_eq!(self.channels.load(atomic::SeqCst), 0); - let mut guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); assert!(guard.queue.dequeue().is_none()); assert!(guard.canceled.is_none()); } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 6bd721599f341..6c8e4eea40fdc 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -146,7 +146,7 @@ impl StdinReader { /// ``` pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> { StdinReaderGuard { - inner: self.inner.lock() + inner: self.inner.lock().unwrap() } } @@ -155,7 +155,7 @@ impl StdinReader { /// The read is performed atomically - concurrent read calls in other /// threads will not interleave with this one. pub fn read_line(&mut self) -> IoResult { - self.inner.lock().0.read_line() + self.inner.lock().unwrap().0.read_line() } /// Like `Buffer::read_until`. @@ -163,7 +163,7 @@ impl StdinReader { /// The read is performed atomically - concurrent read calls in other /// threads will not interleave with this one. pub fn read_until(&mut self, byte: u8) -> IoResult> { - self.inner.lock().0.read_until(byte) + self.inner.lock().unwrap().0.read_until(byte) } /// Like `Buffer::read_char`. @@ -171,13 +171,13 @@ impl StdinReader { /// The read is performed atomically - concurrent read calls in other /// threads will not interleave with this one. pub fn read_char(&mut self) -> IoResult { - self.inner.lock().0.read_char() + self.inner.lock().unwrap().0.read_char() } } impl Reader for StdinReader { fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner.lock().0.read(buf) + self.inner.lock().unwrap().0.read(buf) } // We have to manually delegate all of these because the default impls call @@ -185,23 +185,23 @@ impl Reader for StdinReader { // incur the costs of repeated locking). fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult { - self.inner.lock().0.read_at_least(min, buf) + self.inner.lock().unwrap().0.read_at_least(min, buf) } fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec) -> IoResult { - self.inner.lock().0.push_at_least(min, len, buf) + self.inner.lock().unwrap().0.push_at_least(min, len, buf) } fn read_to_end(&mut self) -> IoResult> { - self.inner.lock().0.read_to_end() + self.inner.lock().unwrap().0.read_to_end() } fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult { - self.inner.lock().0.read_le_uint_n(nbytes) + self.inner.lock().unwrap().0.read_le_uint_n(nbytes) } fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult { - self.inner.lock().0.read_be_uint_n(nbytes) + self.inner.lock().unwrap().0.read_be_uint_n(nbytes) } } diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs index 6cdb199819aff..4091f0df39597 100644 --- a/src/libstd/sync/barrier.rs +++ b/src/libstd/sync/barrier.rs @@ -69,7 +69,7 @@ impl Barrier { /// Barriers are re-usable after all threads have rendezvoused once, and can /// be used continuously. pub fn wait(&self) { - let mut lock = self.lock.lock(); + let mut lock = self.lock.lock().unwrap(); let local_gen = lock.generation_id; lock.count += 1; if lock.count < self.num_threads { @@ -77,7 +77,7 @@ impl Barrier { // http://en.wikipedia.org/wiki/Spurious_wakeup while local_gen == lock.generation_id && lock.count < self.num_threads { - self.cvar.wait(&lock); + lock = self.cvar.wait(lock).unwrap(); } } else { lock.count = 0; diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index f1940bfd829b7..15faf5be258f5 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -11,10 +11,11 @@ use prelude::*; use sync::atomic::{mod, AtomicUint}; -use sync::{mutex, StaticMutexGuard}; +use sync::poison::{mod, LockResult}; use sys_common::condvar as sys; use sys_common::mutex as sys_mutex; use time::Duration; +use sync::{mutex, MutexGuard}; /// A Condition Variable /// @@ -44,18 +45,19 @@ use time::Duration; /// // Inside of our lock, spawn a new thread, and then wait for it to start /// Thread::spawn(move|| { /// let &(ref lock, ref cvar) = &*pair2; -/// let mut started = lock.lock(); +/// let mut started = lock.lock().unwrap(); /// *started = true; /// cvar.notify_one(); /// }).detach(); /// /// // wait for the thread to start up /// let &(ref lock, ref cvar) = &*pair; -/// let started = lock.lock(); +/// let mut started = lock.lock().unwrap(); /// while !*started { -/// cvar.wait(&started); +/// started = cvar.wait(started).unwrap(); /// } /// ``` +#[stable] pub struct Condvar { inner: Box } unsafe impl Send for Condvar {} @@ -73,6 +75,7 @@ unsafe impl Sync for Condvar {} /// /// static CVAR: StaticCondvar = CONDVAR_INIT; /// ``` +#[unstable = "may be merged with Condvar in the future"] pub struct StaticCondvar { inner: sys::Condvar, mutex: AtomicUint, @@ -82,24 +85,16 @@ unsafe impl Send for StaticCondvar {} unsafe impl Sync for StaticCondvar {} /// Constant initializer for a statically allocated condition variable. +#[unstable = "may be merged with Condvar in the future"] pub const CONDVAR_INIT: StaticCondvar = StaticCondvar { inner: sys::CONDVAR_INIT, mutex: atomic::INIT_ATOMIC_UINT, }; -/// A trait for vaules which can be passed to the waiting methods of condition -/// variables. This is implemented by the mutex guards in this module. -/// -/// Note that this trait should likely not be implemented manually unless you -/// really know what you're doing. -pub trait AsMutexGuard { - #[allow(missing_docs)] - unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard; -} - impl Condvar { /// Creates a new condition variable which is ready to be waited on and /// notified. + #[stable] pub fn new() -> Condvar { Condvar { inner: box StaticCondvar { @@ -113,8 +108,8 @@ impl Condvar { /// notification. /// /// This function will atomically unlock the mutex specified (represented by - /// `guard`) and block the current thread. This means that any calls to - /// `notify_*()` which happen logically after the mutex is unlocked are + /// `mutex_guard`) and block the current thread. This means that any calls + /// to `notify_*()` which happen logically after the mutex is unlocked are /// candidates to wake this thread up. When this function call returns, the /// lock specified will have been re-acquired. /// @@ -123,16 +118,24 @@ impl Condvar { /// the predicate must always be checked each time this function returns to /// protect against spurious wakeups. /// + /// # Failure + /// + /// This function will return an error if the mutex being waited on is + /// poisoned when this thread re-acquires the lock. For more information, + /// see information about poisoning on the Mutex type. + /// /// # Panics /// /// This function will `panic!()` if it is used with more than one mutex /// over time. Each condition variable is dynamically bound to exactly one /// mutex to ensure defined behavior across platforms. If this functionality /// is not desired, then unsafe primitives in `sys` are provided. - pub fn wait(&self, mutex_guard: &T) { + #[stable] + pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) + -> LockResult> { unsafe { let me: &'static Condvar = &*(self as *const _); - me.inner.wait(mutex_guard) + me.inner.wait(guard) } } @@ -156,11 +159,11 @@ impl Condvar { // provide. There are also additional concerns about the unix-specific // implementation which may need to be addressed. #[allow(dead_code)] - fn wait_timeout(&self, mutex_guard: &T, - dur: Duration) -> bool { + fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, dur: Duration) + -> LockResult<(MutexGuard<'a, T>, bool)> { unsafe { let me: &'static Condvar = &*(self as *const _); - me.inner.wait_timeout(mutex_guard, dur) + me.inner.wait_timeout(guard, dur) } } @@ -171,6 +174,7 @@ impl Condvar { /// `notify_one` are not buffered in any way. /// /// To wake up all threads, see `notify_one()`. + #[stable] pub fn notify_one(&self) { unsafe { self.inner.inner.notify_one() } } /// Wake up all blocked threads on this condvar. @@ -180,6 +184,7 @@ impl Condvar { /// way. /// /// To wake up only one thread, see `notify_one()`. + #[stable] pub fn notify_all(&self) { unsafe { self.inner.inner.notify_all() } } } @@ -194,13 +199,19 @@ impl StaticCondvar { /// notification. /// /// See `Condvar::wait`. - pub fn wait(&'static self, mutex_guard: &T) { - unsafe { - let lock = mutex_guard.as_mutex_guard(); - let sys = mutex::guard_lock(lock); - self.verify(sys); - self.inner.wait(sys); - (*mutex::guard_poison(lock)).check("mutex"); + #[unstable = "may be merged with Condvar in the future"] + pub fn wait<'a, T>(&'static self, guard: MutexGuard<'a, T>) + -> LockResult> { + let poisoned = unsafe { + let lock = mutex::guard_lock(&guard); + self.verify(lock); + self.inner.wait(lock); + mutex::guard_poison(&guard).get() + }; + if poisoned { + Err(poison::new_poison_error(guard)) + } else { + Ok(guard) } } @@ -209,26 +220,31 @@ impl StaticCondvar { /// /// See `Condvar::wait_timeout`. #[allow(dead_code)] // may want to stabilize this later, see wait_timeout above - fn wait_timeout(&'static self, mutex_guard: &T, - dur: Duration) -> bool { - unsafe { - let lock = mutex_guard.as_mutex_guard(); - let sys = mutex::guard_lock(lock); - self.verify(sys); - let ret = self.inner.wait_timeout(sys, dur); - (*mutex::guard_poison(lock)).check("mutex"); - return ret; + fn wait_timeout<'a, T>(&'static self, guard: MutexGuard<'a, T>, dur: Duration) + -> LockResult<(MutexGuard<'a, T>, bool)> { + let (poisoned, success) = unsafe { + let lock = mutex::guard_lock(&guard); + self.verify(lock); + let success = self.inner.wait_timeout(lock, dur); + (mutex::guard_poison(&guard).get(), success) + }; + if poisoned { + Err(poison::new_poison_error((guard, success))) + } else { + Ok((guard, success)) } } /// Wake up one blocked thread on this condvar. /// /// See `Condvar::notify_one`. + #[unstable = "may be merged with Condvar in the future"] pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } } /// Wake up all blocked threads on this condvar. /// /// See `Condvar::notify_all`. + #[unstable = "may be merged with Condvar in the future"] pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } } /// Deallocate all resources associated with this static condvar. @@ -237,6 +253,7 @@ impl StaticCondvar { /// 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 = "may be merged with Condvar in the future"] pub unsafe fn destroy(&'static self) { self.inner.destroy() } @@ -288,12 +305,12 @@ mod tests { static C: StaticCondvar = CONDVAR_INIT; static M: StaticMutex = MUTEX_INIT; - let g = M.lock(); + let g = M.lock().unwrap(); spawn(move|| { - let _g = M.lock(); + let _g = M.lock().unwrap(); C.notify_one(); }); - C.wait(&g); + let g = C.wait(g).unwrap(); drop(g); unsafe { C.destroy(); M.destroy(); } } @@ -309,13 +326,13 @@ mod tests { let tx = tx.clone(); spawn(move|| { let &(ref lock, ref cond) = &*data; - let mut cnt = lock.lock(); + let mut cnt = lock.lock().unwrap(); *cnt += 1; if *cnt == N { tx.send(()); } while *cnt != 0 { - cond.wait(&cnt); + cnt = cond.wait(cnt).unwrap(); } tx.send(()); }); @@ -324,7 +341,7 @@ mod tests { let &(ref lock, ref cond) = &*data; rx.recv(); - let mut cnt = lock.lock(); + let mut cnt = lock.lock().unwrap(); *cnt = 0; cond.notify_all(); drop(cnt); @@ -339,13 +356,15 @@ mod tests { static C: StaticCondvar = CONDVAR_INIT; static M: StaticMutex = MUTEX_INIT; - let g = M.lock(); - assert!(!C.wait_timeout(&g, Duration::nanoseconds(1000))); + let g = M.lock().unwrap(); + let (g, success) = C.wait_timeout(g, Duration::nanoseconds(1000)).unwrap(); + assert!(!success); spawn(move|| { - let _g = M.lock(); + let _g = M.lock().unwrap(); C.notify_one(); }); - assert!(C.wait_timeout(&g, Duration::days(1))); + let (g, success) = C.wait_timeout(g, Duration::days(1)).unwrap(); + assert!(success); drop(g); unsafe { C.destroy(); M.destroy(); } } @@ -357,15 +376,15 @@ mod tests { static M2: StaticMutex = MUTEX_INIT; static C: StaticCondvar = CONDVAR_INIT; - let g = M1.lock(); + let mut g = M1.lock().unwrap(); spawn(move|| { - let _g = M1.lock(); + let _g = M1.lock().unwrap(); C.notify_one(); }); - C.wait(&g); + g = C.wait(g).unwrap(); drop(g); - C.wait(&M2.lock()); + C.wait(M2.lock().unwrap()).unwrap(); } } diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs index 7605a6a96a005..092acc7ff2569 100644 --- a/src/libstd/sync/mod.rs +++ b/src/libstd/sync/mod.rs @@ -19,14 +19,15 @@ pub use alloc::arc::{Arc, Weak}; -pub use self::mutex::{Mutex, MutexGuard, StaticMutex, StaticMutexGuard, MUTEX_INIT}; +pub use self::mutex::{Mutex, MutexGuard, StaticMutex}; +pub use self::mutex::MUTEX_INIT; pub use self::rwlock::{RWLock, StaticRWLock, RWLOCK_INIT}; pub use self::rwlock::{RWLockReadGuard, RWLockWriteGuard}; -pub use self::rwlock::{StaticRWLockReadGuard, StaticRWLockWriteGuard}; -pub use self::condvar::{Condvar, StaticCondvar, CONDVAR_INIT, AsMutexGuard}; +pub use self::condvar::{Condvar, StaticCondvar, CONDVAR_INIT}; pub use self::once::{Once, ONCE_INIT}; pub use self::semaphore::{Semaphore, SemaphoreGuard}; pub use self::barrier::Barrier; +pub use self::poison::{PoisonError, TryLockError, TryLockResult, LockResult}; pub use self::future::Future; pub use self::task_pool::TaskPool; diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 4d2fbfc4055f4..32c2c67152fe4 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -12,7 +12,7 @@ use prelude::*; use cell::UnsafeCell; use kinds::marker; -use sync::{poison, AsMutexGuard}; +use sync::poison::{mod, TryLockError, TryLockResult, LockResult}; use sys_common::mutex as sys; /// A mutual exclusion primitive useful for protecting shared data @@ -26,12 +26,23 @@ use sys_common::mutex as sys; /// /// # Poisoning /// -/// In order to prevent access to otherwise invalid data, each mutex will -/// propagate any panics which occur while the lock is held. Once a thread has -/// panicked while holding the lock, then all other threads will immediately -/// panic as well once they hold the lock. +/// The mutexes in this module implement a strategy called "poisoning" where a +/// mutex is considered poisoned whenever a thread panics while holding the +/// lock. Once a mutex is poisoned, all other tasks are unable to access the +/// data by default as it is likely tainted (some invariant is not being +/// upheld). /// -/// # Example +/// For a mutex, this means that the `lock` and `try_lock` methods return a +/// `Result` which indicates whether a mutex has been poisoned or not. Most +/// usage of a mutex will simply `unwrap()` these results, propagating panics +/// among threads to ensure that a possibly invalid invariant is not witnessed. +/// +/// A poisoned mutex, however, does not prevent all access to the underlying +/// data. The `PoisonError` type has an `into_guard` method which will return +/// the guard that would have otherwise been returned on a successful lock. This +/// allows access to the data, despite the lock being poisoned. +/// +/// # Examples /// /// ```rust /// use std::sync::{Arc, Mutex}; @@ -48,11 +59,14 @@ use sys_common::mutex as sys; /// let (tx, rx) = channel(); /// for _ in range(0u, 10) { /// let (data, tx) = (data.clone(), tx.clone()); -/// Thread::spawn(move|| { +/// Thread::spawn(move || { /// // The shared static can only be accessed once the lock is held. /// // Our non-atomic increment is safe because we're the only thread /// // which can access the shared state when the lock is held. -/// let mut data = data.lock(); +/// // +/// // We unwrap() the return value to assert that we are not expecting +/// // tasks to ever fail while holding the lock. +/// let mut data = data.lock().unwrap(); /// *data += 1; /// if *data == N { /// tx.send(()); @@ -63,6 +77,36 @@ use sys_common::mutex as sys; /// /// rx.recv(); /// ``` +/// +/// To recover from a poisoned mutex: +/// +/// ```rust +/// use std::sync::{Arc, Mutex}; +/// use std::thread::Thread; +/// +/// let lock = Arc::new(Mutex::new(0u)); +/// let lock2 = lock.clone(); +/// +/// let _ = Thread::spawn(move || -> () { +/// // This thread will acquire the mutex first, unwrapping the result of +/// // `lock` because the lock has not been poisoned. +/// let _lock = lock2.lock().unwrap(); +/// +/// // This panic while holding the lock (`_guard` is in scope) will poison +/// // the mutex. +/// panic!(); +/// }).join(); +/// +/// // The lock is poisoned by this point, but the returned result can be +/// // pattern matched on to return the underlying guard on both branches. +/// let mut guard = match lock.lock() { +/// Ok(guard) => guard, +/// Err(poisoned) => poisoned.into_guard(), +/// }; +/// +/// *guard += 1; +/// ``` +#[stable] pub struct Mutex { // 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 @@ -93,14 +137,15 @@ unsafe impl Sync for Mutex { } /// static LOCK: StaticMutex = MUTEX_INIT; /// /// { -/// let _g = LOCK.lock(); +/// let _g = LOCK.lock().unwrap(); /// // do some productive work /// } /// // lock is unlocked here. /// ``` +#[unstable = "may be merged with Mutex in the future"] pub struct StaticMutex { lock: sys::Mutex, - poison: UnsafeCell, + poison: poison::Flag, } unsafe impl Sync for StaticMutex {} @@ -111,31 +156,27 @@ unsafe impl Sync for StaticMutex {} /// The data protected by the mutex can be access through this guard via its /// Deref and DerefMut implementations #[must_use] +#[stable] pub struct MutexGuard<'a, T: 'a> { // funny underscores due to how Deref/DerefMut currently work (they // disregard field privacy). - __lock: &'a Mutex, - __guard: StaticMutexGuard, -} - -/// An RAII implementation of a "scoped lock" of a static mutex. When this -/// structure is dropped (falls out of scope), the lock will be unlocked. -#[must_use] -pub struct StaticMutexGuard { - lock: &'static sys::Mutex, - marker: marker::NoSend, - poison: poison::Guard<'static>, + __lock: &'a StaticMutex, + __data: &'a UnsafeCell, + __poison: poison::Guard, + __marker: marker::NoSend, } /// Static initialization of a mutex. This constant can be used to initialize /// other mutex constants. +#[unstable = "may be merged with Mutex in the future"] pub const MUTEX_INIT: StaticMutex = StaticMutex { lock: sys::MUTEX_INIT, - poison: UnsafeCell { value: poison::Flag { failed: false } }, + poison: poison::FLAG_INIT, }; impl Mutex { /// Creates a new mutex in an unlocked state ready for use. + #[stable] pub fn new(t: T) -> Mutex { Mutex { inner: box MUTEX_INIT, @@ -150,15 +191,14 @@ impl Mutex { /// held. An RAII guard is returned to allow scoped unlock of the lock. When /// the guard goes out of scope, the mutex will be unlocked. /// - /// # Panics + /// # Failure /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will immediately panic once the mutex is acquired. - pub fn lock(&self) -> MutexGuard { - unsafe { - let lock: &'static StaticMutex = &*(&*self.inner as *const _); - MutexGuard::new(self, lock.lock()) - } + /// this call will return an error once the mutex is acquired. + #[stable] + pub fn lock(&self) -> LockResult> { + unsafe { self.inner.lock.lock() } + MutexGuard::new(&*self.inner, &self.data) } /// Attempts to acquire this lock. @@ -169,17 +209,17 @@ impl Mutex { /// /// This function does not block. /// - /// # Panics + /// # Failure /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will immediately panic if the mutex would otherwise be + /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn try_lock(&self) -> Option> { - unsafe { - let lock: &'static StaticMutex = &*(&*self.inner as *const _); - lock.try_lock().map(|guard| { - MutexGuard::new(self, guard) - }) + #[stable] + pub fn try_lock(&self) -> TryLockResult> { + if unsafe { self.inner.lock.try_lock() } { + Ok(try!(MutexGuard::new(&*self.inner, &self.data))) + } else { + Err(TryLockError::WouldBlock) } } } @@ -194,19 +234,25 @@ impl Drop for Mutex { } } +static DUMMY: UnsafeCell<()> = UnsafeCell { value: () }; + impl StaticMutex { /// Acquires this lock, see `Mutex::lock` - pub fn lock(&'static self) -> StaticMutexGuard { + #[inline] + #[unstable = "may be merged with Mutex in the future"] + pub fn lock(&'static self) -> LockResult> { unsafe { self.lock.lock() } - StaticMutexGuard::new(self) + MutexGuard::new(self, &DUMMY) } /// Attempts to grab this lock, see `Mutex::try_lock` - pub fn try_lock(&'static self) -> Option { + #[inline] + #[unstable = "may be merged with Mutex in the future"] + pub fn try_lock(&'static self) -> TryLockResult> { if unsafe { self.lock.try_lock() } { - Some(StaticMutexGuard::new(self)) + Ok(try!(MutexGuard::new(self, &DUMMY))) } else { - None + Err(TryLockError::WouldBlock) } } @@ -220,61 +266,54 @@ impl StaticMutex { /// *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. + #[unstable = "may be merged with Mutex in the future"] pub unsafe fn destroy(&'static self) { self.lock.destroy() } } impl<'mutex, T> MutexGuard<'mutex, T> { - fn new(lock: &Mutex, guard: StaticMutexGuard) -> MutexGuard { - MutexGuard { __lock: lock, __guard: guard } + fn new(lock: &'mutex StaticMutex, data: &'mutex UnsafeCell) + -> LockResult> { + poison::map_result(lock.poison.borrow(), |guard| { + MutexGuard { + __lock: lock, + __data: data, + __poison: guard, + __marker: marker::NoSend, + } + }) } } -impl<'mutex, T> AsMutexGuard for MutexGuard<'mutex, T> { - unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { &self.__guard } -} - impl<'mutex, T> Deref for MutexGuard<'mutex, T> { - fn deref<'a>(&'a self) -> &'a T { unsafe { &*self.__lock.data.get() } } + fn deref<'a>(&'a self) -> &'a T { + unsafe { &*self.__data.get() } + } } impl<'mutex, T> DerefMut for MutexGuard<'mutex, T> { fn deref_mut<'a>(&'a mut self) -> &'a mut T { - unsafe { &mut *self.__lock.data.get() } + unsafe { &mut *self.__data.get() } } } -impl StaticMutexGuard { - fn new(lock: &'static StaticMutex) -> StaticMutexGuard { +#[unsafe_destructor] +impl<'a, T> Drop for MutexGuard<'a, T> { + #[inline] + fn drop(&mut self) { unsafe { - let guard = StaticMutexGuard { - lock: &lock.lock, - marker: marker::NoSend, - poison: (*lock.poison.get()).borrow(), - }; - guard.poison.check("mutex"); - return guard; + self.__lock.poison.done(&self.__poison); + self.__lock.lock.unlock(); } } } -pub fn guard_lock(guard: &StaticMutexGuard) -> &sys::Mutex { guard.lock } -pub fn guard_poison(guard: &StaticMutexGuard) -> &poison::Guard { - &guard.poison +pub fn guard_lock<'a, T>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { + &guard.__lock.lock } -impl AsMutexGuard for StaticMutexGuard { - unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { self } -} - -#[unsafe_destructor] -impl Drop for StaticMutexGuard { - fn drop(&mut self) { - unsafe { - self.poison.done(); - self.lock.unlock(); - } - } +pub fn guard_poison<'a, T>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { + &guard.__lock.poison } #[cfg(test)] @@ -292,16 +331,16 @@ mod test { #[test] fn smoke() { let m = Mutex::new(()); - drop(m.lock()); - drop(m.lock()); + drop(m.lock().unwrap()); + drop(m.lock().unwrap()); } #[test] fn smoke_static() { static M: StaticMutex = MUTEX_INIT; unsafe { - drop(M.lock()); - drop(M.lock()); + drop(M.lock().unwrap()); + drop(M.lock().unwrap()); M.destroy(); } } @@ -316,7 +355,7 @@ mod test { fn inc() { for _ in range(0, J) { unsafe { - let _g = M.lock(); + let _g = M.lock().unwrap(); CNT += 1; } } @@ -343,7 +382,7 @@ mod test { #[test] fn try_lock() { let m = Mutex::new(()); - assert!(m.try_lock().is_some()); + *m.try_lock().unwrap() = (); } #[test] @@ -355,22 +394,21 @@ mod test { // wait until parent gets in rx.recv(); let &(ref lock, ref cvar) = &*packet2.0; - let mut lock = lock.lock(); + let mut lock = lock.lock().unwrap(); *lock = true; cvar.notify_one(); }); let &(ref lock, ref cvar) = &*packet.0; - let lock = lock.lock(); + let mut lock = lock.lock().unwrap(); tx.send(()); assert!(!*lock); while !*lock { - cvar.wait(&lock); + lock = cvar.wait(lock).unwrap(); } } #[test] - #[should_fail] fn test_arc_condvar_poison() { let packet = Packet(Arc::new((Mutex::new(1i), Condvar::new()))); let packet2 = Packet(packet.0.clone()); @@ -379,31 +417,35 @@ mod test { spawn(move|| { rx.recv(); let &(ref lock, ref cvar) = &*packet2.0; - let _g = lock.lock(); + let _g = lock.lock().unwrap(); cvar.notify_one(); // Parent should fail when it wakes up. panic!(); }); let &(ref lock, ref cvar) = &*packet.0; - let lock = lock.lock(); + let mut lock = lock.lock().unwrap(); tx.send(()); while *lock == 1 { - cvar.wait(&lock); + match cvar.wait(lock) { + Ok(l) => { + lock = l; + assert_eq!(*lock, 1); + } + Err(..) => break, + } } } #[test] - #[should_fail] fn test_mutex_arc_poison() { let arc = Arc::new(Mutex::new(1i)); let arc2 = arc.clone(); - let _ = Thread::spawn(move|| { - let lock = arc2.lock(); + Thread::spawn(move|| { + let lock = arc2.lock().unwrap(); assert_eq!(*lock, 2); }).join(); - let lock = arc.lock(); - assert_eq!(*lock, 1); + assert!(arc.lock().is_err()); } #[test] @@ -414,8 +456,8 @@ mod test { let arc2 = Arc::new(Mutex::new(arc)); let (tx, rx) = channel(); spawn(move|| { - let lock = arc2.lock(); - let lock2 = lock.deref().lock(); + let lock = arc2.lock().unwrap(); + let lock2 = lock.deref().lock().unwrap(); assert_eq!(*lock2, 1); tx.send(()); }); @@ -432,13 +474,13 @@ mod test { } impl Drop for Unwinder { fn drop(&mut self) { - *self.i.lock() += 1; + *self.i.lock().unwrap() += 1; } } let _u = Unwinder { i: arc2 }; panic!(); }).join(); - let lock = arc.lock(); + let lock = arc.lock().unwrap(); assert_eq!(*lock, 2); } } diff --git a/src/libstd/sync/poison.rs b/src/libstd/sync/poison.rs index ad08e9873faad..edf16d99f4966 100644 --- a/src/libstd/sync/poison.rs +++ b/src/libstd/sync/poison.rs @@ -8,31 +8,127 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use prelude::*; + +use cell::UnsafeCell; +use error::FromError; +use fmt; use thread::Thread; -pub struct Flag { pub failed: bool } +pub struct Flag { failed: UnsafeCell } +pub const FLAG_INIT: Flag = Flag { failed: UnsafeCell { value: false } }; impl Flag { - pub fn borrow(&mut self) -> Guard { - Guard { flag: &mut self.failed, panicking: Thread::panicking() } + #[inline] + pub fn borrow(&self) -> LockResult { + let ret = Guard { panicking: Thread::panicking() }; + if unsafe { *self.failed.get() } { + Err(new_poison_error(ret)) + } else { + Ok(ret) + } + } + + #[inline] + pub fn done(&self, guard: &Guard) { + if !guard.panicking && Thread::panicking() { + unsafe { *self.failed.get() = true; } + } + } + + #[inline] + pub fn get(&self) -> bool { + unsafe { *self.failed.get() } } } -pub struct Guard<'a> { - flag: &'a mut bool, +#[allow(missing_copy_implementations)] +pub struct Guard { panicking: bool, } -impl<'a> Guard<'a> { - pub fn check(&self, name: &str) { - if *self.flag { - panic!("poisoned {} - another task failed inside", name); - } +/// A type of error which can be returned whenever a lock is acquired. +/// +/// Both Mutexes and RWLocks are poisoned whenever a task fails while the lock +/// is held. The precise semantics for when a lock is poisoned is documented on +/// each lock, but once a lock is poisoned then all future acquisitions will +/// return this error. +#[stable] +pub struct PoisonError { + guard: T, +} + +/// An enumeration of possible errors which can occur while calling the +/// `try_lock` method. +#[stable] +pub enum TryLockError { + /// The lock could not be acquired because another task failed while holding + /// the lock. + #[stable] + Poisoned(PoisonError), + /// The lock could not be acquired at this time because the operation would + /// otherwise block. + #[stable] + WouldBlock, +} + +/// A type alias for the result of a lock method which can be poisoned. +/// +/// The `Ok` variant of this result indicates that the primitive was not +/// poisoned, and the `Guard` is contained within. The `Err` variant indicates +/// that the primitive was poisoned. Note that the `Err` variant *also* carries +/// the associated guard, and it can be acquired through the `into_inner` +/// method. +#[stable] +pub type LockResult = Result>; + +/// A type alias for the result of a nonblocking locking method. +/// +/// For more information, see `LockResult`. A `TryLockResult` doesn't +/// necessarily hold the associated guard in the `Err` type as the lock may not +/// have been acquired for other reasons. +#[stable] +pub type TryLockResult = Result>; + +impl fmt::Show for PoisonError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "poisoned lock: another task failed inside".fmt(f) + } +} + +impl PoisonError { + /// Consumes this error indicating that a lock is poisoned, returning the + /// underlying guard to allow access regardless. + #[stable] + pub fn into_guard(self) -> T { self.guard } +} + +impl FromError> for TryLockError { + fn from_error(err: PoisonError) -> TryLockError { + TryLockError::Poisoned(err) } +} - pub fn done(&mut self) { - if !self.panicking && Thread::panicking() { - *self.flag = true; +impl fmt::Show for TryLockError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + TryLockError::Poisoned(ref p) => p.fmt(f), + TryLockError::WouldBlock => { + "try_lock failed because the operation would block".fmt(f) + } } } } + +pub fn new_poison_error(guard: T) -> PoisonError { + PoisonError { guard: guard } +} + +pub fn map_result(result: LockResult, f: F) + -> LockResult + where F: FnOnce(T) -> U { + match result { + Ok(t) => Ok(f(t)), + Err(PoisonError { guard }) => Err(new_poison_error(f(guard))) + } +} diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index 76d05d9bfd419..29cc1f8563f9a 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -10,10 +10,10 @@ use prelude::*; -use kinds::marker; use cell::UnsafeCell; +use kinds::marker; +use sync::poison::{mod, LockResult, TryLockError, TryLockResult}; use sys_common::rwlock as sys; -use sync::poison; /// A reader-writer lock /// @@ -28,12 +28,14 @@ use sync::poison; /// locking methods implement `Deref` (and `DerefMut` for the `write` methods) /// to allow access to the contained of the lock. /// +/// # Poisoning +/// /// RWLocks, like Mutexes, will become poisoned on panics. Note, however, that /// an RWLock may only be poisoned if a panic occurs while it is locked /// exclusively (write mode). If a panic occurs in any reader, then the lock /// will not be poisoned. /// -/// # Example +/// # Examples /// /// ``` /// use std::sync::RWLock; @@ -42,19 +44,20 @@ use sync::poison; /// /// // many reader locks can be held at once /// { -/// let r1 = lock.read(); -/// let r2 = lock.read(); +/// let r1 = lock.read().unwrap(); +/// let r2 = lock.read().unwrap(); /// assert_eq!(*r1, 5); /// assert_eq!(*r2, 5); /// } // read locks are dropped at this point /// /// // only one write lock may be held, however /// { -/// let mut w = lock.write(); +/// let mut w = lock.write().unwrap(); /// *w += 1; /// assert_eq!(*w, 6); /// } // write lock is dropped here /// ``` +#[stable] pub struct RWLock { inner: Box, data: UnsafeCell, @@ -77,64 +80,55 @@ unsafe impl Sync for RWLock {} /// static LOCK: StaticRWLock = RWLOCK_INIT; /// /// { -/// let _g = LOCK.read(); +/// let _g = LOCK.read().unwrap(); /// // ... shared read access /// } /// { -/// let _g = LOCK.write(); +/// let _g = LOCK.write().unwrap(); /// // ... exclusive write access /// } /// unsafe { LOCK.destroy() } // free all resources /// ``` +#[unstable = "may be merged with RWLock in the future"] pub struct StaticRWLock { - inner: sys::RWLock, - poison: UnsafeCell, + lock: sys::RWLock, + poison: poison::Flag, } unsafe impl Send for StaticRWLock {} unsafe impl Sync for StaticRWLock {} /// Constant initialization for a statically-initialized rwlock. +#[unstable = "may be merged with RWLock in the future"] pub const RWLOCK_INIT: StaticRWLock = StaticRWLock { - inner: sys::RWLOCK_INIT, - poison: UnsafeCell { value: poison::Flag { failed: false } }, + lock: sys::RWLOCK_INIT, + poison: poison::FLAG_INIT, }; /// RAII structure used to release the shared read access of a lock when /// dropped. #[must_use] +#[stable] pub struct RWLockReadGuard<'a, T: 'a> { - __lock: &'a RWLock, - __guard: StaticRWLockReadGuard, + __lock: &'a StaticRWLock, + __data: &'a UnsafeCell, + __marker: marker::NoSend, } /// RAII structure used to release the exclusive write access of a lock when /// dropped. #[must_use] +#[stable] pub struct RWLockWriteGuard<'a, T: 'a> { - __lock: &'a RWLock, - __guard: StaticRWLockWriteGuard, -} - -/// RAII structure used to release the shared read access of a lock when -/// dropped. -#[must_use] -pub struct StaticRWLockReadGuard { - lock: &'static sys::RWLock, - marker: marker::NoSend, -} - -/// RAII structure used to release the exclusive write access of a lock when -/// dropped. -#[must_use] -pub struct StaticRWLockWriteGuard { - lock: &'static sys::RWLock, - marker: marker::NoSend, - poison: poison::Guard<'static>, + __lock: &'a StaticRWLock, + __data: &'a UnsafeCell, + __poison: poison::Guard, + __marker: marker::NoSend, } impl RWLock { /// Creates a new instance of an RWLock which is unlocked and read to go. + #[stable] pub fn new(t: T) -> RWLock { RWLock { inner: box RWLOCK_INIT, data: UnsafeCell::new(t) } } @@ -151,17 +145,16 @@ impl RWLock { /// Returns an RAII guard which will release this thread's shared access /// once it is dropped. /// - /// # Panics + /// # Failure /// - /// This function will panic if the RWLock is poisoned. An RWLock is - /// poisoned whenever a writer panics while holding an exclusive lock. The - /// panic will occur immediately after the lock has been acquired. + /// This function will return an error if the RWLock is poisoned. An RWLock + /// is poisoned whenever a writer panics while holding an exclusive lock. + /// The failure will occur immediately after the lock has been acquired. #[inline] - pub fn read(&self) -> RWLockReadGuard { - unsafe { - let lock: &'static StaticRWLock = &*(&*self.inner as *const _); - RWLockReadGuard::new(self, lock.read()) - } + #[stable] + pub fn read(&self) -> LockResult> { + unsafe { self.inner.lock.read() } + RWLockReadGuard::new(&*self.inner, &self.data) } /// Attempt to acquire this lock with shared read access. @@ -173,18 +166,19 @@ impl RWLock { /// guarantees with respect to the ordering of whether contentious readers /// or writers will acquire the lock first. /// - /// # Panics + /// # Failure /// - /// This function will panic if the RWLock is poisoned. An RWLock is - /// poisoned whenever a writer panics while holding an exclusive lock. A - /// panic will only occur if the lock is acquired. + /// This function will return an error if the RWLock is poisoned. An RWLock + /// is poisoned whenever a writer panics while holding an exclusive lock. An + /// error will only be returned if the lock would have otherwise been + /// acquired. #[inline] - pub fn try_read(&self) -> Option> { - unsafe { - let lock: &'static StaticRWLock = &*(&*self.inner as *const _); - lock.try_read().map(|guard| { - RWLockReadGuard::new(self, guard) - }) + #[stable] + pub fn try_read(&self) -> TryLockResult> { + if unsafe { self.inner.lock.try_read() } { + Ok(try!(RWLockReadGuard::new(&*self.inner, &self.data))) + } else { + Err(TryLockError::WouldBlock) } } @@ -197,17 +191,16 @@ impl RWLock { /// Returns an RAII guard which will drop the write access of this rwlock /// when dropped. /// - /// # Panics + /// # Failure /// - /// This function will panic if the RWLock is poisoned. An RWLock is - /// poisoned whenever a writer panics while holding an exclusive lock. The - /// panic will occur when the lock is acquired. + /// This function will return an error if the RWLock is poisoned. An RWLock + /// is poisoned whenever a writer panics while holding an exclusive lock. + /// An error will be returned when the lock is acquired. #[inline] - pub fn write(&self) -> RWLockWriteGuard { - unsafe { - let lock: &'static StaticRWLock = &*(&*self.inner as *const _); - RWLockWriteGuard::new(self, lock.write()) - } + #[stable] + pub fn write(&self) -> LockResult> { + unsafe { self.inner.lock.write() } + RWLockWriteGuard::new(&*self.inner, &self.data) } /// Attempt to lock this rwlock with exclusive write access. @@ -216,18 +209,19 @@ impl RWLock { /// to `write` would otherwise block. If successful, an RAII guard is /// returned. /// - /// # Panics + /// # Failure /// - /// This function will panic if the RWLock is poisoned. An RWLock is - /// poisoned whenever a writer panics while holding an exclusive lock. A - /// panic will only occur if the lock is acquired. + /// This function will return an error if the RWLock is poisoned. An RWLock + /// is poisoned whenever a writer panics while holding an exclusive lock. An + /// error will only be returned if the lock would have otherwise been + /// acquired. #[inline] - pub fn try_write(&self) -> Option> { - unsafe { - let lock: &'static StaticRWLock = &*(&*self.inner as *const _); - lock.try_write().map(|guard| { - RWLockWriteGuard::new(self, guard) - }) + #[stable] + pub fn try_write(&self) -> TryLockResult> { + if unsafe { self.inner.lock.try_read() } { + Ok(try!(RWLockWriteGuard::new(&*self.inner, &self.data))) + } else { + Err(TryLockError::WouldBlock) } } } @@ -235,30 +229,35 @@ impl RWLock { #[unsafe_destructor] impl Drop for RWLock { fn drop(&mut self) { - unsafe { self.inner.inner.destroy() } + unsafe { self.inner.lock.destroy() } } } +static DUMMY: UnsafeCell<()> = UnsafeCell { value: () }; + impl StaticRWLock { /// 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) -> StaticRWLockReadGuard { - unsafe { self.inner.read() } - StaticRWLockReadGuard::new(self) + #[unstable = "may be merged with RWLock in the future"] + pub fn read(&'static self) -> LockResult> { + unsafe { self.lock.read() } + RWLockReadGuard::new(self, &DUMMY) } /// Attempt to acquire this lock with shared read access. /// /// See `RWLock::try_read`. #[inline] - pub fn try_read(&'static self) -> Option { - if unsafe { self.inner.try_read() } { - Some(StaticRWLockReadGuard::new(self)) + #[unstable = "may be merged with RWLock in the future"] + pub fn try_read(&'static self) + -> TryLockResult> { + if unsafe { self.lock.try_read() } { + Ok(try!(RWLockReadGuard::new(self, &DUMMY))) } else { - None + Err(TryLockError::WouldBlock) } } @@ -267,20 +266,23 @@ impl StaticRWLock { /// /// See `RWLock::write`. #[inline] - pub fn write(&'static self) -> StaticRWLockWriteGuard { - unsafe { self.inner.write() } - StaticRWLockWriteGuard::new(self) + #[unstable = "may be merged with RWLock in the future"] + pub fn write(&'static self) -> LockResult> { + unsafe { self.lock.write() } + RWLockWriteGuard::new(self, &DUMMY) } /// Attempt to lock this rwlock with exclusive write access. /// /// See `RWLock::try_write`. #[inline] - pub fn try_write(&'static self) -> Option { - if unsafe { self.inner.try_write() } { - Some(StaticRWLockWriteGuard::new(self)) + #[unstable = "may be merged with RWLock in the future"] + pub fn try_write(&'static self) + -> TryLockResult> { + if unsafe { self.lock.try_write() } { + Ok(try!(RWLockWriteGuard::new(self, &DUMMY))) } else { - None + Err(TryLockError::WouldBlock) } } @@ -290,70 +292,62 @@ impl StaticRWLock { /// 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. + #[unstable = "may be merged with RWLock in the future"] pub unsafe fn destroy(&'static self) { - self.inner.destroy() + self.lock.destroy() } } impl<'rwlock, T> RWLockReadGuard<'rwlock, T> { - fn new(lock: &RWLock, guard: StaticRWLockReadGuard) - -> RWLockReadGuard { - RWLockReadGuard { __lock: lock, __guard: guard } + fn new(lock: &'rwlock StaticRWLock, data: &'rwlock UnsafeCell) + -> LockResult> { + poison::map_result(lock.poison.borrow(), |_| { + RWLockReadGuard { + __lock: lock, + __data: data, + __marker: marker::NoSend, + } + }) } } impl<'rwlock, T> RWLockWriteGuard<'rwlock, T> { - fn new(lock: &RWLock, guard: StaticRWLockWriteGuard) - -> RWLockWriteGuard { - RWLockWriteGuard { __lock: lock, __guard: guard } + fn new(lock: &'rwlock StaticRWLock, data: &'rwlock UnsafeCell) + -> LockResult> { + poison::map_result(lock.poison.borrow(), |guard| { + RWLockWriteGuard { + __lock: lock, + __data: data, + __poison: guard, + __marker: marker::NoSend, + } + }) } } impl<'rwlock, T> Deref for RWLockReadGuard<'rwlock, T> { - fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } } + fn deref(&self) -> &T { unsafe { &*self.__data.get() } } } impl<'rwlock, T> Deref for RWLockWriteGuard<'rwlock, T> { - fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } } + fn deref(&self) -> &T { unsafe { &*self.__data.get() } } } impl<'rwlock, T> DerefMut for RWLockWriteGuard<'rwlock, T> { - fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.__lock.data.get() } } -} - -impl StaticRWLockReadGuard { - fn new(lock: &'static StaticRWLock) -> StaticRWLockReadGuard { - let guard = StaticRWLockReadGuard { - lock: &lock.inner, - marker: marker::NoSend, - }; - unsafe { (*lock.poison.get()).borrow().check("rwlock"); } - return guard; - } -} -impl StaticRWLockWriteGuard { - fn new(lock: &'static StaticRWLock) -> StaticRWLockWriteGuard { - unsafe { - let guard = StaticRWLockWriteGuard { - lock: &lock.inner, - marker: marker::NoSend, - poison: (*lock.poison.get()).borrow(), - }; - guard.poison.check("rwlock"); - return guard; - } + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.__data.get() } } } #[unsafe_destructor] -impl Drop for StaticRWLockReadGuard { +impl<'a, T> Drop for RWLockReadGuard<'a, T> { fn drop(&mut self) { - unsafe { self.lock.read_unlock(); } + unsafe { self.__lock.lock.read_unlock(); } } } #[unsafe_destructor] -impl Drop for StaticRWLockWriteGuard { +impl<'a, T> Drop for RWLockWriteGuard<'a, T> { fn drop(&mut self) { - self.poison.done(); - unsafe { self.lock.write_unlock(); } + self.__lock.poison.done(&self.__poison); + unsafe { self.__lock.lock.write_unlock(); } } } @@ -368,19 +362,19 @@ mod tests { #[test] fn smoke() { let l = RWLock::new(()); - drop(l.read()); - drop(l.write()); - drop((l.read(), l.read())); - drop(l.write()); + drop(l.read().unwrap()); + drop(l.write().unwrap()); + drop((l.read().unwrap(), l.read().unwrap())); + drop(l.write().unwrap()); } #[test] fn static_smoke() { static R: StaticRWLock = RWLOCK_INIT; - drop(R.read()); - drop(R.write()); - drop((R.read(), R.read())); - drop(R.write()); + drop(R.read().unwrap()); + drop(R.write().unwrap()); + drop((R.read().unwrap(), R.read().unwrap())); + drop(R.write().unwrap()); unsafe { R.destroy(); } } @@ -397,9 +391,9 @@ mod tests { let mut rng = rand::task_rng(); for _ in range(0, M) { if rng.gen_weighted_bool(N) { - drop(R.write()); + drop(R.write().unwrap()); } else { - drop(R.read()); + drop(R.read().unwrap()); } } drop(tx); @@ -411,51 +405,47 @@ mod tests { } #[test] - #[should_fail] fn test_rw_arc_poison_wr() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::spawn(move|| { - let lock = arc2.write(); - assert_eq!(*lock, 2); + let _: Result = Thread::spawn(move|| { + let _lock = arc2.write().unwrap(); + panic!(); }).join(); - let lock = arc.read(); - assert_eq!(*lock, 1); + assert!(arc.read().is_err()); } #[test] - #[should_fail] fn test_rw_arc_poison_ww() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::spawn(move|| { - let lock = arc2.write(); - assert_eq!(*lock, 2); + let _: Result = Thread::spawn(move|| { + let _lock = arc2.write().unwrap(); + panic!(); }).join(); - let lock = arc.write(); - assert_eq!(*lock, 1); + assert!(arc.write().is_err()); } #[test] fn test_rw_arc_no_poison_rr() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::spawn(move|| { - let lock = arc2.read(); - assert_eq!(*lock, 2); + let _: Result = Thread::spawn(move|| { + let _lock = arc2.read().unwrap(); + panic!(); }).join(); - let lock = arc.read(); + let lock = arc.read().unwrap(); assert_eq!(*lock, 1); } #[test] fn test_rw_arc_no_poison_rw() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::spawn(move|| { - let lock = arc2.read(); - assert_eq!(*lock, 2); + let _: Result = Thread::spawn(move|| { + let _lock = arc2.read().unwrap(); + panic!() }).join(); - let lock = arc.write(); + let lock = arc.write().unwrap(); assert_eq!(*lock, 1); } @@ -466,7 +456,7 @@ mod tests { let (tx, rx) = channel(); Thread::spawn(move|| { - let mut lock = arc2.write(); + let mut lock = arc2.write().unwrap(); for _ in range(0u, 10) { let tmp = *lock; *lock = -1; @@ -481,7 +471,7 @@ mod tests { for _ in range(0u, 5) { let arc3 = arc.clone(); children.push(Thread::spawn(move|| { - let lock = arc3.read(); + let lock = arc3.read().unwrap(); assert!(*lock >= 0); })); } @@ -493,7 +483,7 @@ mod tests { // Wait for writer to finish rx.recv(); - let lock = arc.read(); + let lock = arc.read().unwrap(); assert_eq!(*lock, 10); } @@ -507,14 +497,14 @@ mod tests { } impl Drop for Unwinder { fn drop(&mut self) { - let mut lock = self.i.write(); + let mut lock = self.i.write().unwrap(); *lock += 1; } } let _u = Unwinder { i: arc2 }; panic!(); }).join(); - let lock = arc.read(); + let lock = arc.read().unwrap(); assert_eq!(*lock, 2); } } diff --git a/src/libstd/sync/semaphore.rs b/src/libstd/sync/semaphore.rs index 574b0f22bee00..e3b683a6ccb38 100644 --- a/src/libstd/sync/semaphore.rs +++ b/src/libstd/sync/semaphore.rs @@ -68,9 +68,9 @@ impl Semaphore { /// This method will block until the internal count of the semaphore is at /// least 1. pub fn acquire(&self) { - let mut count = self.lock.lock(); + let mut count = self.lock.lock().unwrap(); while *count <= 0 { - self.cvar.wait(&count); + count = self.cvar.wait(count).unwrap(); } *count -= 1; } @@ -80,7 +80,7 @@ impl Semaphore { /// This will increment the number of resources in this semaphore by 1 and /// will notify any pending waiters in `acquire` or `access` if necessary. pub fn release(&self) { - *self.lock.lock() += 1; + *self.lock.lock().unwrap() += 1; self.cvar.notify_one(); } diff --git a/src/libstd/sync/task_pool.rs b/src/libstd/sync/task_pool.rs index 366e4b7d35b01..98da5ccc55476 100644 --- a/src/libstd/sync/task_pool.rs +++ b/src/libstd/sync/task_pool.rs @@ -113,7 +113,7 @@ fn spawn_in_pool(jobs: Arc>>) { let message = { // Only lock jobs for the time it takes // to get a job, not run it. - let lock = jobs.lock(); + let lock = jobs.lock().unwrap(); lock.recv_opt() }; diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index a629f035b07af..9ef1c33312fc8 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -83,7 +83,7 @@ impl Helper { F: FnOnce() -> T, { unsafe { - let _guard = self.lock.lock(); + let _guard = self.lock.lock().unwrap(); if !*self.initialized.get() { let (tx, rx) = channel(); *self.chan.get() = mem::transmute(box tx); @@ -95,7 +95,7 @@ impl Helper { let t = f(); Thread::spawn(move |:| { helper(receive.0, rx, t); - let _g = self.lock.lock(); + let _g = self.lock.lock().unwrap(); *self.shutdown.get() = true; self.cond.notify_one() }).detach(); @@ -111,7 +111,7 @@ impl Helper { /// This is only valid if the worker thread has previously booted pub fn send(&'static self, msg: M) { unsafe { - let _guard = self.lock.lock(); + let _guard = self.lock.lock().unwrap(); // Must send and *then* signal to ensure that the child receives the // message. Otherwise it could wake up and go to sleep before we @@ -127,7 +127,7 @@ impl Helper { // Shut down, but make sure this is done inside our lock to ensure // that we'll always receive the exit signal when the thread // returns. - let guard = self.lock.lock(); + let mut guard = self.lock.lock().unwrap(); // Close the channel by destroying it let chan: Box> = mem::transmute(*self.chan.get()); @@ -137,7 +137,7 @@ impl Helper { // Wait for the child to exit while !*self.shutdown.get() { - self.cond.wait(&guard); + guard = self.cond.wait(guard).unwrap(); } drop(guard); diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs index 56731bd7ec3ee..38e304ccfecc0 100644 --- a/src/libstd/thread.rs +++ b/src/libstd/thread.rs @@ -334,6 +334,7 @@ impl Thread { } /// Determines whether the current thread is panicking. + #[inline] pub fn panicking() -> bool { unwind::panicking() } @@ -349,9 +350,9 @@ impl Thread { // or futuxes, and in either case may allow spurious wakeups. pub fn park() { let thread = Thread::current(); - let mut guard = thread.inner.lock.lock(); + let mut guard = thread.inner.lock.lock().unwrap(); while !*guard { - thread.inner.cvar.wait(&guard); + guard = thread.inner.cvar.wait(guard).unwrap(); } *guard = false; } @@ -360,7 +361,7 @@ impl Thread { /// /// See the module doc for more detail. pub fn unpark(&self) { - let mut guard = self.inner.lock.lock(); + let mut guard = self.inner.lock.lock().unwrap(); if !*guard { *guard = true; self.inner.cvar.notify_one(); diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index 242dceb425636..4cfa27093527a 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -240,13 +240,18 @@ impl Key { unsafe { let slot = slot.get().expect("cannot access a TLS value during or \ after it is destroyed"); - if (*slot.get()).is_none() { - *slot.get() = Some((self.init)()); - } - f((*slot.get()).as_ref().unwrap()) + f(match *slot.get() { + Some(ref inner) => inner, + None => self.init(slot), + }) } } + unsafe fn init(&self, slot: &UnsafeCell>) -> &T { + *slot.get() = Some((self.init)()); + (*slot.get()).as_ref().unwrap() + } + /// Test this TLS key to determine whether its value has been destroyed for /// the current thread or not. /// diff --git a/src/test/bench/msgsend-ring-mutex-arcs.rs b/src/test/bench/msgsend-ring-mutex-arcs.rs index 49f53bf9d3883..8ec44b2dd3c14 100644 --- a/src/test/bench/msgsend-ring-mutex-arcs.rs +++ b/src/test/bench/msgsend-ring-mutex-arcs.rs @@ -28,15 +28,15 @@ type pipe = Arc<(Mutex>, Condvar)>; fn send(p: &pipe, msg: uint) { let &(ref lock, ref cond) = &**p; - let mut arr = lock.lock(); + let mut arr = lock.lock().unwrap(); arr.push(msg); cond.notify_one(); } fn recv(p: &pipe) -> uint { let &(ref lock, ref cond) = &**p; - let mut arr = lock.lock(); + let mut arr = lock.lock().unwrap(); while arr.is_empty() { - cond.wait(&arr); + arr = cond.wait(arr).unwrap(); } arr.pop().unwrap() } diff --git a/src/test/run-pass/match-ref-binding-in-guard-3256.rs b/src/test/run-pass/match-ref-binding-in-guard-3256.rs index 9bb912e081ce6..a83bc73457e02 100644 --- a/src/test/run-pass/match-ref-binding-in-guard-3256.rs +++ b/src/test/run-pass/match-ref-binding-in-guard-3256.rs @@ -11,13 +11,11 @@ use std::sync::Mutex; pub fn main() { - unsafe { - let x = Some(Mutex::new(true)); - match x { - Some(ref z) if *z.lock() => { - assert!(*z.lock()); - }, - _ => panic!() - } + let x = Some(Mutex::new(true)); + match x { + Some(ref z) if *z.lock().unwrap() => { + assert!(*z.lock().unwrap()); + }, + _ => panic!() } } diff --git a/src/test/run-pass/writealias.rs b/src/test/run-pass/writealias.rs index f48272366e2fd..dacfeb0081925 100644 --- a/src/test/run-pass/writealias.rs +++ b/src/test/run-pass/writealias.rs @@ -15,13 +15,11 @@ struct Point {x: int, y: int, z: int} fn f(p: &mut Point) { p.z = 13; } pub fn main() { - unsafe { - let x = Some(Mutex::new(true)); - match x { - Some(ref z) if *z.lock() => { - assert!(*z.lock()); - }, - _ => panic!() - } + let x = Some(Mutex::new(true)); + match x { + Some(ref z) if *z.lock().unwrap() => { + assert!(*z.lock().unwrap()); + }, + _ => panic!() } }