mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-30 10:45:18 +00:00
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]
This commit is contained in:
parent
3dcc409fac
commit
76e5ed655c
@ -483,7 +483,7 @@ fn main() {
|
|||||||
for i in range(0u, 3u) {
|
for i in range(0u, 3u) {
|
||||||
let number = numbers.clone();
|
let number = numbers.clone();
|
||||||
Thread::spawn(move || {
|
Thread::spawn(move || {
|
||||||
let mut array = number.lock();
|
let mut array = number.lock().unwrap();
|
||||||
|
|
||||||
(*array)[i] += 1;
|
(*array)[i] += 1;
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
//! let five = five.clone();
|
//! let five = five.clone();
|
||||||
//!
|
//!
|
||||||
//! Thread::spawn(move || {
|
//! Thread::spawn(move || {
|
||||||
//! let mut number = five.lock();
|
//! let mut number = five.lock().unwrap();
|
||||||
//!
|
//!
|
||||||
//! *number += 1;
|
//! *number += 1;
|
||||||
//!
|
//!
|
||||||
@ -722,7 +722,7 @@ mod tests {
|
|||||||
|
|
||||||
let a = Arc::new(Cycle { x: Mutex::new(None) });
|
let a = Arc::new(Cycle { x: Mutex::new(None) });
|
||||||
let b = a.clone().downgrade();
|
let b = a.clone().downgrade();
|
||||||
*a.x.lock() = Some(b);
|
*a.x.lock().unwrap() = Some(b);
|
||||||
|
|
||||||
// hopefully we don't double-free (or leak)...
|
// hopefully we don't double-free (or leak)...
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ impl SharedEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dump(&mut self, handler: &Handler) {
|
fn dump(&mut self, handler: &Handler) {
|
||||||
let mut buffer = self.buffer.lock();
|
let mut buffer = self.buffer.lock().unwrap();
|
||||||
for diag in buffer.iter() {
|
for diag in buffer.iter() {
|
||||||
match diag.code {
|
match diag.code {
|
||||||
Some(ref code) => {
|
Some(ref code) => {
|
||||||
@ -123,7 +123,7 @@ impl Emitter for SharedEmitter {
|
|||||||
msg: &str, code: Option<&str>, lvl: Level) {
|
msg: &str, code: Option<&str>, lvl: Level) {
|
||||||
assert!(cmsp.is_none(), "SharedEmitter doesn't support spans");
|
assert!(cmsp.is_none(), "SharedEmitter doesn't support spans");
|
||||||
|
|
||||||
self.buffer.lock().push(Diagnostic {
|
self.buffer.lock().unwrap().push(Diagnostic {
|
||||||
msg: msg.to_string(),
|
msg: msg.to_string(),
|
||||||
code: code.map(|s| s.to_string()),
|
code: code.map(|s| s.to_string()),
|
||||||
lvl: lvl,
|
lvl: lvl,
|
||||||
@ -915,7 +915,7 @@ fn run_work_multithreaded(sess: &Session,
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Avoid holding the lock for the entire duration of the match.
|
// 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 {
|
match maybe_work {
|
||||||
Some(work) => {
|
Some(work) => {
|
||||||
execute_work_item(&cgcx, work);
|
execute_work_item(&cgcx, work);
|
||||||
|
@ -86,7 +86,7 @@ impl<T: Send> Packet<T> {
|
|||||||
// and that could cause problems on platforms where it is
|
// and that could cause problems on platforms where it is
|
||||||
// represented by opaque data structure
|
// represented by opaque data structure
|
||||||
pub fn postinit_lock(&self) -> MutexGuard<()> {
|
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
|
// This function is used at the creation of a shared packet to inherit a
|
||||||
@ -435,7 +435,7 @@ impl<T: Send> Packet<T> {
|
|||||||
// about looking at and dealing with to_wake. Once we have acquired the
|
// about looking at and dealing with to_wake. Once we have acquired the
|
||||||
// lock, we are guaranteed that inherit_blocker is done.
|
// 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
|
// 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 => {}
|
NoneBlocked => {}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
drop(guard); // unlock
|
drop(guard); // unlock
|
||||||
wait_token.wait(); // block
|
wait_token.wait(); // block
|
||||||
lock.lock() // relock
|
lock.lock().unwrap() // relock
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wakes up a thread, dropping the lock at the correct time
|
/// Wakes up a thread, dropping the lock at the correct time
|
||||||
@ -161,7 +161,7 @@ impl<T: Send> Packet<T> {
|
|||||||
fn acquire_send_slot(&self) -> MutexGuard<State<T>> {
|
fn acquire_send_slot(&self) -> MutexGuard<State<T>> {
|
||||||
let mut node = Node { token: None, next: 0 as *mut Node };
|
let mut node = Node { token: None, next: 0 as *mut Node };
|
||||||
loop {
|
loop {
|
||||||
let mut guard = self.lock.lock();
|
let mut guard = self.lock.lock().unwrap();
|
||||||
// are we ready to go?
|
// are we ready to go?
|
||||||
if guard.disconnected || guard.buf.size() < guard.buf.cap() {
|
if guard.disconnected || guard.buf.size() < guard.buf.cap() {
|
||||||
return guard;
|
return guard;
|
||||||
@ -202,7 +202,7 @@ impl<T: Send> Packet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_send(&self, t: T) -> Result<(), super::TrySendError<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 {
|
if guard.disconnected {
|
||||||
Err(super::RecvDisconnected(t))
|
Err(super::RecvDisconnected(t))
|
||||||
} else if guard.buf.size() == guard.buf.cap() {
|
} else if guard.buf.size() == guard.buf.cap() {
|
||||||
@ -239,7 +239,7 @@ impl<T: Send> Packet<T> {
|
|||||||
// When reading this, remember that there can only ever be one receiver at
|
// When reading this, remember that there can only ever be one receiver at
|
||||||
// time.
|
// time.
|
||||||
pub fn recv(&self) -> Result<T, ()> {
|
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
|
// Wait for the buffer to have something in it. No need for a while loop
|
||||||
// because we're the only receiver.
|
// because we're the only receiver.
|
||||||
@ -258,7 +258,7 @@ impl<T: Send> Packet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_recv(&self) -> Result<T, Failure> {
|
pub fn try_recv(&self) -> Result<T, Failure> {
|
||||||
let mut guard = self.lock.lock();
|
let mut guard = self.lock.lock().unwrap();
|
||||||
|
|
||||||
// Easy cases first
|
// Easy cases first
|
||||||
if guard.disconnected { return Err(Disconnected) }
|
if guard.disconnected { return Err(Disconnected) }
|
||||||
@ -315,7 +315,7 @@ impl<T: Send> Packet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not much to do other than wake up a receiver if one's there
|
// 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 }
|
if guard.disconnected { return }
|
||||||
guard.disconnected = true;
|
guard.disconnected = true;
|
||||||
match mem::replace(&mut guard.blocker, NoneBlocked) {
|
match mem::replace(&mut guard.blocker, NoneBlocked) {
|
||||||
@ -326,7 +326,7 @@ impl<T: Send> Packet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop_port(&self) {
|
pub fn drop_port(&self) {
|
||||||
let mut guard = self.lock.lock();
|
let mut guard = self.lock.lock().unwrap();
|
||||||
|
|
||||||
if guard.disconnected { return }
|
if guard.disconnected { return }
|
||||||
guard.disconnected = true;
|
guard.disconnected = true;
|
||||||
@ -372,14 +372,14 @@ impl<T: Send> Packet<T> {
|
|||||||
// If Ok, the value is whether this port has data, if Err, then the upgraded
|
// If Ok, the value is whether this port has data, if Err, then the upgraded
|
||||||
// port needs to be checked instead of this one.
|
// port needs to be checked instead of this one.
|
||||||
pub fn can_recv(&self) -> bool {
|
pub fn can_recv(&self) -> bool {
|
||||||
let guard = self.lock.lock();
|
let guard = self.lock.lock().unwrap();
|
||||||
guard.disconnected || guard.buf.size() > 0
|
guard.disconnected || guard.buf.size() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to start selection on this port. This can either succeed or fail
|
// Attempts to start selection on this port. This can either succeed or fail
|
||||||
// because there is data waiting.
|
// because there is data waiting.
|
||||||
pub fn start_selection(&self, token: SignalToken) -> StartResult {
|
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 {
|
if guard.disconnected || guard.buf.size() > 0 {
|
||||||
Abort
|
Abort
|
||||||
} else {
|
} else {
|
||||||
@ -397,7 +397,7 @@ impl<T: Send> Packet<T> {
|
|||||||
//
|
//
|
||||||
// The return value indicates whether there's data on this port.
|
// The return value indicates whether there's data on this port.
|
||||||
pub fn abort_selection(&self) -> bool {
|
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) {
|
match mem::replace(&mut guard.blocker, NoneBlocked) {
|
||||||
NoneBlocked => true,
|
NoneBlocked => true,
|
||||||
BlockedSender(token) => {
|
BlockedSender(token) => {
|
||||||
@ -413,7 +413,7 @@ impl<T: Send> Packet<T> {
|
|||||||
impl<T: Send> Drop for Packet<T> {
|
impl<T: Send> Drop for Packet<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
assert_eq!(self.channels.load(atomic::SeqCst), 0);
|
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.queue.dequeue().is_none());
|
||||||
assert!(guard.canceled.is_none());
|
assert!(guard.canceled.is_none());
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ impl StdinReader {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
|
pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
|
||||||
StdinReaderGuard {
|
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
|
/// The read is performed atomically - concurrent read calls in other
|
||||||
/// threads will not interleave with this one.
|
/// threads will not interleave with this one.
|
||||||
pub fn read_line(&mut self) -> IoResult<String> {
|
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`.
|
/// Like `Buffer::read_until`.
|
||||||
@ -163,7 +163,7 @@ impl StdinReader {
|
|||||||
/// The read is performed atomically - concurrent read calls in other
|
/// The read is performed atomically - concurrent read calls in other
|
||||||
/// threads will not interleave with this one.
|
/// threads will not interleave with this one.
|
||||||
pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
|
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`.
|
/// Like `Buffer::read_char`.
|
||||||
@ -171,13 +171,13 @@ impl StdinReader {
|
|||||||
/// The read is performed atomically - concurrent read calls in other
|
/// The read is performed atomically - concurrent read calls in other
|
||||||
/// threads will not interleave with this one.
|
/// threads will not interleave with this one.
|
||||||
pub fn read_char(&mut self) -> IoResult<char> {
|
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 {
|
impl Reader for StdinReader {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
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
|
// 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).
|
// incur the costs of repeated locking).
|
||||||
|
|
||||||
fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> {
|
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> {
|
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>> {
|
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> {
|
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> {
|
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 @@ impl Barrier {
|
|||||||
/// Barriers are re-usable after all threads have rendezvoused once, and can
|
/// Barriers are re-usable after all threads have rendezvoused once, and can
|
||||||
/// be used continuously.
|
/// be used continuously.
|
||||||
pub fn wait(&self) {
|
pub fn wait(&self) {
|
||||||
let mut lock = self.lock.lock();
|
let mut lock = self.lock.lock().unwrap();
|
||||||
let local_gen = lock.generation_id;
|
let local_gen = lock.generation_id;
|
||||||
lock.count += 1;
|
lock.count += 1;
|
||||||
if lock.count < self.num_threads {
|
if lock.count < self.num_threads {
|
||||||
@ -77,7 +77,7 @@ impl Barrier {
|
|||||||
// http://en.wikipedia.org/wiki/Spurious_wakeup
|
// http://en.wikipedia.org/wiki/Spurious_wakeup
|
||||||
while local_gen == lock.generation_id &&
|
while local_gen == lock.generation_id &&
|
||||||
lock.count < self.num_threads {
|
lock.count < self.num_threads {
|
||||||
self.cvar.wait(&lock);
|
lock = self.cvar.wait(lock).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lock.count = 0;
|
lock.count = 0;
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
use sync::atomic::{mod, AtomicUint};
|
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::condvar as sys;
|
||||||
use sys_common::mutex as sys_mutex;
|
use sys_common::mutex as sys_mutex;
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
@ -44,16 +45,16 @@ use time::Duration;
|
|||||||
/// // Inside of our lock, spawn a new thread, and then wait for it to start
|
/// // Inside of our lock, spawn a new thread, and then wait for it to start
|
||||||
/// Thread::spawn(move|| {
|
/// Thread::spawn(move|| {
|
||||||
/// let &(ref lock, ref cvar) = &*pair2;
|
/// let &(ref lock, ref cvar) = &*pair2;
|
||||||
/// let mut started = lock.lock();
|
/// let mut started = lock.lock().unwrap();
|
||||||
/// *started = true;
|
/// *started = true;
|
||||||
/// cvar.notify_one();
|
/// cvar.notify_one();
|
||||||
/// }).detach();
|
/// }).detach();
|
||||||
///
|
///
|
||||||
/// // wait for the thread to start up
|
/// // wait for the thread to start up
|
||||||
/// let &(ref lock, ref cvar) = &*pair;
|
/// let &(ref lock, ref cvar) = &*pair;
|
||||||
/// let started = lock.lock();
|
/// let mut started = lock.lock().unwrap();
|
||||||
/// while !*started {
|
/// while !*started {
|
||||||
/// cvar.wait(&started);
|
/// started = cvar.wait(started).unwrap();
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Condvar { inner: Box<StaticCondvar> }
|
pub struct Condvar { inner: Box<StaticCondvar> }
|
||||||
@ -92,9 +93,9 @@ pub const CONDVAR_INIT: StaticCondvar = StaticCondvar {
|
|||||||
///
|
///
|
||||||
/// Note that this trait should likely not be implemented manually unless you
|
/// Note that this trait should likely not be implemented manually unless you
|
||||||
/// really know what you're doing.
|
/// really know what you're doing.
|
||||||
pub trait AsMutexGuard {
|
pub trait AsGuard {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard;
|
fn as_guard(&self) -> CondvarGuard;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Condvar {
|
impl Condvar {
|
||||||
@ -113,8 +114,8 @@ impl Condvar {
|
|||||||
/// notification.
|
/// notification.
|
||||||
///
|
///
|
||||||
/// This function will atomically unlock the mutex specified (represented by
|
/// This function will atomically unlock the mutex specified (represented by
|
||||||
/// `guard`) and block the current thread. This means that any calls to
|
/// `mutex_guard`) and block the current thread. This means that any calls
|
||||||
/// `notify_*()` which happen logically after the mutex is unlocked are
|
/// to `notify_*()` which happen logically after the mutex is unlocked are
|
||||||
/// candidates to wake this thread up. When this function call returns, the
|
/// candidates to wake this thread up. When this function call returns, the
|
||||||
/// lock specified will have been re-acquired.
|
/// lock specified will have been re-acquired.
|
||||||
///
|
///
|
||||||
@ -123,13 +124,20 @@ impl Condvar {
|
|||||||
/// the predicate must always be checked each time this function returns to
|
/// the predicate must always be checked each time this function returns to
|
||||||
/// protect against spurious wakeups.
|
/// 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
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This function will `panic!()` if it is used with more than one mutex
|
/// This function will `panic!()` if it is used with more than one mutex
|
||||||
/// over time. Each condition variable is dynamically bound to exactly one
|
/// over time. Each condition variable is dynamically bound to exactly one
|
||||||
/// mutex to ensure defined behavior across platforms. If this functionality
|
/// mutex to ensure defined behavior across platforms. If this functionality
|
||||||
/// is not desired, then unsafe primitives in `sys` are provided.
|
/// 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 {
|
unsafe {
|
||||||
let me: &'static Condvar = &*(self as *const _);
|
let me: &'static Condvar = &*(self as *const _);
|
||||||
me.inner.wait(mutex_guard)
|
me.inner.wait(mutex_guard)
|
||||||
@ -156,8 +164,8 @@ impl Condvar {
|
|||||||
// provide. There are also additional concerns about the unix-specific
|
// provide. There are also additional concerns about the unix-specific
|
||||||
// implementation which may need to be addressed.
|
// implementation which may need to be addressed.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn wait_timeout<T: AsMutexGuard>(&self, mutex_guard: &T,
|
fn wait_timeout<T: AsGuard>(&self, mutex_guard: T, dur: Duration)
|
||||||
dur: Duration) -> bool {
|
-> LockResult<(T, bool)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let me: &'static Condvar = &*(self as *const _);
|
let me: &'static Condvar = &*(self as *const _);
|
||||||
me.inner.wait_timeout(mutex_guard, dur)
|
me.inner.wait_timeout(mutex_guard, dur)
|
||||||
@ -194,13 +202,17 @@ impl StaticCondvar {
|
|||||||
/// notification.
|
/// notification.
|
||||||
///
|
///
|
||||||
/// See `Condvar::wait`.
|
/// See `Condvar::wait`.
|
||||||
pub fn wait<T: AsMutexGuard>(&'static self, mutex_guard: &T) {
|
pub fn wait<T: AsGuard>(&'static self, mutex_guard: T) -> LockResult<T> {
|
||||||
unsafe {
|
let poisoned = unsafe {
|
||||||
let lock = mutex_guard.as_mutex_guard();
|
let cvar_guard = mutex_guard.as_guard();
|
||||||
let sys = mutex::guard_lock(lock);
|
self.verify(cvar_guard.lock);
|
||||||
self.verify(sys);
|
self.inner.wait(cvar_guard.lock);
|
||||||
self.inner.wait(sys);
|
cvar_guard.poisoned.get()
|
||||||
(*mutex::guard_poison(lock)).check("mutex");
|
};
|
||||||
|
if poisoned {
|
||||||
|
Err(poison::new_poison_error(mutex_guard))
|
||||||
|
} else {
|
||||||
|
Ok(mutex_guard)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,15 +221,18 @@ impl StaticCondvar {
|
|||||||
///
|
///
|
||||||
/// See `Condvar::wait_timeout`.
|
/// See `Condvar::wait_timeout`.
|
||||||
#[allow(dead_code)] // may want to stabilize this later, see wait_timeout above
|
#[allow(dead_code)] // may want to stabilize this later, see wait_timeout above
|
||||||
fn wait_timeout<T: AsMutexGuard>(&'static self, mutex_guard: &T,
|
fn wait_timeout<T: AsGuard>(&'static self, mutex_guard: T, dur: Duration)
|
||||||
dur: Duration) -> bool {
|
-> LockResult<(T, bool)> {
|
||||||
unsafe {
|
let (poisoned, success) = unsafe {
|
||||||
let lock = mutex_guard.as_mutex_guard();
|
let cvar_guard = mutex_guard.as_guard();
|
||||||
let sys = mutex::guard_lock(lock);
|
self.verify(cvar_guard.lock);
|
||||||
self.verify(sys);
|
let success = self.inner.wait_timeout(cvar_guard.lock, dur);
|
||||||
let ret = self.inner.wait_timeout(sys, dur);
|
(cvar_guard.poisoned.get(), success)
|
||||||
(*mutex::guard_poison(lock)).check("mutex");
|
};
|
||||||
return ret;
|
if poisoned {
|
||||||
|
Err(poison::new_poison_error((mutex_guard, success)))
|
||||||
|
} else {
|
||||||
|
Ok((mutex_guard, success))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,12 +303,12 @@ mod tests {
|
|||||||
static C: StaticCondvar = CONDVAR_INIT;
|
static C: StaticCondvar = CONDVAR_INIT;
|
||||||
static M: StaticMutex = MUTEX_INIT;
|
static M: StaticMutex = MUTEX_INIT;
|
||||||
|
|
||||||
let g = M.lock();
|
let g = M.lock().unwrap();
|
||||||
spawn(move|| {
|
spawn(move|| {
|
||||||
let _g = M.lock();
|
let _g = M.lock().unwrap();
|
||||||
C.notify_one();
|
C.notify_one();
|
||||||
});
|
});
|
||||||
C.wait(&g);
|
let g = C.wait(g).unwrap();
|
||||||
drop(g);
|
drop(g);
|
||||||
unsafe { C.destroy(); M.destroy(); }
|
unsafe { C.destroy(); M.destroy(); }
|
||||||
}
|
}
|
||||||
@ -309,13 +324,13 @@ mod tests {
|
|||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
spawn(move|| {
|
spawn(move|| {
|
||||||
let &(ref lock, ref cond) = &*data;
|
let &(ref lock, ref cond) = &*data;
|
||||||
let mut cnt = lock.lock();
|
let mut cnt = lock.lock().unwrap();
|
||||||
*cnt += 1;
|
*cnt += 1;
|
||||||
if *cnt == N {
|
if *cnt == N {
|
||||||
tx.send(());
|
tx.send(());
|
||||||
}
|
}
|
||||||
while *cnt != 0 {
|
while *cnt != 0 {
|
||||||
cond.wait(&cnt);
|
cnt = cond.wait(cnt).unwrap();
|
||||||
}
|
}
|
||||||
tx.send(());
|
tx.send(());
|
||||||
});
|
});
|
||||||
@ -324,7 +339,7 @@ mod tests {
|
|||||||
|
|
||||||
let &(ref lock, ref cond) = &*data;
|
let &(ref lock, ref cond) = &*data;
|
||||||
rx.recv();
|
rx.recv();
|
||||||
let mut cnt = lock.lock();
|
let mut cnt = lock.lock().unwrap();
|
||||||
*cnt = 0;
|
*cnt = 0;
|
||||||
cond.notify_all();
|
cond.notify_all();
|
||||||
drop(cnt);
|
drop(cnt);
|
||||||
@ -339,13 +354,15 @@ mod tests {
|
|||||||
static C: StaticCondvar = CONDVAR_INIT;
|
static C: StaticCondvar = CONDVAR_INIT;
|
||||||
static M: StaticMutex = MUTEX_INIT;
|
static M: StaticMutex = MUTEX_INIT;
|
||||||
|
|
||||||
let g = M.lock();
|
let g = M.lock().unwrap();
|
||||||
assert!(!C.wait_timeout(&g, Duration::nanoseconds(1000)));
|
let (g, success) = C.wait_timeout(g, Duration::nanoseconds(1000)).unwrap();
|
||||||
|
assert!(!success);
|
||||||
spawn(move|| {
|
spawn(move|| {
|
||||||
let _g = M.lock();
|
let _g = M.lock().unwrap();
|
||||||
C.notify_one();
|
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);
|
drop(g);
|
||||||
unsafe { C.destroy(); M.destroy(); }
|
unsafe { C.destroy(); M.destroy(); }
|
||||||
}
|
}
|
||||||
@ -357,15 +374,15 @@ mod tests {
|
|||||||
static M2: StaticMutex = MUTEX_INIT;
|
static M2: StaticMutex = MUTEX_INIT;
|
||||||
static C: StaticCondvar = CONDVAR_INIT;
|
static C: StaticCondvar = CONDVAR_INIT;
|
||||||
|
|
||||||
let g = M1.lock();
|
let mut g = M1.lock().unwrap();
|
||||||
spawn(move|| {
|
spawn(move|| {
|
||||||
let _g = M1.lock();
|
let _g = M1.lock().unwrap();
|
||||||
C.notify_one();
|
C.notify_one();
|
||||||
});
|
});
|
||||||
C.wait(&g);
|
g = C.wait(g).unwrap();
|
||||||
drop(g);
|
drop(g);
|
||||||
|
|
||||||
C.wait(&M2.lock());
|
C.wait(M2.lock().unwrap()).unwrap();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,16 +17,20 @@
|
|||||||
|
|
||||||
#![experimental]
|
#![experimental]
|
||||||
|
|
||||||
|
use sys_common::mutex as sys_mutex;
|
||||||
|
|
||||||
pub use alloc::arc::{Arc, Weak};
|
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::{RWLock, StaticRWLock, RWLOCK_INIT};
|
||||||
pub use self::rwlock::{RWLockReadGuard, RWLockWriteGuard};
|
pub use self::rwlock::{RWLockReadGuard, RWLockWriteGuard};
|
||||||
pub use self::rwlock::{StaticRWLockReadGuard, StaticRWLockWriteGuard};
|
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::once::{Once, ONCE_INIT};
|
||||||
pub use self::semaphore::{Semaphore, SemaphoreGuard};
|
pub use self::semaphore::{Semaphore, SemaphoreGuard};
|
||||||
pub use self::barrier::Barrier;
|
pub use self::barrier::Barrier;
|
||||||
|
pub use self::poison::{PoisonError, TryLockError, TryLockResult, LockResult};
|
||||||
|
|
||||||
pub use self::future::Future;
|
pub use self::future::Future;
|
||||||
pub use self::task_pool::TaskPool;
|
pub use self::task_pool::TaskPool;
|
||||||
@ -41,3 +45,10 @@ mod poison;
|
|||||||
mod rwlock;
|
mod rwlock;
|
||||||
mod semaphore;
|
mod semaphore;
|
||||||
mod task_pool;
|
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 prelude::*;
|
|||||||
|
|
||||||
use cell::UnsafeCell;
|
use cell::UnsafeCell;
|
||||||
use kinds::marker;
|
use kinds::marker;
|
||||||
use sync::{poison, AsMutexGuard};
|
use sync::{AsGuard, CondvarGuard};
|
||||||
|
use sync::poison::{mod, TryLockError, TryLockResult, LockResult};
|
||||||
use sys_common::mutex as sys;
|
use sys_common::mutex as sys;
|
||||||
|
|
||||||
/// A mutual exclusion primitive useful for protecting shared data
|
/// A mutual exclusion primitive useful for protecting shared data
|
||||||
@ -26,12 +27,23 @@ use sys_common::mutex as sys;
|
|||||||
///
|
///
|
||||||
/// # Poisoning
|
/// # Poisoning
|
||||||
///
|
///
|
||||||
/// In order to prevent access to otherwise invalid data, each mutex will
|
/// The mutexes in this module implement a strategy called "poisoning" where a
|
||||||
/// propagate any panics which occur while the lock is held. Once a thread has
|
/// mutex is considered poisoned whenever a thread panics while holding the
|
||||||
/// panicked while holding the lock, then all other threads will immediately
|
/// lock. Once a mutex is poisoned, all other tasks are unable to access the
|
||||||
/// panic as well once they hold the lock.
|
/// 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
|
/// ```rust
|
||||||
/// use std::sync::{Arc, Mutex};
|
/// use std::sync::{Arc, Mutex};
|
||||||
@ -48,11 +60,14 @@ use sys_common::mutex as sys;
|
|||||||
/// let (tx, rx) = channel();
|
/// let (tx, rx) = channel();
|
||||||
/// for _ in range(0u, 10) {
|
/// for _ in range(0u, 10) {
|
||||||
/// let (data, tx) = (data.clone(), tx.clone());
|
/// 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.
|
/// // The shared static can only be accessed once the lock is held.
|
||||||
/// // Our non-atomic increment is safe because we're the only thread
|
/// // Our non-atomic increment is safe because we're the only thread
|
||||||
/// // which can access the shared state when the lock is held.
|
/// // 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;
|
/// *data += 1;
|
||||||
/// if *data == N {
|
/// if *data == N {
|
||||||
/// tx.send(());
|
/// tx.send(());
|
||||||
@ -63,6 +78,35 @@ use sys_common::mutex as sys;
|
|||||||
///
|
///
|
||||||
/// rx.recv();
|
/// 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> {
|
pub struct Mutex<T> {
|
||||||
// Note that this static mutex is in a *box*, not inlined into the struct
|
// 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
|
// 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;
|
/// static LOCK: StaticMutex = MUTEX_INIT;
|
||||||
///
|
///
|
||||||
/// {
|
/// {
|
||||||
/// let _g = LOCK.lock();
|
/// let _g = LOCK.lock().unwrap();
|
||||||
/// // do some productive work
|
/// // do some productive work
|
||||||
/// }
|
/// }
|
||||||
/// // lock is unlocked here.
|
/// // lock is unlocked here.
|
||||||
/// ```
|
/// ```
|
||||||
pub struct StaticMutex {
|
pub struct StaticMutex {
|
||||||
lock: sys::Mutex,
|
lock: sys::Mutex,
|
||||||
poison: UnsafeCell<poison::Flag>,
|
poison: poison::Flag,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sync for StaticMutex {}
|
unsafe impl Sync for StaticMutex {}
|
||||||
@ -114,24 +158,27 @@ unsafe impl Sync for StaticMutex {}
|
|||||||
pub struct MutexGuard<'a, T: 'a> {
|
pub struct MutexGuard<'a, T: 'a> {
|
||||||
// funny underscores due to how Deref/DerefMut currently work (they
|
// funny underscores due to how Deref/DerefMut currently work (they
|
||||||
// disregard field privacy).
|
// disregard field privacy).
|
||||||
__lock: &'a Mutex<T>,
|
__inner: Guard<'a, Mutex<T>>,
|
||||||
__guard: StaticMutexGuard,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An RAII implementation of a "scoped lock" of a static mutex. When this
|
/// 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.
|
/// structure is dropped (falls out of scope), the lock will be unlocked.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct StaticMutexGuard {
|
pub struct StaticMutexGuard {
|
||||||
lock: &'static sys::Mutex,
|
inner: Guard<'static, StaticMutex>,
|
||||||
marker: marker::NoSend,
|
}
|
||||||
poison: poison::Guard<'static>,
|
|
||||||
|
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
|
/// Static initialization of a mutex. This constant can be used to initialize
|
||||||
/// other mutex constants.
|
/// other mutex constants.
|
||||||
pub const MUTEX_INIT: StaticMutex = StaticMutex {
|
pub const MUTEX_INIT: StaticMutex = StaticMutex {
|
||||||
lock: sys::MUTEX_INIT,
|
lock: sys::MUTEX_INIT,
|
||||||
poison: UnsafeCell { value: poison::Flag { failed: false } },
|
poison: poison::FLAG_INIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<T: Send> Mutex<T> {
|
impl<T: Send> Mutex<T> {
|
||||||
@ -150,15 +197,13 @@ impl<T: Send> Mutex<T> {
|
|||||||
/// held. An RAII guard is returned to allow scoped unlock of the lock. When
|
/// 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.
|
/// 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
|
/// If another user of this mutex panicked while holding the mutex, then
|
||||||
/// this call will immediately panic once the mutex is acquired.
|
/// this call will return an error once the mutex is acquired.
|
||||||
pub fn lock(&self) -> MutexGuard<T> {
|
pub fn lock(&self) -> LockResult<MutexGuard<T>> {
|
||||||
unsafe {
|
unsafe { self.inner.lock.lock() }
|
||||||
let lock: &'static StaticMutex = &*(&*self.inner as *const _);
|
MutexGuard::new(self)
|
||||||
MutexGuard::new(self, lock.lock())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to acquire this lock.
|
/// Attempts to acquire this lock.
|
||||||
@ -169,17 +214,16 @@ impl<T: Send> Mutex<T> {
|
|||||||
///
|
///
|
||||||
/// This function does not block.
|
/// This function does not block.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// If another user of this mutex panicked while holding the mutex, then
|
/// 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.
|
/// acquired.
|
||||||
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
|
pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> {
|
||||||
unsafe {
|
if unsafe { self.inner.lock.try_lock() } {
|
||||||
let lock: &'static StaticMutex = &*(&*self.inner as *const _);
|
Ok(try!(MutexGuard::new(self)))
|
||||||
lock.try_lock().map(|guard| {
|
} else {
|
||||||
MutexGuard::new(self, guard)
|
Err(TryLockError::WouldBlock)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,17 +240,19 @@ impl<T: Send> Drop for Mutex<T> {
|
|||||||
|
|
||||||
impl StaticMutex {
|
impl StaticMutex {
|
||||||
/// Acquires this lock, see `Mutex::lock`
|
/// Acquires this lock, see `Mutex::lock`
|
||||||
pub fn lock(&'static self) -> StaticMutexGuard {
|
#[inline]
|
||||||
|
pub fn lock(&'static self) -> LockResult<StaticMutexGuard> {
|
||||||
unsafe { self.lock.lock() }
|
unsafe { self.lock.lock() }
|
||||||
StaticMutexGuard::new(self)
|
StaticMutexGuard::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to grab this lock, see `Mutex::try_lock`
|
/// 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() } {
|
if unsafe { self.lock.try_lock() } {
|
||||||
Some(StaticMutexGuard::new(self))
|
Ok(try!(StaticMutexGuard::new(self)))
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(TryLockError::WouldBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,53 +272,85 @@ impl StaticMutex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'mutex, T> MutexGuard<'mutex, T> {
|
impl<'mutex, T> MutexGuard<'mutex, T> {
|
||||||
fn new(lock: &Mutex<T>, guard: StaticMutexGuard) -> MutexGuard<T> {
|
fn new(lock: &Mutex<T>) -> LockResult<MutexGuard<T>> {
|
||||||
MutexGuard { __lock: lock, __guard: guard }
|
poison::map_result(Guard::new(lock), |guard| {
|
||||||
|
MutexGuard { __inner: guard }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mutex, T> AsMutexGuard for MutexGuard<'mutex, T> {
|
impl<T> AsGuard for Mutex<T> {
|
||||||
unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { &self.__guard }
|
fn as_guard(&self) -> CondvarGuard { self.inner.as_guard() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'mutex, T> Deref<T> for MutexGuard<'mutex, T> {
|
impl<'mutex, T> AsGuard for MutexGuard<'mutex, T> {
|
||||||
fn deref<'a>(&'a self) -> &'a T { unsafe { &*self.__lock.data.get() } }
|
fn as_guard(&self) -> CondvarGuard {
|
||||||
}
|
CondvarGuard {
|
||||||
impl<'mutex, T> DerefMut<T> for MutexGuard<'mutex, T> {
|
lock: &self.__inner.inner.inner.lock,
|
||||||
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
|
poisoned: &self.__inner.inner.inner.poison,
|
||||||
unsafe { &mut *self.__lock.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn guard_lock(guard: &StaticMutexGuard) -> &sys::Mutex { guard.lock }
|
impl<'mutex, T> Deref<T> for MutexGuard<'mutex, T> {
|
||||||
pub fn guard_poison(guard: &StaticMutexGuard) -> &poison::Guard {
|
fn deref<'a>(&'a self) -> &'a T {
|
||||||
&guard.poison
|
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.__inner.inner.data.get() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsMutexGuard for StaticMutexGuard {
|
impl StaticMutexGuard {
|
||||||
unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { self }
|
#[inline]
|
||||||
|
fn new(lock: &'static StaticMutex) -> LockResult<StaticMutexGuard> {
|
||||||
|
poison::map_result(Guard::new(lock), |guard| {
|
||||||
|
StaticMutexGuard { inner: guard }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsGuard for StaticMutex {
|
||||||
|
#[inline]
|
||||||
|
fn as_guard(&self) -> CondvarGuard {
|
||||||
|
CondvarGuard { lock: &self.lock, poisoned: &self.poison }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
#[unsafe_destructor]
|
||||||
impl Drop for StaticMutexGuard {
|
impl<'a, T: AsGuard> Drop for Guard<'a, T> {
|
||||||
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.poison.done();
|
let data = self.inner.as_guard();
|
||||||
self.lock.unlock();
|
data.poisoned.done(&self.poison);
|
||||||
|
data.lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,16 +370,16 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn smoke() {
|
fn smoke() {
|
||||||
let m = Mutex::new(());
|
let m = Mutex::new(());
|
||||||
drop(m.lock());
|
drop(m.lock().unwrap());
|
||||||
drop(m.lock());
|
drop(m.lock().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke_static() {
|
fn smoke_static() {
|
||||||
static M: StaticMutex = MUTEX_INIT;
|
static M: StaticMutex = MUTEX_INIT;
|
||||||
unsafe {
|
unsafe {
|
||||||
drop(M.lock());
|
drop(M.lock().unwrap());
|
||||||
drop(M.lock());
|
drop(M.lock().unwrap());
|
||||||
M.destroy();
|
M.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,7 +394,7 @@ mod test {
|
|||||||
fn inc() {
|
fn inc() {
|
||||||
for _ in range(0, J) {
|
for _ in range(0, J) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _g = M.lock();
|
let _g = M.lock().unwrap();
|
||||||
CNT += 1;
|
CNT += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,7 +421,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn try_lock() {
|
fn try_lock() {
|
||||||
let m = Mutex::new(());
|
let m = Mutex::new(());
|
||||||
assert!(m.try_lock().is_some());
|
*m.try_lock().unwrap() = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -355,22 +433,21 @@ mod test {
|
|||||||
// wait until parent gets in
|
// wait until parent gets in
|
||||||
rx.recv();
|
rx.recv();
|
||||||
let &(ref lock, ref cvar) = &*packet2.0;
|
let &(ref lock, ref cvar) = &*packet2.0;
|
||||||
let mut lock = lock.lock();
|
let mut lock = lock.lock().unwrap();
|
||||||
*lock = true;
|
*lock = true;
|
||||||
cvar.notify_one();
|
cvar.notify_one();
|
||||||
});
|
});
|
||||||
|
|
||||||
let &(ref lock, ref cvar) = &*packet.0;
|
let &(ref lock, ref cvar) = &*packet.0;
|
||||||
let lock = lock.lock();
|
let mut lock = lock.lock().unwrap();
|
||||||
tx.send(());
|
tx.send(());
|
||||||
assert!(!*lock);
|
assert!(!*lock);
|
||||||
while !*lock {
|
while !*lock {
|
||||||
cvar.wait(&lock);
|
lock = cvar.wait(lock).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_fail]
|
|
||||||
fn test_arc_condvar_poison() {
|
fn test_arc_condvar_poison() {
|
||||||
let packet = Packet(Arc::new((Mutex::new(1i), Condvar::new())));
|
let packet = Packet(Arc::new((Mutex::new(1i), Condvar::new())));
|
||||||
let packet2 = Packet(packet.0.clone());
|
let packet2 = Packet(packet.0.clone());
|
||||||
@ -379,31 +456,35 @@ mod test {
|
|||||||
spawn(move|| {
|
spawn(move|| {
|
||||||
rx.recv();
|
rx.recv();
|
||||||
let &(ref lock, ref cvar) = &*packet2.0;
|
let &(ref lock, ref cvar) = &*packet2.0;
|
||||||
let _g = lock.lock();
|
let _g = lock.lock().unwrap();
|
||||||
cvar.notify_one();
|
cvar.notify_one();
|
||||||
// Parent should fail when it wakes up.
|
// Parent should fail when it wakes up.
|
||||||
panic!();
|
panic!();
|
||||||
});
|
});
|
||||||
|
|
||||||
let &(ref lock, ref cvar) = &*packet.0;
|
let &(ref lock, ref cvar) = &*packet.0;
|
||||||
let lock = lock.lock();
|
let mut lock = lock.lock().unwrap();
|
||||||
tx.send(());
|
tx.send(());
|
||||||
while *lock == 1 {
|
while *lock == 1 {
|
||||||
cvar.wait(&lock);
|
match cvar.wait(lock) {
|
||||||
|
Ok(l) => {
|
||||||
|
lock = l;
|
||||||
|
assert_eq!(*lock, 1);
|
||||||
|
}
|
||||||
|
Err(..) => break,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_fail]
|
|
||||||
fn test_mutex_arc_poison() {
|
fn test_mutex_arc_poison() {
|
||||||
let arc = Arc::new(Mutex::new(1i));
|
let arc = Arc::new(Mutex::new(1i));
|
||||||
let arc2 = arc.clone();
|
let arc2 = arc.clone();
|
||||||
let _ = Thread::spawn(move|| {
|
Thread::spawn(move|| {
|
||||||
let lock = arc2.lock();
|
let lock = arc2.lock().unwrap();
|
||||||
assert_eq!(*lock, 2);
|
assert_eq!(*lock, 2);
|
||||||
}).join();
|
}).join();
|
||||||
let lock = arc.lock();
|
assert!(arc.lock().is_err());
|
||||||
assert_eq!(*lock, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -414,8 +495,8 @@ mod test {
|
|||||||
let arc2 = Arc::new(Mutex::new(arc));
|
let arc2 = Arc::new(Mutex::new(arc));
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
spawn(move|| {
|
spawn(move|| {
|
||||||
let lock = arc2.lock();
|
let lock = arc2.lock().unwrap();
|
||||||
let lock2 = lock.deref().lock();
|
let lock2 = lock.deref().lock().unwrap();
|
||||||
assert_eq!(*lock2, 1);
|
assert_eq!(*lock2, 1);
|
||||||
tx.send(());
|
tx.send(());
|
||||||
});
|
});
|
||||||
@ -432,13 +513,13 @@ mod test {
|
|||||||
}
|
}
|
||||||
impl Drop for Unwinder {
|
impl Drop for Unwinder {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
*self.i.lock() += 1;
|
*self.i.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _u = Unwinder { i: arc2 };
|
let _u = Unwinder { i: arc2 };
|
||||||
panic!();
|
panic!();
|
||||||
}).join();
|
}).join();
|
||||||
let lock = arc.lock();
|
let lock = arc.lock().unwrap();
|
||||||
assert_eq!(*lock, 2);
|
assert_eq!(*lock, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,31 +8,120 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
|
use cell::UnsafeCell;
|
||||||
|
use error::FromError;
|
||||||
|
use fmt;
|
||||||
use thread::Thread;
|
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 {
|
impl Flag {
|
||||||
pub fn borrow(&mut self) -> Guard {
|
#[inline]
|
||||||
Guard { flag: &mut self.failed, panicking: Thread::panicking() }
|
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> {
|
#[allow(missing_copy_implementations)]
|
||||||
flag: &'a mut bool,
|
pub struct Guard {
|
||||||
panicking: bool,
|
panicking: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Guard<'a> {
|
/// A type of error which can be returned whenever a lock is acquired.
|
||||||
pub fn check(&self, name: &str) {
|
///
|
||||||
if *self.flag {
|
/// Both Mutexes and RWLocks are poisoned whenever a task fails while the lock
|
||||||
panic!("poisoned {} - another task failed inside", name);
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn done(&mut self) {
|
/// An enumeration of possible errors which can occur while calling the
|
||||||
if !self.panicking && Thread::panicking() {
|
/// `try_lock` method.
|
||||||
*self.flag = true;
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 prelude::*;
|
||||||
|
|
||||||
use kinds::marker;
|
|
||||||
use cell::UnsafeCell;
|
use cell::UnsafeCell;
|
||||||
|
use kinds::marker;
|
||||||
|
use sync::poison::{mod, LockResult, TryLockError, TryLockResult};
|
||||||
use sys_common::rwlock as sys;
|
use sys_common::rwlock as sys;
|
||||||
use sync::poison;
|
|
||||||
|
|
||||||
/// A reader-writer lock
|
/// A reader-writer lock
|
||||||
///
|
///
|
||||||
@ -28,12 +28,14 @@ use sync::poison;
|
|||||||
/// locking methods implement `Deref` (and `DerefMut` for the `write` methods)
|
/// locking methods implement `Deref` (and `DerefMut` for the `write` methods)
|
||||||
/// to allow access to the contained of the lock.
|
/// to allow access to the contained of the lock.
|
||||||
///
|
///
|
||||||
|
/// # Poisoning
|
||||||
|
///
|
||||||
/// RWLocks, like Mutexes, will become poisoned on panics. Note, however, that
|
/// 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
|
/// 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
|
/// exclusively (write mode). If a panic occurs in any reader, then the lock
|
||||||
/// will not be poisoned.
|
/// will not be poisoned.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::sync::RWLock;
|
/// use std::sync::RWLock;
|
||||||
@ -42,15 +44,15 @@ use sync::poison;
|
|||||||
///
|
///
|
||||||
/// // many reader locks can be held at once
|
/// // many reader locks can be held at once
|
||||||
/// {
|
/// {
|
||||||
/// let r1 = lock.read();
|
/// let r1 = lock.read().unwrap();
|
||||||
/// let r2 = lock.read();
|
/// let r2 = lock.read().unwrap();
|
||||||
/// assert_eq!(*r1, 5);
|
/// assert_eq!(*r1, 5);
|
||||||
/// assert_eq!(*r2, 5);
|
/// assert_eq!(*r2, 5);
|
||||||
/// } // read locks are dropped at this point
|
/// } // read locks are dropped at this point
|
||||||
///
|
///
|
||||||
/// // only one write lock may be held, however
|
/// // only one write lock may be held, however
|
||||||
/// {
|
/// {
|
||||||
/// let mut w = lock.write();
|
/// let mut w = lock.write().unwrap();
|
||||||
/// *w += 1;
|
/// *w += 1;
|
||||||
/// assert_eq!(*w, 6);
|
/// assert_eq!(*w, 6);
|
||||||
/// } // write lock is dropped here
|
/// } // write lock is dropped here
|
||||||
@ -77,18 +79,18 @@ unsafe impl<T> Sync for RWLock<T> {}
|
|||||||
/// static LOCK: StaticRWLock = RWLOCK_INIT;
|
/// static LOCK: StaticRWLock = RWLOCK_INIT;
|
||||||
///
|
///
|
||||||
/// {
|
/// {
|
||||||
/// let _g = LOCK.read();
|
/// let _g = LOCK.read().unwrap();
|
||||||
/// // ... shared read access
|
/// // ... shared read access
|
||||||
/// }
|
/// }
|
||||||
/// {
|
/// {
|
||||||
/// let _g = LOCK.write();
|
/// let _g = LOCK.write().unwrap();
|
||||||
/// // ... exclusive write access
|
/// // ... exclusive write access
|
||||||
/// }
|
/// }
|
||||||
/// unsafe { LOCK.destroy() } // free all resources
|
/// unsafe { LOCK.destroy() } // free all resources
|
||||||
/// ```
|
/// ```
|
||||||
pub struct StaticRWLock {
|
pub struct StaticRWLock {
|
||||||
inner: sys::RWLock,
|
lock: sys::RWLock,
|
||||||
poison: UnsafeCell<poison::Flag>,
|
poison: poison::Flag,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for StaticRWLock {}
|
unsafe impl Send for StaticRWLock {}
|
||||||
@ -96,41 +98,52 @@ unsafe impl Sync for StaticRWLock {}
|
|||||||
|
|
||||||
/// Constant initialization for a statically-initialized rwlock.
|
/// Constant initialization for a statically-initialized rwlock.
|
||||||
pub const RWLOCK_INIT: StaticRWLock = StaticRWLock {
|
pub const RWLOCK_INIT: StaticRWLock = StaticRWLock {
|
||||||
inner: sys::RWLOCK_INIT,
|
lock: sys::RWLOCK_INIT,
|
||||||
poison: UnsafeCell { value: poison::Flag { failed: false } },
|
poison: poison::FLAG_INIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// RAII structure used to release the shared read access of a lock when
|
/// RAII structure used to release the shared read access of a lock when
|
||||||
/// dropped.
|
/// dropped.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct RWLockReadGuard<'a, T: 'a> {
|
pub struct RWLockReadGuard<'a, T: 'a> {
|
||||||
__lock: &'a RWLock<T>,
|
__inner: ReadGuard<'a, RWLock<T>>,
|
||||||
__guard: StaticRWLockReadGuard,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RAII structure used to release the exclusive write access of a lock when
|
/// RAII structure used to release the exclusive write access of a lock when
|
||||||
/// dropped.
|
/// dropped.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct RWLockWriteGuard<'a, T: 'a> {
|
pub struct RWLockWriteGuard<'a, T: 'a> {
|
||||||
__lock: &'a RWLock<T>,
|
__inner: WriteGuard<'a, RWLock<T>>,
|
||||||
__guard: StaticRWLockWriteGuard,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RAII structure used to release the shared read access of a lock when
|
/// RAII structure used to release the shared read access of a lock when
|
||||||
/// dropped.
|
/// dropped.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct StaticRWLockReadGuard {
|
pub struct StaticRWLockReadGuard {
|
||||||
lock: &'static sys::RWLock,
|
_inner: ReadGuard<'static, StaticRWLock>,
|
||||||
marker: marker::NoSend,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RAII structure used to release the exclusive write access of a lock when
|
/// RAII structure used to release the exclusive write access of a lock when
|
||||||
/// dropped.
|
/// dropped.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct StaticRWLockWriteGuard {
|
pub struct StaticRWLockWriteGuard {
|
||||||
lock: &'static sys::RWLock,
|
_inner: WriteGuard<'static, StaticRWLock>,
|
||||||
marker: marker::NoSend,
|
}
|
||||||
poison: poison::Guard<'static>,
|
|
||||||
|
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> {
|
impl<T: Send + Sync> RWLock<T> {
|
||||||
@ -151,17 +164,15 @@ impl<T: Send + Sync> RWLock<T> {
|
|||||||
/// Returns an RAII guard which will release this thread's shared access
|
/// Returns an RAII guard which will release this thread's shared access
|
||||||
/// once it is dropped.
|
/// once it is dropped.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// This function will panic if the RWLock is poisoned. An RWLock is
|
/// This function will return an error if the RWLock is poisoned. An RWLock
|
||||||
/// poisoned whenever a writer panics while holding an exclusive lock. The
|
/// is poisoned whenever a writer panics while holding an exclusive lock.
|
||||||
/// panic will occur immediately after the lock has been acquired.
|
/// The failure will occur immediately after the lock has been acquired.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read(&self) -> RWLockReadGuard<T> {
|
pub fn read(&self) -> LockResult<RWLockReadGuard<T>> {
|
||||||
unsafe {
|
unsafe { self.inner.lock.read() }
|
||||||
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
|
RWLockReadGuard::new(self)
|
||||||
RWLockReadGuard::new(self, lock.read())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to acquire this lock with shared read access.
|
/// Attempt to acquire this lock with shared read access.
|
||||||
@ -173,18 +184,18 @@ impl<T: Send + Sync> RWLock<T> {
|
|||||||
/// guarantees with respect to the ordering of whether contentious readers
|
/// guarantees with respect to the ordering of whether contentious readers
|
||||||
/// or writers will acquire the lock first.
|
/// or writers will acquire the lock first.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// This function will panic if the RWLock is poisoned. An RWLock is
|
/// This function will return an error if the RWLock is poisoned. An RWLock
|
||||||
/// poisoned whenever a writer panics while holding an exclusive lock. A
|
/// is poisoned whenever a writer panics while holding an exclusive lock. An
|
||||||
/// panic will only occur if the lock is acquired.
|
/// error will only be returned if the lock would have otherwise been
|
||||||
|
/// acquired.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_read(&self) -> Option<RWLockReadGuard<T>> {
|
pub fn try_read(&self) -> TryLockResult<RWLockReadGuard<T>> {
|
||||||
unsafe {
|
if unsafe { self.inner.lock.try_read() } {
|
||||||
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
|
Ok(try!(RWLockReadGuard::new(self)))
|
||||||
lock.try_read().map(|guard| {
|
} else {
|
||||||
RWLockReadGuard::new(self, guard)
|
Err(TryLockError::WouldBlock)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,17 +208,15 @@ impl<T: Send + Sync> RWLock<T> {
|
|||||||
/// Returns an RAII guard which will drop the write access of this rwlock
|
/// Returns an RAII guard which will drop the write access of this rwlock
|
||||||
/// when dropped.
|
/// when dropped.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// This function will panic if the RWLock is poisoned. An RWLock is
|
/// This function will return an error if the RWLock is poisoned. An RWLock
|
||||||
/// poisoned whenever a writer panics while holding an exclusive lock. The
|
/// is poisoned whenever a writer panics while holding an exclusive lock.
|
||||||
/// panic will occur when the lock is acquired.
|
/// An error will be returned when the lock is acquired.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn write(&self) -> RWLockWriteGuard<T> {
|
pub fn write(&self) -> LockResult<RWLockWriteGuard<T>> {
|
||||||
unsafe {
|
unsafe { self.inner.lock.write() }
|
||||||
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
|
RWLockWriteGuard::new(self)
|
||||||
RWLockWriteGuard::new(self, lock.write())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to lock this rwlock with exclusive write access.
|
/// Attempt to lock this rwlock with exclusive write access.
|
||||||
@ -216,18 +225,18 @@ impl<T: Send + Sync> RWLock<T> {
|
|||||||
/// to `write` would otherwise block. If successful, an RAII guard is
|
/// to `write` would otherwise block. If successful, an RAII guard is
|
||||||
/// returned.
|
/// returned.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// This function will panic if the RWLock is poisoned. An RWLock is
|
/// This function will return an error if the RWLock is poisoned. An RWLock
|
||||||
/// poisoned whenever a writer panics while holding an exclusive lock. A
|
/// is poisoned whenever a writer panics while holding an exclusive lock. An
|
||||||
/// panic will only occur if the lock is acquired.
|
/// error will only be returned if the lock would have otherwise been
|
||||||
|
/// acquired.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_write(&self) -> Option<RWLockWriteGuard<T>> {
|
pub fn try_write(&self) -> TryLockResult<RWLockWriteGuard<T>> {
|
||||||
unsafe {
|
if unsafe { self.inner.lock.try_read() } {
|
||||||
let lock: &'static StaticRWLock = &*(&*self.inner as *const _);
|
Ok(try!(RWLockWriteGuard::new(self)))
|
||||||
lock.try_write().map(|guard| {
|
} else {
|
||||||
RWLockWriteGuard::new(self, guard)
|
Err(TryLockError::WouldBlock)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,7 +244,7 @@ impl<T: Send + Sync> RWLock<T> {
|
|||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
impl<T> Drop for RWLock<T> {
|
impl<T> Drop for RWLock<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { self.inner.inner.destroy() }
|
unsafe { self.inner.lock.destroy() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,8 +254,8 @@ impl StaticRWLock {
|
|||||||
///
|
///
|
||||||
/// See `RWLock::read`.
|
/// See `RWLock::read`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read(&'static self) -> StaticRWLockReadGuard {
|
pub fn read(&'static self) -> LockResult<StaticRWLockReadGuard> {
|
||||||
unsafe { self.inner.read() }
|
unsafe { self.lock.read() }
|
||||||
StaticRWLockReadGuard::new(self)
|
StaticRWLockReadGuard::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,11 +263,11 @@ impl StaticRWLock {
|
|||||||
///
|
///
|
||||||
/// See `RWLock::try_read`.
|
/// See `RWLock::try_read`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_read(&'static self) -> Option<StaticRWLockReadGuard> {
|
pub fn try_read(&'static self) -> TryLockResult<StaticRWLockReadGuard> {
|
||||||
if unsafe { self.inner.try_read() } {
|
if unsafe { self.lock.try_read() } {
|
||||||
Some(StaticRWLockReadGuard::new(self))
|
Ok(try!(StaticRWLockReadGuard::new(self)))
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(TryLockError::WouldBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,8 +276,8 @@ impl StaticRWLock {
|
|||||||
///
|
///
|
||||||
/// See `RWLock::write`.
|
/// See `RWLock::write`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn write(&'static self) -> StaticRWLockWriteGuard {
|
pub fn write(&'static self) -> LockResult<StaticRWLockWriteGuard> {
|
||||||
unsafe { self.inner.write() }
|
unsafe { self.lock.write() }
|
||||||
StaticRWLockWriteGuard::new(self)
|
StaticRWLockWriteGuard::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,11 +285,11 @@ impl StaticRWLock {
|
|||||||
///
|
///
|
||||||
/// See `RWLock::try_write`.
|
/// See `RWLock::try_write`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_write(&'static self) -> Option<StaticRWLockWriteGuard> {
|
pub fn try_write(&'static self) -> TryLockResult<StaticRWLockWriteGuard> {
|
||||||
if unsafe { self.inner.try_write() } {
|
if unsafe { self.lock.try_write() } {
|
||||||
Some(StaticRWLockWriteGuard::new(self))
|
Ok(try!(StaticRWLockWriteGuard::new(self)))
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(TryLockError::WouldBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,69 +300,92 @@ impl StaticRWLock {
|
|||||||
/// of this lock. This method is required to be called to not leak memory on
|
/// of this lock. This method is required to be called to not leak memory on
|
||||||
/// all platforms.
|
/// all platforms.
|
||||||
pub unsafe fn destroy(&'static self) {
|
pub unsafe fn destroy(&'static self) {
|
||||||
self.inner.destroy()
|
self.lock.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'rwlock, T> RWLockReadGuard<'rwlock, T> {
|
impl<'rwlock, T> RWLockReadGuard<'rwlock, T> {
|
||||||
fn new(lock: &RWLock<T>, guard: StaticRWLockReadGuard)
|
fn new(lock: &RWLock<T>) -> LockResult<RWLockReadGuard<T>> {
|
||||||
-> RWLockReadGuard<T> {
|
poison::map_result(ReadGuard::new(lock), |guard| {
|
||||||
RWLockReadGuard { __lock: lock, __guard: guard }
|
RWLockReadGuard { __inner: guard }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'rwlock, T> RWLockWriteGuard<'rwlock, T> {
|
impl<'rwlock, T> RWLockWriteGuard<'rwlock, T> {
|
||||||
fn new(lock: &RWLock<T>, guard: StaticRWLockWriteGuard)
|
fn new(lock: &RWLock<T>) -> LockResult<RWLockWriteGuard<T>> {
|
||||||
-> RWLockWriteGuard<T> {
|
poison::map_result(WriteGuard::new(lock), |guard| {
|
||||||
RWLockWriteGuard { __lock: lock, __guard: guard }
|
RWLockWriteGuard { __inner: guard }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'rwlock, T> Deref<T> for RWLockReadGuard<'rwlock, T> {
|
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> {
|
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> {
|
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 {
|
impl StaticRWLockReadGuard {
|
||||||
fn new(lock: &'static StaticRWLock) -> StaticRWLockReadGuard {
|
#[inline]
|
||||||
let guard = StaticRWLockReadGuard {
|
fn new(lock: &'static StaticRWLock) -> LockResult<StaticRWLockReadGuard> {
|
||||||
lock: &lock.inner,
|
poison::map_result(ReadGuard::new(lock), |guard| {
|
||||||
marker: marker::NoSend,
|
StaticRWLockReadGuard { _inner: guard }
|
||||||
};
|
})
|
||||||
unsafe { (*lock.poison.get()).borrow().check("rwlock"); }
|
|
||||||
return guard;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl StaticRWLockWriteGuard {
|
impl StaticRWLockWriteGuard {
|
||||||
fn new(lock: &'static StaticRWLock) -> StaticRWLockWriteGuard {
|
#[inline]
|
||||||
unsafe {
|
fn new(lock: &'static StaticRWLock) -> LockResult<StaticRWLockWriteGuard> {
|
||||||
let guard = StaticRWLockWriteGuard {
|
poison::map_result(WriteGuard::new(lock), |guard| {
|
||||||
lock: &lock.inner,
|
StaticRWLockWriteGuard { _inner: guard }
|
||||||
marker: marker::NoSend,
|
})
|
||||||
poison: (*lock.poison.get()).borrow(),
|
}
|
||||||
};
|
}
|
||||||
guard.poison.check("rwlock");
|
|
||||||
return 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]
|
#[unsafe_destructor]
|
||||||
impl Drop for StaticRWLockReadGuard {
|
impl<'a, T: AsStaticRWLock> Drop for ReadGuard<'a, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { self.lock.read_unlock(); }
|
unsafe { self.inner.as_static_rwlock().lock.read_unlock(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
impl Drop for StaticRWLockWriteGuard {
|
impl<'a, T: AsStaticRWLock> Drop for WriteGuard<'a, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.poison.done();
|
let inner = self.inner.as_static_rwlock();
|
||||||
unsafe { self.lock.write_unlock(); }
|
inner.poison.done(&self.poison);
|
||||||
|
unsafe { inner.lock.write_unlock(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,19 +400,19 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn smoke() {
|
fn smoke() {
|
||||||
let l = RWLock::new(());
|
let l = RWLock::new(());
|
||||||
drop(l.read());
|
drop(l.read().unwrap());
|
||||||
drop(l.write());
|
drop(l.write().unwrap());
|
||||||
drop((l.read(), l.read()));
|
drop((l.read().unwrap(), l.read().unwrap()));
|
||||||
drop(l.write());
|
drop(l.write().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn static_smoke() {
|
fn static_smoke() {
|
||||||
static R: StaticRWLock = RWLOCK_INIT;
|
static R: StaticRWLock = RWLOCK_INIT;
|
||||||
drop(R.read());
|
drop(R.read().unwrap());
|
||||||
drop(R.write());
|
drop(R.write().unwrap());
|
||||||
drop((R.read(), R.read()));
|
drop((R.read().unwrap(), R.read().unwrap()));
|
||||||
drop(R.write());
|
drop(R.write().unwrap());
|
||||||
unsafe { R.destroy(); }
|
unsafe { R.destroy(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,9 +429,9 @@ mod tests {
|
|||||||
let mut rng = rand::task_rng();
|
let mut rng = rand::task_rng();
|
||||||
for _ in range(0, M) {
|
for _ in range(0, M) {
|
||||||
if rng.gen_weighted_bool(N) {
|
if rng.gen_weighted_bool(N) {
|
||||||
drop(R.write());
|
drop(R.write().unwrap());
|
||||||
} else {
|
} else {
|
||||||
drop(R.read());
|
drop(R.read().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drop(tx);
|
drop(tx);
|
||||||
@ -411,51 +443,47 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_fail]
|
|
||||||
fn test_rw_arc_poison_wr() {
|
fn test_rw_arc_poison_wr() {
|
||||||
let arc = Arc::new(RWLock::new(1i));
|
let arc = Arc::new(RWLock::new(1i));
|
||||||
let arc2 = arc.clone();
|
let arc2 = arc.clone();
|
||||||
let _ = Thread::spawn(move|| {
|
let _: Result<uint, _> = Thread::spawn(move|| {
|
||||||
let lock = arc2.write();
|
let _lock = arc2.write().unwrap();
|
||||||
assert_eq!(*lock, 2);
|
panic!();
|
||||||
}).join();
|
}).join();
|
||||||
let lock = arc.read();
|
assert!(arc.read().is_err());
|
||||||
assert_eq!(*lock, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_fail]
|
|
||||||
fn test_rw_arc_poison_ww() {
|
fn test_rw_arc_poison_ww() {
|
||||||
let arc = Arc::new(RWLock::new(1i));
|
let arc = Arc::new(RWLock::new(1i));
|
||||||
let arc2 = arc.clone();
|
let arc2 = arc.clone();
|
||||||
let _ = Thread::spawn(move|| {
|
let _: Result<uint, _> = Thread::spawn(move|| {
|
||||||
let lock = arc2.write();
|
let _lock = arc2.write().unwrap();
|
||||||
assert_eq!(*lock, 2);
|
panic!();
|
||||||
}).join();
|
}).join();
|
||||||
let lock = arc.write();
|
assert!(arc.write().is_err());
|
||||||
assert_eq!(*lock, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rw_arc_no_poison_rr() {
|
fn test_rw_arc_no_poison_rr() {
|
||||||
let arc = Arc::new(RWLock::new(1i));
|
let arc = Arc::new(RWLock::new(1i));
|
||||||
let arc2 = arc.clone();
|
let arc2 = arc.clone();
|
||||||
let _ = Thread::spawn(move|| {
|
let _: Result<uint, _> = Thread::spawn(move|| {
|
||||||
let lock = arc2.read();
|
let _lock = arc2.read().unwrap();
|
||||||
assert_eq!(*lock, 2);
|
panic!();
|
||||||
}).join();
|
}).join();
|
||||||
let lock = arc.read();
|
let lock = arc.read().unwrap();
|
||||||
assert_eq!(*lock, 1);
|
assert_eq!(*lock, 1);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rw_arc_no_poison_rw() {
|
fn test_rw_arc_no_poison_rw() {
|
||||||
let arc = Arc::new(RWLock::new(1i));
|
let arc = Arc::new(RWLock::new(1i));
|
||||||
let arc2 = arc.clone();
|
let arc2 = arc.clone();
|
||||||
let _ = Thread::spawn(move|| {
|
let _: Result<uint, _> = Thread::spawn(move|| {
|
||||||
let lock = arc2.read();
|
let _lock = arc2.read().unwrap();
|
||||||
assert_eq!(*lock, 2);
|
panic!()
|
||||||
}).join();
|
}).join();
|
||||||
let lock = arc.write();
|
let lock = arc.write().unwrap();
|
||||||
assert_eq!(*lock, 1);
|
assert_eq!(*lock, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +494,7 @@ mod tests {
|
|||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
Thread::spawn(move|| {
|
Thread::spawn(move|| {
|
||||||
let mut lock = arc2.write();
|
let mut lock = arc2.write().unwrap();
|
||||||
for _ in range(0u, 10) {
|
for _ in range(0u, 10) {
|
||||||
let tmp = *lock;
|
let tmp = *lock;
|
||||||
*lock = -1;
|
*lock = -1;
|
||||||
@ -481,7 +509,7 @@ mod tests {
|
|||||||
for _ in range(0u, 5) {
|
for _ in range(0u, 5) {
|
||||||
let arc3 = arc.clone();
|
let arc3 = arc.clone();
|
||||||
children.push(Thread::spawn(move|| {
|
children.push(Thread::spawn(move|| {
|
||||||
let lock = arc3.read();
|
let lock = arc3.read().unwrap();
|
||||||
assert!(*lock >= 0);
|
assert!(*lock >= 0);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -493,7 +521,7 @@ mod tests {
|
|||||||
|
|
||||||
// Wait for writer to finish
|
// Wait for writer to finish
|
||||||
rx.recv();
|
rx.recv();
|
||||||
let lock = arc.read();
|
let lock = arc.read().unwrap();
|
||||||
assert_eq!(*lock, 10);
|
assert_eq!(*lock, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,14 +535,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
impl Drop for Unwinder {
|
impl Drop for Unwinder {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut lock = self.i.write();
|
let mut lock = self.i.write().unwrap();
|
||||||
*lock += 1;
|
*lock += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _u = Unwinder { i: arc2 };
|
let _u = Unwinder { i: arc2 };
|
||||||
panic!();
|
panic!();
|
||||||
}).join();
|
}).join();
|
||||||
let lock = arc.read();
|
let lock = arc.read().unwrap();
|
||||||
assert_eq!(*lock, 2);
|
assert_eq!(*lock, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,9 +68,9 @@ impl Semaphore {
|
|||||||
/// This method will block until the internal count of the semaphore is at
|
/// This method will block until the internal count of the semaphore is at
|
||||||
/// least 1.
|
/// least 1.
|
||||||
pub fn acquire(&self) {
|
pub fn acquire(&self) {
|
||||||
let mut count = self.lock.lock();
|
let mut count = self.lock.lock().unwrap();
|
||||||
while *count <= 0 {
|
while *count <= 0 {
|
||||||
self.cvar.wait(&count);
|
count = self.cvar.wait(count).unwrap();
|
||||||
}
|
}
|
||||||
*count -= 1;
|
*count -= 1;
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ impl Semaphore {
|
|||||||
/// This will increment the number of resources in this semaphore by 1 and
|
/// This will increment the number of resources in this semaphore by 1 and
|
||||||
/// will notify any pending waiters in `acquire` or `access` if necessary.
|
/// will notify any pending waiters in `acquire` or `access` if necessary.
|
||||||
pub fn release(&self) {
|
pub fn release(&self) {
|
||||||
*self.lock.lock() += 1;
|
*self.lock.lock().unwrap() += 1;
|
||||||
self.cvar.notify_one();
|
self.cvar.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ fn spawn_in_pool(jobs: Arc<Mutex<Receiver<Thunk>>>) {
|
|||||||
let message = {
|
let message = {
|
||||||
// Only lock jobs for the time it takes
|
// Only lock jobs for the time it takes
|
||||||
// to get a job, not run it.
|
// to get a job, not run it.
|
||||||
let lock = jobs.lock();
|
let lock = jobs.lock().unwrap();
|
||||||
lock.recv_opt()
|
lock.recv_opt()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ impl<M: Send> Helper<M> {
|
|||||||
F: FnOnce() -> T,
|
F: FnOnce() -> T,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let _guard = self.lock.lock();
|
let _guard = self.lock.lock().unwrap();
|
||||||
if !*self.initialized.get() {
|
if !*self.initialized.get() {
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
*self.chan.get() = mem::transmute(box tx);
|
*self.chan.get() = mem::transmute(box tx);
|
||||||
@ -95,7 +95,7 @@ impl<M: Send> Helper<M> {
|
|||||||
let t = f();
|
let t = f();
|
||||||
Thread::spawn(move |:| {
|
Thread::spawn(move |:| {
|
||||||
helper(receive.0, rx, t);
|
helper(receive.0, rx, t);
|
||||||
let _g = self.lock.lock();
|
let _g = self.lock.lock().unwrap();
|
||||||
*self.shutdown.get() = true;
|
*self.shutdown.get() = true;
|
||||||
self.cond.notify_one()
|
self.cond.notify_one()
|
||||||
}).detach();
|
}).detach();
|
||||||
@ -111,7 +111,7 @@ impl<M: Send> Helper<M> {
|
|||||||
/// This is only valid if the worker thread has previously booted
|
/// This is only valid if the worker thread has previously booted
|
||||||
pub fn send(&'static self, msg: M) {
|
pub fn send(&'static self, msg: M) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _guard = self.lock.lock();
|
let _guard = self.lock.lock().unwrap();
|
||||||
|
|
||||||
// Must send and *then* signal to ensure that the child receives the
|
// Must send and *then* signal to ensure that the child receives the
|
||||||
// message. Otherwise it could wake up and go to sleep before we
|
// message. Otherwise it could wake up and go to sleep before we
|
||||||
@ -127,7 +127,7 @@ impl<M: Send> Helper<M> {
|
|||||||
// Shut down, but make sure this is done inside our lock to ensure
|
// Shut down, but make sure this is done inside our lock to ensure
|
||||||
// that we'll always receive the exit signal when the thread
|
// that we'll always receive the exit signal when the thread
|
||||||
// returns.
|
// returns.
|
||||||
let guard = self.lock.lock();
|
let mut guard = self.lock.lock().unwrap();
|
||||||
|
|
||||||
// Close the channel by destroying it
|
// Close the channel by destroying it
|
||||||
let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
|
let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
|
||||||
@ -137,7 +137,7 @@ impl<M: Send> Helper<M> {
|
|||||||
|
|
||||||
// Wait for the child to exit
|
// Wait for the child to exit
|
||||||
while !*self.shutdown.get() {
|
while !*self.shutdown.get() {
|
||||||
self.cond.wait(&guard);
|
guard = self.cond.wait(guard).unwrap();
|
||||||
}
|
}
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
|
@ -334,6 +334,7 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the current thread is panicking.
|
/// Determines whether the current thread is panicking.
|
||||||
|
#[inline]
|
||||||
pub fn panicking() -> bool {
|
pub fn panicking() -> bool {
|
||||||
unwind::panicking()
|
unwind::panicking()
|
||||||
}
|
}
|
||||||
@ -349,9 +350,9 @@ impl Thread {
|
|||||||
// or futuxes, and in either case may allow spurious wakeups.
|
// or futuxes, and in either case may allow spurious wakeups.
|
||||||
pub fn park() {
|
pub fn park() {
|
||||||
let thread = Thread::current();
|
let thread = Thread::current();
|
||||||
let mut guard = thread.inner.lock.lock();
|
let mut guard = thread.inner.lock.lock().unwrap();
|
||||||
while !*guard {
|
while !*guard {
|
||||||
thread.inner.cvar.wait(&guard);
|
guard = thread.inner.cvar.wait(guard).unwrap();
|
||||||
}
|
}
|
||||||
*guard = false;
|
*guard = false;
|
||||||
}
|
}
|
||||||
@ -360,7 +361,7 @@ impl Thread {
|
|||||||
///
|
///
|
||||||
/// See the module doc for more detail.
|
/// See the module doc for more detail.
|
||||||
pub fn unpark(&self) {
|
pub fn unpark(&self) {
|
||||||
let mut guard = self.inner.lock.lock();
|
let mut guard = self.inner.lock.lock().unwrap();
|
||||||
if !*guard {
|
if !*guard {
|
||||||
*guard = true;
|
*guard = true;
|
||||||
self.inner.cvar.notify_one();
|
self.inner.cvar.notify_one();
|
||||||
|
@ -240,13 +240,18 @@ impl<T: 'static> Key<T> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let slot = slot.get().expect("cannot access a TLS value during or \
|
let slot = slot.get().expect("cannot access a TLS value during or \
|
||||||
after it is destroyed");
|
after it is destroyed");
|
||||||
if (*slot.get()).is_none() {
|
f(match *slot.get() {
|
||||||
*slot.get() = Some((self.init)());
|
Some(ref inner) => inner,
|
||||||
}
|
None => self.init(slot),
|
||||||
f((*slot.get()).as_ref().unwrap())
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
/// Test this TLS key to determine whether its value has been destroyed for
|
||||||
/// the current thread or not.
|
/// the current thread or not.
|
||||||
///
|
///
|
||||||
|
@ -28,15 +28,15 @@ type pipe = Arc<(Mutex<Vec<uint>>, Condvar)>;
|
|||||||
|
|
||||||
fn send(p: &pipe, msg: uint) {
|
fn send(p: &pipe, msg: uint) {
|
||||||
let &(ref lock, ref cond) = &**p;
|
let &(ref lock, ref cond) = &**p;
|
||||||
let mut arr = lock.lock();
|
let mut arr = lock.lock().unwrap();
|
||||||
arr.push(msg);
|
arr.push(msg);
|
||||||
cond.notify_one();
|
cond.notify_one();
|
||||||
}
|
}
|
||||||
fn recv(p: &pipe) -> uint {
|
fn recv(p: &pipe) -> uint {
|
||||||
let &(ref lock, ref cond) = &**p;
|
let &(ref lock, ref cond) = &**p;
|
||||||
let mut arr = lock.lock();
|
let mut arr = lock.lock().unwrap();
|
||||||
while arr.is_empty() {
|
while arr.is_empty() {
|
||||||
cond.wait(&arr);
|
arr = cond.wait(arr).unwrap();
|
||||||
}
|
}
|
||||||
arr.pop().unwrap()
|
arr.pop().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,11 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
unsafe {
|
let x = Some(Mutex::new(true));
|
||||||
let x = Some(Mutex::new(true));
|
match x {
|
||||||
match x {
|
Some(ref z) if *z.lock().unwrap() => {
|
||||||
Some(ref z) if *z.lock() => {
|
assert!(*z.lock().unwrap());
|
||||||
assert!(*z.lock());
|
},
|
||||||
},
|
_ => panic!()
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,11 @@ struct Point {x: int, y: int, z: int}
|
|||||||
fn f(p: &mut Point) { p.z = 13; }
|
fn f(p: &mut Point) { p.z = 13; }
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
unsafe {
|
let x = Some(Mutex::new(true));
|
||||||
let x = Some(Mutex::new(true));
|
match x {
|
||||||
match x {
|
Some(ref z) if *z.lock().unwrap() => {
|
||||||
Some(ref z) if *z.lock() => {
|
assert!(*z.lock().unwrap());
|
||||||
assert!(*z.lock());
|
},
|
||||||
},
|
_ => panic!()
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user