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

@ -669,8 +669,7 @@ impl Scheduler {
// is acquired here. This is the resumption points and the "bounce"
// that it is referring to.
unsafe {
current_task.nasty_deschedule_lock.lock();
current_task.nasty_deschedule_lock.unlock();
let _guard = current_task.nasty_deschedule_lock.lock();
}
return current_task;
}
@ -766,9 +765,10 @@ impl Scheduler {
// unlocked the lock so there's no worry of this memory going away.
let cur = self.change_task_context(cur, next, |sched, mut task| {
let lock: *mut Mutex = &mut task.nasty_deschedule_lock;
unsafe { (*lock).lock() }
f(sched, BlockedTask::block(task.swap()));
unsafe { (*lock).unlock() }
unsafe {
let _guard = (*lock).lock();
f(sched, BlockedTask::block(task.swap()));
}
});
cur.put();
}
@ -1466,12 +1466,11 @@ mod test {
let mut handle = pool.spawn_sched();
handle.send(PinnedTask(pool.task(TaskOpts::new(), proc() {
unsafe {
LOCK.lock();
let mut guard = LOCK.lock();
start_ch.send(());
LOCK.wait(); // block the scheduler thread
LOCK.signal(); // let them know we have the lock
LOCK.unlock();
guard.wait(); // block the scheduler thread
guard.signal(); // let them know we have the lock
}
fin_ch.send(());
@ -1503,10 +1502,9 @@ mod test {
child_ch.send(20);
pingpong(&parent_po, &child_ch);
unsafe {
LOCK.lock();
LOCK.signal(); // wakeup waiting scheduler
LOCK.wait(); // wait for them to grab the lock
LOCK.unlock();
let mut guard = LOCK.lock();
guard.signal(); // wakeup waiting scheduler
guard.wait(); // wait for them to grab the lock
}
})));
drop(handle);

View File

@ -324,9 +324,8 @@ impl GreenTask {
unsafe {
let mtx = &mut self.nasty_deschedule_lock as *mut Mutex;
let handle = self.handle.get_mut_ref() as *mut SchedHandle;
(*mtx).lock();
let _guard = (*mtx).lock();
(*handle).send(RunOnce(self));
(*mtx).unlock();
}
}
}

View File

@ -29,9 +29,8 @@ pub fn increment() {
pub fn decrement() {
unsafe {
if TASK_COUNT.fetch_sub(1, atomics::SeqCst) == 1 {
TASK_LOCK.lock();
TASK_LOCK.signal();
TASK_LOCK.unlock();
let mut guard = TASK_LOCK.lock();
guard.signal();
}
}
}
@ -40,11 +39,12 @@ pub fn decrement() {
/// the entry points of native programs
pub fn wait_for_other_tasks() {
unsafe {
TASK_LOCK.lock();
while TASK_COUNT.load(atomics::SeqCst) > 0 {
TASK_LOCK.wait();
{
let mut guard = TASK_LOCK.lock();
while TASK_COUNT.load(atomics::SeqCst) > 0 {
guard.wait();
}
}
TASK_LOCK.unlock();
TASK_LOCK.destroy();
}
}

View File

@ -222,7 +222,7 @@ pub fn init() {
static mut INITIALIZED: bool = false;
static mut LOCK: Mutex = MUTEX_INIT;
LOCK.lock();
let _guard = LOCK.lock();
if !INITIALIZED {
let mut data: WSADATA = mem::init();
let ret = WSAStartup(0x202, // version 2.2
@ -230,7 +230,6 @@ pub fn init() {
assert_eq!(ret, 0);
INITIALIZED = true;
}
LOCK.unlock();
}
}

View File

@ -41,7 +41,7 @@ pub fn boot(helper: fn(imp::signal, Port<Req>)) {
static mut INITIALIZED: bool = false;
unsafe {
LOCK.lock();
let mut _guard = LOCK.lock();
if !INITIALIZED {
let (msgp, msgc) = Chan::new();
// promote this to a shared channel
@ -58,7 +58,6 @@ pub fn boot(helper: fn(imp::signal, Port<Req>)) {
rt::at_exit(proc() { shutdown() });
INITIALIZED = true;
}
LOCK.unlock();
}
}

View File

@ -191,20 +191,19 @@ impl rt::Runtime for Ops {
let task = BlockedTask::block(cur_task);
if times == 1 {
(*me).lock.lock();
let mut guard = (*me).lock.lock();
(*me).awoken = false;
match f(task) {
Ok(()) => {
while !(*me).awoken {
(*me).lock.wait();
guard.wait();
}
}
Err(task) => { cast::forget(task.wake()); }
}
(*me).lock.unlock();
} else {
let mut iter = task.make_selectable(times);
(*me).lock.lock();
let mut guard = (*me).lock.lock();
(*me).awoken = false;
let success = iter.all(|task| {
match f(task) {
@ -216,9 +215,8 @@ impl rt::Runtime for Ops {
}
});
while success && !(*me).awoken {
(*me).lock.wait();
guard.wait();
}
(*me).lock.unlock();
}
// re-acquire ownership of the task
cur_task = cast::transmute::<uint, ~Task>(cur_task_dupe);
@ -235,10 +233,9 @@ impl rt::Runtime for Ops {
let me = &mut *self as *mut Ops;
to_wake.put_runtime(self as ~rt::Runtime);
cast::forget(to_wake);
(*me).lock.lock();
let mut guard = (*me).lock.lock();
(*me).awoken = true;
(*me).lock.signal();
(*me).lock.unlock();
guard.signal();
}
}

View File

@ -75,7 +75,7 @@ impl<T: Send> Packet<T> {
select_lock: unsafe { Mutex::new() },
};
// see comments in inherit_blocker about why we grab this lock
unsafe { p.select_lock.lock() }
unsafe { p.select_lock.lock_noguard() }
return p;
}
@ -124,7 +124,7 @@ impl<T: Send> Packet<T> {
// interfere with this method. After we unlock this lock, we're
// signifying that we're done modifying self.cnt and self.to_wake and
// the port is ready for the world to continue using it.
unsafe { self.select_lock.unlock() }
unsafe { self.select_lock.unlock_noguard() }
}
pub fn send(&mut self, t: T) -> bool {
@ -438,8 +438,7 @@ impl<T: Send> Packet<T> {
// about looking at and dealing with to_wake. Once we have acquired the
// lock, we are guaranteed that inherit_blocker is done.
unsafe {
self.select_lock.lock();
self.select_lock.unlock();
let _guard = self.select_lock.lock();
}
// Like the stream implementation, we want to make sure that the count

View File

@ -44,7 +44,6 @@ use ptr;
use str;
use str::{Str, StrSlice};
use fmt;
use unstable::finally::Finally;
use sync::atomics::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
use path::{Path, GenericPath};
use iter::Iterator;
@ -146,15 +145,12 @@ Serialize access through a global lock.
*/
fn with_env_lock<T>(f: || -> T) -> T {
use unstable::mutex::{Mutex, MUTEX_INIT};
use unstable::finally::Finally;
static mut lock: Mutex = MUTEX_INIT;
unsafe {
return (|| {
lock.lock();
f()
}).finally(|| lock.unlock());
let _guard = lock.lock();
f()
}
}

View File

@ -68,7 +68,6 @@ mod imp {
use option::{Option, Some, None};
use ptr::RawPtr;
use iter::Iterator;
use unstable::finally::Finally;
use unstable::mutex::{Mutex, MUTEX_INIT};
use mem;
@ -111,16 +110,10 @@ mod imp {
}
fn with_lock<T>(f: || -> T) -> T {
(|| {
unsafe {
lock.lock();
f()
}
}).finally(|| {
unsafe {
lock.unlock();
}
})
unsafe {
let _guard = lock.lock();
f()
}
}
fn get_global_ptr() -> *mut Option<~~[~[u8]]> {

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();
}
}

View File

@ -288,11 +288,11 @@ impl StaticMutex {
// `lock()` function on an OS mutex
fn native_lock(&mut self, t: ~Task) {
Local::put(t);
unsafe { self.lock.lock(); }
unsafe { self.lock.lock_noguard(); }
}
fn native_unlock(&mut self) {
unsafe { self.lock.unlock(); }
unsafe { self.lock.unlock_noguard(); }
}
fn green_lock(&mut self, t: ~Task) {