提交 76e5ed65 编写于 作者: A Alex Crichton

std: Return Result from RWLock/Mutex methods

All of the current std::sync primitives have poisoning enable which means that
when a task fails inside of a write-access lock then all future attempts to
acquire the lock will fail. This strategy ensures that stale data whose
invariants are possibly not upheld are never viewed by other tasks to help
propagate unexpected panics (bugs in a program) among tasks.

Currently there is no way to test whether a mutex or rwlock is poisoned. One
method would be to duplicate all the methods with a sister foo_catch function,
for example. This pattern is, however, against our [error guidelines][errors].
As a result, this commit exposes the fact that a task has failed internally
through the return value of a `Result`.

[errors]: https://github.com/rust-lang/rfcs/blob/master/text/0236-error-conventions.md#do-not-provide-both-result-and-fail-variants

All methods now return a `LockResult<T>` or a `TryLockResult<T>` which
communicates whether the lock was poisoned or not. In a `LockResult`, both the
`Ok` and `Err` variants contains the `MutexGuard<T>` that is being returned in
order to allow access to the data if poisoning is not desired. This also means
that the lock is *always* held upon returning from `.lock()`.

A new type, `PoisonError`, was added with one method `into_guard` which can
consume the assertion that a lock is poisoned to gain access to the underlying
data.

This is a breaking change because the signatures of these methods have changed,
often incompatible ways. One major difference is that the `wait` methods on a
condition variable now consume the guard and return it in as a `LockResult` to
indicate whether the lock was poisoned while waiting. Most code can be updated
by calling `.unwrap()` on the return value of `.lock()`.

