std: add an RAII unlocker to Mutex.

This automatically unlocks its lock when it goes out of scope, and
provides a safe(ish) method to call .wait.
This commit is contained in:
Huon Wilson 2014-02-13 17:17:50 +11:00
parent fba32ea79f
commit 76a59fd6e2
13 changed files with 134 additions and 94 deletions

View file

@ -157,7 +157,7 @@ pub mod dl {
unsafe {
// dlerror isn't thread safe, so we need to lock around this entire
// sequence
lock.lock();
let _guard = lock.lock();
let _old_error = dlerror();
let result = f();
@ -168,7 +168,7 @@ pub mod dl {
} else {
Err(str::raw::from_c_str(last_error))
};
lock.unlock();
ret
}
}

View file

@ -47,10 +47,24 @@
#[allow(non_camel_case_types)];
use option::{Option, None, Some};
use ops::Drop;
pub struct Mutex {
priv inner: imp::Mutex,
}
/// Automatically unlocks the mutex that it was created from on
/// destruction.
///
/// Using this makes lock-based code resilient to unwinding/task
/// failure, because the lock will be automatically unlocked even
/// then.
#[must_use]
pub struct LockGuard<'a> {
priv lock: &'a mut Mutex
}
pub static MUTEX_INIT: Mutex = Mutex {
inner: imp::MUTEX_INIT,
};
@ -63,23 +77,62 @@ impl Mutex {
/// Acquires this lock. This assumes that the current thread does not
/// already hold the lock.
pub unsafe fn lock(&mut self) { self.inner.lock() }
///
/// # Example
/// ```
/// use std::unstable::mutex::Mutex;
/// unsafe {
/// let mut lock = Mutex::new();
///
/// {
/// let _guard = lock.lock();
/// // critical section...
/// } // automatically unlocked in `_guard`'s destructor
/// }
/// ```
pub unsafe fn lock<'a>(&'a mut self) -> LockGuard<'a> {
self.inner.lock();
/// Attempts to acquire the lock. The value returned is whether the lock was
/// acquired or not
pub unsafe fn trylock(&mut self) -> bool { self.inner.trylock() }
LockGuard { lock: self }
}
/// Attempts to acquire the lock. The value returned is `Some` if
/// the attempt succeeded.
pub unsafe fn trylock<'a>(&'a mut self) -> Option<LockGuard<'a>> {
if self.inner.trylock() {
Some(LockGuard { lock: self })
} else {
None
}
}
/// Acquire the lock without creating a `LockGuard`.
///
/// Prefer using `.lock`.
pub unsafe fn lock_noguard(&mut self) { self.inner.lock() }
/// Attempts to acquire the lock without creating a
/// `LockGuard`. The value returned is whether the lock was
/// acquired or not.
///
/// Prefer using `.trylock`.
pub unsafe fn trylock_noguard(&mut self) -> bool {
self.inner.trylock()
}
/// Unlocks the lock. This assumes that the current thread already holds the
/// lock.
pub unsafe fn unlock(&mut self) { self.inner.unlock() }
pub unsafe fn unlock_noguard(&mut self) { self.inner.unlock() }
/// Block on the internal condition variable.
///
/// This function assumes that the lock is already held
pub unsafe fn wait(&mut self) { self.inner.wait() }
/// This function assumes that the lock is already held. Prefer
/// using `LockGuard.wait` since that guarantees that the lock is
/// held.
pub unsafe fn wait_noguard(&mut self) { self.inner.wait() }
/// Signals a thread in `wait` to wake up
pub unsafe fn signal(&mut self) { self.inner.signal() }
pub unsafe fn signal_noguard(&mut self) { self.inner.signal() }
/// This function is especially unsafe because there are no guarantees made
/// that no other thread is currently holding the lock or waiting on the
@ -87,6 +140,25 @@ impl Mutex {
pub unsafe fn destroy(&mut self) { self.inner.destroy() }
}
impl<'a> LockGuard<'a> {
/// Block on the internal condition variable.
pub unsafe fn wait(&mut self) {
self.lock.wait_noguard()
}
/// Signals a thread in `wait` to wake up.
pub unsafe fn signal(&mut self) {
self.lock.signal_noguard()
}
}
#[unsafe_destructor]
impl<'a> Drop for LockGuard<'a> {
fn drop(&mut self) {
unsafe {self.lock.unlock_noguard()}
}
}
#[cfg(unix)]
mod imp {
use libc;
@ -382,6 +454,7 @@ mod imp {
mod test {
use prelude::*;
use mem::drop;
use super::{Mutex, MUTEX_INIT};
use rt::thread::Thread;
@ -389,8 +462,7 @@ mod test {
fn somke_lock() {
static mut lock: Mutex = MUTEX_INIT;
unsafe {
lock.lock();
lock.unlock();
let _guard = lock.lock();
}
}
@ -398,14 +470,14 @@ mod test {
fn somke_cond() {
static mut lock: Mutex = MUTEX_INIT;
unsafe {
lock.lock();
let mut guard = lock.lock();
let t = Thread::start(proc() {
lock.lock();
lock.signal();
lock.unlock();
let mut guard = lock.lock();
guard.signal();
});
lock.wait();
lock.unlock();
guard.wait();
drop(guard);
t.join();
}
}

View file

@ -11,16 +11,16 @@
use clone::Clone;
use kinds::Send;
use ops::Drop;
use option::{Option,Some,None};
use option::Option;
use sync::arc::UnsafeArc;
use unstable::mutex::Mutex;
use unstable::mutex::{Mutex, LockGuard};
pub struct LittleLock {
priv l: Mutex,
}
pub struct LittleGuard<'a> {
priv l: &'a mut Mutex,
priv l: LockGuard<'a>
}
impl Drop for LittleLock {
@ -29,33 +29,21 @@ impl Drop for LittleLock {
}
}
#[unsafe_destructor]
impl<'a> Drop for LittleGuard<'a> {
fn drop(&mut self) {
unsafe { self.l.unlock(); }
}
}
impl LittleLock {
pub fn new() -> LittleLock {
unsafe { LittleLock { l: Mutex::new() } }
}
pub unsafe fn lock<'a>(&'a mut self) -> LittleGuard<'a> {
self.l.lock();
LittleGuard { l: &mut self.l }
LittleGuard { l: self.l.lock() }
}
pub unsafe fn try_lock<'a>(&'a mut self) -> Option<LittleGuard<'a>> {
if self.l.trylock() {
Some(LittleGuard { l: &mut self.l })
} else {
None
}
self.l.trylock().map(|guard| LittleGuard { l: guard })
}
pub unsafe fn signal(&mut self) {
self.l.signal();
self.l.signal_noguard();
}
}