[breaking-change]
上级 3dcc409f
......@@ -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;
......
......@@ -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 @@ struct Cycle {
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)...
}
......
......@@ -98,7 +98,7 @@ fn new() -> 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 @@ fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
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);
......
......@@ -86,7 +86,7 @@ pub fn new() -> Packet<T> {
// 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 @@ pub fn abort_selection(&mut self, _was_upgrade: bool) -> bool {
// 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
......
......@@ -121,9 +121,9 @@ fn wait<'a, 'b, T: Send>(lock: &'a Mutex<State<T>>,
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 @@ pub fn new(cap: uint) -> Packet<T> {
fn acquire_send_slot(&self) -> MutexGuard<State<T>> {
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 @@ pub fn send(&self, t: T) -> Result<(), T> {
}
pub fn try_send(&self, t: T) -> Result<(), super::TrySendError<T>> {
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 @@ pub fn try_send(&self, t: T) -> Result<(), super::TrySendError<T>> {
// When reading this, remember that there can only ever be one receiver at
// time.
pub fn recv(&self) -> Result<T, ()> {
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 @@ pub fn recv(&self) -> Result<T, ()> {
}
pub fn try_recv(&self) -> Result<T, Failure> {
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 @@ pub fn drop_chan(&self) {
}
// 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 @@ pub fn drop_chan(&self) {
}
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 @@ pub fn drop_port(&self) {
// 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 @@ pub fn start_selection(&self, token: SignalToken) -> StartResult {
//
// 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 @@ pub fn abort_selection(&self) -> bool {
impl<T: Send> Drop for Packet<T> {
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());
}
......
......@@ -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 @@ pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
/// The read is performed atomically - concurrent read calls in other
/// threads will not interleave with this one.
pub fn read_line(&mut self) -> IoResult<String> {
self.inner.lock().0.read_line()
self.inner.lock().unwrap().0.read_line()
}
/// Like `Buffer::read_until`.
......@@ -163,7 +163,7 @@ pub fn read_line(&mut self) -> IoResult<String> {
/// 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<Vec<u8>> {
self.inner.lock().0.read_until(byte)
self.inner.lock().unwrap().0.read_until(byte)
}
/// Like `Buffer::read_char`.
......@@ -171,13 +171,13 @@ pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
/// The read is performed atomically - concurrent read calls in other
/// threads will not interleave with this one.
pub fn read_char(&mut self) -> IoResult<char> {
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<uint> {
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 @@ fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
// incur the costs of repeated locking).
fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> {
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<u8>) -> IoResult<uint> {
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<Vec<u8>> {
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<u64> {
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<u64> {
self.inner.lock().0.read_be_uint_n(nbytes)
self.inner.lock().unwrap().0.read_be_uint_n(nbytes)
}
}
......
......@@ -69,7 +69,7 @@ pub fn new(n: uint) -> 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 @@ pub fn wait(&self) {
// 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;
......
......@@ -11,7 +11,8 @@
use prelude::*;
use sync::atomic::{mod, AtomicUint};
use sync::{mutex, StaticMutexGuard};
use sync::poison::{mod, LockResult};
use sync::CondvarGuard;
use sys_common::condvar as sys;
use sys_common::mutex as sys_mutex;
use time::Duration;
......@@ -44,16 +45,16 @@
/// // 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();
/// }
/// ```
pub struct Condvar { inner: Box<StaticCondvar> }
......@@ -92,9 +93,9 @@ unsafe impl Sync for StaticCondvar {}
///
/// Note that this trait should likely not be implemented manually unless you
/// really know what you're doing.
pub trait AsMutexGuard {
pub trait AsGuard {
#[allow(missing_docs)]
unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard;
fn as_guard(&self) -> CondvarGuard;
}
impl Condvar {
......@@ -113,8 +114,8 @@ pub fn new() -> 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,13 +124,20 @@ pub fn new() -> 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<T: AsMutexGuard>(&self, mutex_guard: &T) {
pub fn wait<T: AsGuard>(&self, mutex_guard: T)
-> LockResult<T> {
unsafe {
let me: &'static Condvar = &*(self as *const _);
me.inner.wait(mutex_guard)
......@@ -156,8 +164,8 @@ pub fn wait<T: AsMutexGuard>(&self, mutex_guard: &T) {
// provide. There are also additional concerns about the unix-specific
// implementation which may need to be addressed.
#[allow(dead_code)]
fn wait_timeout<T: AsMutexGuard>(&self, mutex_guard: &T,
dur: Duration) -> bool {
fn wait_timeout<T: AsGuard>(&self, mutex_guard: T, dur: Duration)
-> LockResult<(T, bool)> {
unsafe {
let me: &'static Condvar = &*(self as *const _);
me.inner.wait_timeout(mutex_guard, dur)
......@@ -194,13 +202,17 @@ impl StaticCondvar {
/// notification.
///
/// See `Condvar::wait`.
pub fn wait<T: AsMutexGuard>(&'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");
pub fn wait<T: AsGuard>(&'static self, mutex_guard: T) -> LockResult<T> {
let poisoned = unsafe {
let cvar_guard = mutex_guard.as_guard();
self.verify(cvar_guard.lock);
self.inner.wait(cvar_guard.lock);
cvar_guard.poisoned.get()
};
if poisoned {
Err(poison::new_poison_error(mutex_guard))
} else {
Ok(mutex_guard)
}
}
......@@ -209,15 +221,18 @@ pub fn wait<T: AsMutexGuard>(&'static self, mutex_guard: &T) {
///
/// See `Condvar::wait_timeout`.
#[allow(dead_code)] // may want to stabilize this later, see wait_timeout above
fn wait_timeout<T: AsMutexGuard>(&'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<T: AsGuard>(&'static self, mutex_guard: T, dur: Duration)
-> LockResult<(T, bool)> {
let (poisoned, success) = unsafe {
let cvar_guard = mutex_guard.as_guard();
self.verify(cvar_guard.lock);
let success = self.inner.wait_timeout(cvar_guard.lock, dur);
(cvar_guard.poisoned.get(), success)
};
if poisoned {
Err(poison::new_poison_error((mutex_guard, success)))
} else {
Ok((mutex_guard, success))
}
}
......@@ -288,12 +303,12 @@ fn notify_one() {
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 +324,13 @@ fn notify_all() {
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 +339,7 @@ fn notify_all() {
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 +354,15 @@ fn wait_timeout() {
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 +374,15 @@ fn two_mutexes() {
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();
}
}
......@@ -17,16 +17,20 @@
#![experimental]
use sys_common::mutex as sys_mutex;
pub use alloc::arc::{Arc, Weak};
pub use self::mutex::{Mutex, MutexGuard, StaticMutex, StaticMutexGuard, MUTEX_INIT};
pub use self::mutex::{Mutex, MutexGuard, StaticMutex, StaticMutexGuard};
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, AsGuard};
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;
......@@ -41,3 +45,10 @@
mod rwlock;
mod semaphore;
mod task_pool;
/// Structure returned by `AsGuard` to wait on a condition variable.
// NB: defined here to all modules have access to these private fields.
pub struct CondvarGuard<'a> {
lock: &'a sys_mutex::Mutex,
poisoned: &'a poison::Flag,
}
......@@ -12,7 +12,8 @@
use cell::UnsafeCell;
use kinds::marker;
use sync::{poison, AsMutexGuard};
use sync::{AsGuard, CondvarGuard};
use sync::poison::{mod, TryLockError, TryLockResult, LockResult};
use sys_common::mutex as sys;
/// A mutual exclusion primitive useful for protecting shared data
......@@ -26,12 +27,23 @@
///
/// # 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 +60,14 @@
/// 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 +78,35 @@
///
/// 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;
/// ```
pub struct Mutex<T> {
// 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,14 @@ unsafe impl<T:Send> Sync for Mutex<T> { }
/// static LOCK: StaticMutex = MUTEX_INIT;
///
/// {
/// let _g = LOCK.lock();
/// let _g = LOCK.lock().unwrap();
/// // do some productive work
/// }
/// // lock is unlocked here.
/// ```
pub struct StaticMutex {
lock: sys::Mutex,
poison: UnsafeCell<poison::Flag>,
poison: poison::Flag,
}
unsafe impl Sync for StaticMutex {}
......@@ -114,24 +158,27 @@ unsafe impl Sync for StaticMutex {}
pub struct MutexGuard<'a, T: 'a> {
// funny underscores due to how Deref/DerefMut currently work (they
// disregard field privacy).
__lock: &'a Mutex<T>,
__guard: StaticMutexGuard,
__inner: Guard<'a, Mutex<T>>,
}
/// 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>,
inner: Guard<'static, StaticMutex>,
}
struct Guard<'a, T: 'a> {
inner: &'a T,
poison: poison::Guard,
marker: marker::NoSend, // even if 'a is static, this cannot be sent
}
/// Static initialization of a mutex. This constant can be used to initialize
/// other mutex constants.
pub const MUTEX_INIT: StaticMutex = StaticMutex {
lock: sys::MUTEX_INIT,
poison: UnsafeCell { value: poison::Flag { failed: false } },
poison: poison::FLAG_INIT,
};
impl<T: Send> Mutex<T> {
......@@ -150,15 +197,13 @@ pub fn new(t: T) -> Mutex<T> {
/// 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<T> {
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.
pub fn lock(&self) -> LockResult<MutexGuard<T>> {
unsafe { self.inner.lock.lock() }
MutexGuard::new(self)
}
/// Attempts to acquire this lock.
......@@ -169,17 +214,16 @@ pub fn lock(&self) -> MutexGuard<T> {
///
/// 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<MutexGuard<T>> {
unsafe {
let lock: &'static StaticMutex = &*(&*self.inner as *const _);
lock.try_lock().map(|guard| {
MutexGuard::new(self, guard)
})
pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> {
if unsafe { self.inner.lock.try_lock() } {
Ok(try!(MutexGuard::new(self)))
} else {
Err(TryLockError::WouldBlock)
}
}
}
......@@ -196,17 +240,19 @@ fn drop(&mut self) {
impl StaticMutex {
/// Acquires this lock, see `Mutex::lock`
pub fn lock(&'static self) -> StaticMutexGuard {
#[inline]
pub fn lock(&'static self) -> LockResult<StaticMutexGuard> {
unsafe { self.lock.lock() }
StaticMutexGuard::new(self)
}
/// Attempts to grab this lock, see `Mutex::try_lock`
pub fn try_lock(&'static self) -> Option<StaticMutexGuard> {
#[inline]
pub fn try_lock(&'static self) -> TryLockResult<StaticMutexGuard> {
if unsafe { self.lock.try_lock() } {
Some(StaticMutexGuard::new(self))
Ok(try!(StaticMutexGuard::new(self)))
} else {
None
Err(TryLockError::WouldBlock)
}
}
......@@ -226,53 +272,85 @@ pub unsafe fn destroy(&'static self) {
}
impl<'mutex, T> MutexGuard<'mutex, T> {
fn new(lock: &Mutex<T>, guard: StaticMutexGuard) -> MutexGuard<T> {
MutexGuard { __lock: lock, __guard: guard }
fn new(lock: &Mutex<T>) -> LockResult<MutexGuard<T>> {
poison::map_result(Guard::new(lock), |guard| {
MutexGuard { __inner: guard }
})
}
}
impl<'mutex, T> AsMutexGuard for MutexGuard<'mutex, T> {
unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { &self.__guard }
impl<T> AsGuard for Mutex<T> {
fn as_guard(&self) -> CondvarGuard { self.inner.as_guard() }
}
impl<'mutex, T> AsGuard for MutexGuard<'mutex, T> {
fn as_guard(&self) -> CondvarGuard {
CondvarGuard {
lock: &self.__inner.inner.inner.lock,
poisoned: &self.__inner.inner.inner.poison,
}
}
}
impl<'mutex, T> Deref<T> 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.__inner.inner.data.get() }
}
}
impl<'mutex, T> DerefMut<T> for MutexGuard<'mutex, T> {
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
unsafe { &mut *self.__lock.data.get() }
unsafe { &mut *self.__inner.inner.data.get() }
}
}
impl StaticMutexGuard {
fn new(lock: &'static StaticMutex) -> StaticMutexGuard {
unsafe {
let guard = StaticMutexGuard {
lock: &lock.lock,
marker: marker::NoSend,
poison: (*lock.poison.get()).borrow(),
};
guard.poison.check("mutex");
return guard;
}
#[inline]
fn new(lock: &'static StaticMutex) -> LockResult<StaticMutexGuard> {
poison::map_result(Guard::new(lock), |guard| {
StaticMutexGuard { inner: guard }
})
}
}
pub fn guard_lock(guard: &StaticMutexGuard) -> &sys::Mutex { guard.lock }
pub fn guard_poison(guard: &StaticMutexGuard) -> &poison::Guard {
&guard.poison
impl AsGuard for StaticMutex {
#[inline]
fn as_guard(&self) -> CondvarGuard {
CondvarGuard { lock: &self.lock, poisoned: &self.poison }
}
}
impl AsMutexGuard for StaticMutexGuard {
unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { self }
impl AsGuard for StaticMutexGuard {
#[inline]
fn as_guard(&self) -> CondvarGuard {
CondvarGuard {
lock: &self.inner.inner.lock,
poisoned: &self.inner.inner.poison,
}
}
}
impl<'a, T: AsGuard> Guard<'a, T> {
#[inline]
fn new(t: &T) -> LockResult<Guard<T>> {
let data = t.as_guard();
poison::map_result(data.poisoned.borrow(), |guard| {
Guard {
inner: t,
poison: guard,
marker: marker::NoSend,
}
})
}
}
#[unsafe_destructor]
impl Drop for StaticMutexGuard {
impl<'a, T: AsGuard> Drop for Guard<'a, T> {
#[inline]
fn drop(&mut self) {
unsafe {
self.poison.done();
self.lock.unlock();
let data = self.inner.as_guard();
data.poisoned.done(&self.poison);
data.lock.unlock();
}
}
}
......@@ -292,16 +370,16 @@ unsafe impl<T> Sync for Packet<T> {}
#[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 +394,7 @@ fn lots_and_lots() {
fn inc() {
for _ in range(0, J) {
unsafe {
let _g = M.lock();
let _g = M.lock().unwrap();
CNT += 1;
}
}
......@@ -343,7 +421,7 @@ fn inc() {
#[test]
fn try_lock() {
let m = Mutex::new(());
assert!(m.try_lock().is_some());
*m.try_lock().unwrap() = ();
}
#[test]
......@@ -355,22 +433,21 @@ fn test_mutex_arc_condvar() {
// 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 +456,35 @@ fn test_arc_condvar_poison() {
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 +495,8 @@ fn test_mutex_arc_nested() {
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 +513,13 @@ struct Unwinder {
}
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);
}
}
......@@ -8,31 +8,120 @@
// 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<bool> }
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<Guard> {
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.
pub struct PoisonError<T> {
guard: T,
}
/// An enumeration of possible errors which can occur while calling the
/// `try_lock` method.
pub enum TryLockError<T> {
/// The lock could not be acquired because another task failed while holding
/// the lock.
Poisoned(PoisonError<T>),
/// The lock could not be acquired at this time because the operation would
/// otherwise block.
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.
pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
/// 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.
pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
impl<T> fmt::Show for PoisonError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
"poisoned lock: another task failed inside".fmt(f)
}
}
impl<T> PoisonError<T> {
/// Consumes this error indicating that a lock is poisoned, returning the
/// underlying guard to allow access regardless.
pub fn into_guard(self) -> T { self.guard }
}
impl<T> FromError<PoisonError<T>> for TryLockError<T> {
fn from_error(err: PoisonError<T>) -> TryLockError<T> {
TryLockError::Poisoned(err)
}
}
pub fn done(&mut self) {
if !self.panicking && Thread::panicking() {
*self.flag = true;
impl<T> fmt::Show for TryLockError<T> {
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<T>(guard: T) -> PoisonError<T> {
PoisonError { guard: guard }
}
pub fn map_result<T, U, F>(result: LockResult<T>, f: F)
-> LockResult<U>
where F: FnOnce(T) -> U {
match result {
Ok(t) => Ok(f(t)),
Err(PoisonError { guard }) => Err(new_poison_error(f(guard)))
}
}
......@@ -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 @@
/// 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,15 +44,15 @@
///
/// // 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
......@@ -77,18 +79,18 @@ unsafe impl<T> Sync for RWLock<T> {}
/// 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
/// ```
pub struct StaticRWLock {
inner: sys::RWLock,
poison: UnsafeCell<poison::Flag>,
lock: sys::RWLock,
poison: poison::Flag,
}
unsafe impl Send for StaticRWLock {}
......@@ -96,41 +98,52 @@ unsafe impl Sync for StaticRWLock {}
/// Constant initialization for a statically-initialized rwlock.
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]
pub struct RWLockReadGuard<'a, T: 'a> {
__lock: &'a RWLock<T>,
__guard: StaticRWLockReadGuard,
__inner: ReadGuard<'a, RWLock<T>>,
}
/// RAII structure used to release the exclusive write access of a lock when
/// dropped.
#[must_use]
pub struct RWLockWriteGuard<'a, T: 'a> {
__lock: &'a RWLock<T>,
__guard: StaticRWLockWriteGuard,
__inner: WriteGuard<'a, RWLock<T>>,
}
/// 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,
_inner: ReadGuard<'static, StaticRWLock>,
}
/// 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>,
_inner: WriteGuard<'static, StaticRWLock>,
}
struct ReadGuard<'a, T: 'a> {
inner: &'a T,
marker: marker::NoSend, // even if 'a == static, cannot send
}
struct WriteGuard<'a, T: 'a> {
inner: &'a T,
poison: poison::Guard,
marker: marker::NoSend, // even if 'a == static, cannot send
}
#[doc(hidden)]
trait AsStaticRWLock {
fn as_static_rwlock(&self) -> &StaticRWLock;
}
impl<T: Send + Sync> RWLock<T> {
......@@ -151,17 +164,15 @@ pub fn new(t: T) -> RWLock<T> {
/// 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<T> {
unsafe {
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
RWLockReadGuard::new(self, lock.read())
}
pub fn read(&self) -> LockResult<RWLockReadGuard<T>> {
unsafe { self.inner.lock.read() }
RWLockReadGuard::new(self)
}
/// Attempt to acquire this lock with shared read access.
......@@ -173,18 +184,18 @@ pub fn read(&self) -> RWLockReadGuard<T> {
/// 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<RWLockReadGuard<T>> {
unsafe {
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
lock.try_read().map(|guard| {
RWLockReadGuard::new(self, guard)
})
pub fn try_read(&self) -> TryLockResult<RWLockReadGuard<T>> {
if unsafe { self.inner.lock.try_read() } {
Ok(try!(RWLockReadGuard::new(self)))
} else {
Err(TryLockError::WouldBlock)
}
}
......@@ -197,17 +208,15 @@ pub fn try_read(&self) -> Option<RWLockReadGuard<T>> {
/// 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<T> {
unsafe {
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
RWLockWriteGuard::new(self, lock.write())
}
pub fn write(&self) -> LockResult<RWLockWriteGuard<T>> {
unsafe { self.inner.lock.write() }
RWLockWriteGuard::new(self)
}
/// Attempt to lock this rwlock with exclusive write access.
......@@ -216,18 +225,18 @@ pub fn write(&self) -> RWLockWriteGuard<T> {
/// 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<RWLockWriteGuard<T>> {
unsafe {
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
lock.try_write().map(|guard| {
RWLockWriteGuard::new(self, guard)
})
pub fn try_write(&self) -> TryLockResult<RWLockWriteGuard<T>> {
if unsafe { self.inner.lock.try_read() } {
Ok(try!(RWLockWriteGuard::new(self)))
} else {
Err(TryLockError::WouldBlock)
}
}
}
......@@ -235,7 +244,7 @@ pub fn try_write(&self) -> Option<RWLockWriteGuard<T>> {
#[unsafe_destructor]
impl<T> Drop for RWLock<T> {
fn drop(&mut self) {
unsafe { self.inner.inner.destroy() }
unsafe { self.inner.lock.destroy() }
}
}
......@@ -245,8 +254,8 @@ impl StaticRWLock {
///
/// See `RWLock::read`.
#[inline]
pub fn read(&'static self) -> StaticRWLockReadGuard {
unsafe { self.inner.read() }
pub fn read(&'static self) -> LockResult<StaticRWLockReadGuard> {
unsafe { self.lock.read() }
StaticRWLockReadGuard::new(self)
}
......@@ -254,11 +263,11 @@ pub fn read(&'static self) -> StaticRWLockReadGuard {
///
/// See `RWLock::try_read`.
#[inline]
pub fn try_read(&'static self) -> Option<StaticRWLockReadGuard> {
if unsafe { self.inner.try_read() } {
Some(StaticRWLockReadGuard::new(self))
pub fn try_read(&'static self) -> TryLockResult<StaticRWLockReadGuard> {
if unsafe { self.lock.try_read() } {
Ok(try!(StaticRWLockReadGuard::new(self)))
} else {
None
Err(TryLockError::WouldBlock)
}
}
......@@ -267,8 +276,8 @@ pub fn try_read(&'static self) -> Option<StaticRWLockReadGuard> {
///
/// See `RWLock::write`.
#[inline]
pub fn write(&'static self) -> StaticRWLockWriteGuard {
unsafe { self.inner.write() }
pub fn write(&'static self) -> LockResult<StaticRWLockWriteGuard> {
unsafe { self.lock.write() }
StaticRWLockWriteGuard::new(self)
}
......@@ -276,11 +285,11 @@ pub fn write(&'static self) -> StaticRWLockWriteGuard {
///
/// See `RWLock::try_write`.
#[inline]
pub fn try_write(&'static self) -> Option<StaticRWLockWriteGuard> {
if unsafe { self.inner.try_write() } {
Some(StaticRWLockWriteGuard::new(self))
pub fn try_write(&'static self) -> TryLockResult<StaticRWLockWriteGuard> {
if unsafe { self.lock.try_write() } {
Ok(try!(StaticRWLockWriteGuard::new(self)))
} else {
None
Err(TryLockError::WouldBlock)
}
}
......@@ -291,69 +300,92 @@ pub fn try_write(&'static self) -> Option<StaticRWLockWriteGuard> {
/// of this lock. This method is required to be called to not leak memory on
/// all platforms.
pub unsafe fn destroy(&'static self) {
self.inner.destroy()
self.lock.destroy()
}
}
impl<'rwlock, T> RWLockReadGuard<'rwlock, T> {
fn new(lock: &RWLock<T>, guard: StaticRWLockReadGuard)
-> RWLockReadGuard<T> {
RWLockReadGuard { __lock: lock, __guard: guard }
fn new(lock: &RWLock<T>) -> LockResult<RWLockReadGuard<T>> {
poison::map_result(ReadGuard::new(lock), |guard| {
RWLockReadGuard { __inner: guard }
})
}
}
impl<'rwlock, T> RWLockWriteGuard<'rwlock, T> {
fn new(lock: &RWLock<T>, guard: StaticRWLockWriteGuard)
-> RWLockWriteGuard<T> {
RWLockWriteGuard { __lock: lock, __guard: guard }
fn new(lock: &RWLock<T>) -> LockResult<RWLockWriteGuard<T>> {
poison::map_result(WriteGuard::new(lock), |guard| {
RWLockWriteGuard { __inner: guard }
})
}
}
impl<'rwlock, T> Deref<T> for RWLockReadGuard<'rwlock, T> {
fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } }
fn deref(&self) -> &T { unsafe { &*self.__inner.inner.data.get() } }
}
impl<'rwlock, T> Deref<T> for RWLockWriteGuard<'rwlock, T> {
fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } }
fn deref(&self) -> &T { unsafe { &*self.__inner.inner.data.get() } }
}
impl<'rwlock, T> DerefMut<T> for RWLockWriteGuard<'rwlock, T> {
fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.__lock.data.get() } }
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.__inner.inner.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;
#[inline]
fn new(lock: &'static StaticRWLock) -> LockResult<StaticRWLockReadGuard> {
poison::map_result(ReadGuard::new(lock), |guard| {
StaticRWLockReadGuard { _inner: 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;
}
#[inline]
fn new(lock: &'static StaticRWLock) -> LockResult<StaticRWLockWriteGuard> {
poison::map_result(WriteGuard::new(lock), |guard| {
StaticRWLockWriteGuard { _inner: guard }
})
}
}
impl<T> AsStaticRWLock for RWLock<T> {
#[inline]
fn as_static_rwlock(&self) -> &StaticRWLock { &*self.inner }
}
impl AsStaticRWLock for StaticRWLock {
#[inline]
fn as_static_rwlock(&self) -> &StaticRWLock { self }
}
impl<'a, T: AsStaticRWLock> ReadGuard<'a, T> {
fn new(t: &'a T) -> LockResult<ReadGuard<'a, T>> {
poison::map_result(t.as_static_rwlock().poison.borrow(), |_| {
ReadGuard { inner: t, marker: marker::NoSend }
})
}
}
impl<'a, T: AsStaticRWLock> WriteGuard<'a, T> {
fn new(t: &'a T) -> LockResult<WriteGuard<'a, T>> {
poison::map_result(t.as_static_rwlock().poison.borrow(), |guard| {
WriteGuard { inner: t, marker: marker::NoSend, poison: guard }
})
}
}
#[unsafe_destructor]
impl Drop for StaticRWLockReadGuard {
impl<'a, T: AsStaticRWLock> Drop for ReadGuard<'a, T> {
fn drop(&mut self) {
unsafe { self.lock.read_unlock(); }
unsafe { self.inner.as_static_rwlock().lock.read_unlock(); }
}
}
#[unsafe_destructor]
impl Drop for StaticRWLockWriteGuard {
impl<'a, T: AsStaticRWLock> Drop for WriteGuard<'a, T> {
fn drop(&mut self) {
self.poison.done();
unsafe { self.lock.write_unlock(); }
let inner = self.inner.as_static_rwlock();
inner.poison.done(&self.poison);
unsafe { inner.lock.write_unlock(); }
}
}
......@@ -368,19 +400,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 +429,9 @@ fn frob() {
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 +443,47 @@ fn frob() {
}
#[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<uint, _> = 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<uint, _> = 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<uint, _> = 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<uint, _> = 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 +494,7 @@ fn test_rw_arc() {
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 +509,7 @@ fn test_rw_arc() {
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 +521,7 @@ fn test_rw_arc() {
// Wait for writer to finish
rx.recv();
let lock = arc.read();
let lock = arc.read().unwrap();
assert_eq!(*lock, 10);
}
......@@ -507,14 +535,14 @@ struct Unwinder {
}
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);
}
}
......@@ -68,9 +68,9 @@ pub fn new(count: int) -> 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 @@ pub fn acquire(&self) {
/// 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();
}
......
......@@ -113,7 +113,7 @@ fn spawn_in_pool(jobs: Arc<Mutex<Receiver<Thunk>>>) {
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()
};
......
......@@ -83,7 +83,7 @@ pub fn boot<T, F>(&'static self, f: F, helper: fn(helper_signal::signal, Receive
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 @@ pub fn boot<T, F>(&'static self, f: F, helper: fn(helper_signal::signal, Receive
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 @@ pub fn boot<T, F>(&'static self, f: F, helper: fn(helper_signal::signal, Receive
/// 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 @@ fn shutdown(&'static self) {
// 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<Sender<M>> = mem::transmute(*self.chan.get());
......@@ -137,7 +137,7 @@ fn shutdown(&'static self) {
// Wait for the child to exit
while !*self.shutdown.get() {
self.cond.wait(&guard);
guard = self.cond.wait(guard).unwrap();
}
drop(guard);
......
......@@ -334,6 +334,7 @@ pub fn yield_now() {
}
/// Determines whether the current thread is panicking.
#[inline]
pub fn panicking() -> bool {
unwind::panicking()
}
......@@ -349,9 +350,9 @@ pub fn panicking() -> bool {
// 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 @@ pub fn park() {
///
/// 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();
......
......@@ -240,13 +240,18 @@ pub fn with<F, R>(&'static self, f: F) -> R
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<Option<T>>) -> &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.
///
......
......@@ -28,15 +28,15 @@
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()
}
......
......@@ -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!()
}
}
......@@ -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!()
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册