diff options
| author | bors <bors@rust-lang.org> | 2014-02-15 15:21:28 -0800 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-02-15 15:21:28 -0800 |
| commit | d98668a55996d656072a4cac7abb1dcbbdf4f48b (patch) | |
| tree | 9480a773995df199411254f148e321b64cd24556 /src/libstd | |
| parent | 6b025c803c72d610c2ad4c950151b0d23782d114 (diff) | |
| parent | 4668cdf3c4788e4a67f1b7dea0eb2d661ac05a49 (diff) | |
| download | rust-d98668a55996d656072a4cac7abb1dcbbdf4f48b.tar.gz rust-d98668a55996d656072a4cac7abb1dcbbdf4f48b.zip | |
auto merge of #12235 : huonw/rust/raii-lock, r=alexcrichton
- adds a `LockGuard` type returned by `.lock` and `.trylock` that unlocks the mutex in the destructor - renames `mutex::Mutex` to `StaticNativeMutex` - adds a `NativeMutex` type with a destructor - removes `LittleLock` - adds `#[must_use]` to `sync::mutex::Guard` to remind people to use it
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/comm/shared.rs | 14 | ||||
| -rw-r--r-- | src/libstd/os.rs | 12 | ||||
| -rw-r--r-- | src/libstd/rt/args.rs | 19 | ||||
| -rw-r--r-- | src/libstd/unstable/dynamic_lib.rs | 8 | ||||
| -rw-r--r-- | src/libstd/unstable/mutex.rs | 303 | ||||
| -rw-r--r-- | src/libstd/unstable/sync.rs | 66 |
6 files changed, 276 insertions, 146 deletions
diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs index 77bf2d7a68d..031ce991ba4 100644 --- a/src/libstd/comm/shared.rs +++ b/src/libstd/comm/shared.rs @@ -28,7 +28,7 @@ use rt::local::Local; use rt::task::{Task, BlockedTask}; use rt::thread::Thread; use sync::atomics; -use unstable::mutex::Mutex; +use unstable::mutex::NativeMutex; use vec::OwnedVector; use mpsc = sync::mpsc_queue; @@ -53,7 +53,7 @@ pub struct Packet<T> { // this lock protects various portions of this implementation during // select() - select_lock: Mutex, + select_lock: NativeMutex, } pub enum Failure { @@ -72,10 +72,10 @@ impl<T: Send> Packet<T> { channels: atomics::AtomicInt::new(2), port_dropped: atomics::AtomicBool::new(false), sender_drain: atomics::AtomicInt::new(0), - select_lock: unsafe { Mutex::new() }, + select_lock: unsafe { NativeMutex::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 @@ -487,7 +486,6 @@ impl<T: Send> Drop for Packet<T> { assert_eq!(self.cnt.load(atomics::SeqCst), DISCONNECTED); assert_eq!(self.to_wake.load(atomics::SeqCst), 0); assert_eq!(self.channels.load(atomics::SeqCst), 0); - self.select_lock.destroy(); } } } diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 20d1ae2f3e2..719ed62d03d 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -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; @@ -145,16 +144,13 @@ Accessing environment variables is not generally threadsafe. Serialize access through a global lock. */ fn with_env_lock<T>(f: || -> T) -> T { - use unstable::mutex::{Mutex, MUTEX_INIT}; - use unstable::finally::Finally; + use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - static mut lock: Mutex = MUTEX_INIT; + static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT; unsafe { - return (|| { - lock.lock(); - f() - }).finally(|| lock.unlock()); + let _guard = lock.lock(); + f() } } diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index c417ea375fd..6f73265978b 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -68,12 +68,11 @@ mod imp { use option::{Option, Some, None}; use ptr::RawPtr; use iter::Iterator; - use unstable::finally::Finally; - use unstable::mutex::{Mutex, MUTEX_INIT}; + use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; use mem; static mut global_args_ptr: uint = 0; - static mut lock: Mutex = MUTEX_INIT; + static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT; #[cfg(not(test))] pub unsafe fn init(argc: int, argv: **u8) { @@ -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]]> { diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs index 8529b69c6eb..84fa528ebf1 100644 --- a/src/libstd/unstable/dynamic_lib.rs +++ b/src/libstd/unstable/dynamic_lib.rs @@ -152,12 +152,12 @@ pub mod dl { } pub fn check_for_errors_in<T>(f: || -> T) -> Result<T, ~str> { - use unstable::mutex::{Mutex, MUTEX_INIT}; - static mut lock: Mutex = MUTEX_INIT; + use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT; 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 } } diff --git a/src/libstd/unstable/mutex.rs b/src/libstd/unstable/mutex.rs index 3122e925e82..34ddee46d35 100644 --- a/src/libstd/unstable/mutex.rs +++ b/src/libstd/unstable/mutex.rs @@ -8,78 +8,157 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A native mutex and condition variable type +//! A native mutex and condition variable type. //! //! This module contains bindings to the platform's native mutex/condition -//! variable primitives. It provides a single type, `Mutex`, which can be -//! statically initialized via the `MUTEX_INIT` value. This object serves as both a -//! mutex and a condition variable simultaneously. +//! variable primitives. It provides two types: `StaticNativeMutex`, which can +//! be statically initialized via the `NATIVE_MUTEX_INIT` value, and a simple +//! wrapper `NativeMutex` that has a destructor to clean up after itself. These +//! objects serve as both mutexes and condition variables simultaneously. //! -//! The lock is lazily initialized, but it can only be unsafely destroyed. A -//! statically initialized lock doesn't necessarily have a time at which it can -//! get deallocated. For this reason, there is no `Drop` implementation of the -//! mutex, but rather the `destroy()` method must be invoked manually if -//! destruction of the mutex is desired. +//! The static lock is lazily initialized, but it can only be unsafely +//! destroyed. A statically initialized lock doesn't necessarily have a time at +//! which it can get deallocated. For this reason, there is no `Drop` +//! implementation of the static mutex, but rather the `destroy()` method must +//! be invoked manually if destruction of the mutex is desired. //! -//! It is not recommended to use this type for idiomatic rust use. This type is -//! appropriate where no other options are available, but other rust concurrency -//! primitives should be used before this type. +//! The non-static `NativeMutex` type does have a destructor, but cannot be +//! statically initialized. +//! +//! It is not recommended to use this type for idiomatic rust use. These types +//! are appropriate where no other options are available, but other rust +//! concurrency primitives should be used before them: the `sync` crate defines +//! `StaticMutex` and `Mutex` types. //! //! # Example //! -//! use std::unstable::mutex::{Mutex, MUTEX_INIT}; +//! ```rust +//! use std::unstable::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT}; +//! +//! // Use a statically initialized mutex +//! static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; +//! +//! unsafe { +//! let _guard = LOCK.lock(); +//! } // automatically unlocked here //! -//! // Use a statically initialized mutex -//! static mut lock: Mutex = MUTEX_INIT; +//! // Use a normally initialized mutex +//! unsafe { +//! let mut lock = NativeMutex::new(); //! -//! unsafe { -//! lock.lock(); -//! lock.unlock(); -//! } +//! { +//! let _guard = lock.lock(); +//! } // unlocked here //! -//! // Use a normally initialized mutex -//! let mut lock = Mutex::new(); -//! unsafe { -//! lock.lock(); -//! lock.unlock(); -//! lock.destroy(); -//! } +//! // sometimes the RAII guard isn't appropriate +//! lock.lock_noguard(); +//! lock.unlock_noguard(); +//! } // `lock` is deallocated here +//! ``` #[allow(non_camel_case_types)]; -pub struct Mutex { +use option::{Option, None, Some}; +use ops::Drop; + +/// A native mutex suitable for storing in statics (that is, it has +/// the `destroy` method rather than a destructor). +/// +/// Prefer the `NativeMutex` type where possible, since that does not +/// require manual deallocation. +pub struct StaticNativeMutex { priv inner: imp::Mutex, } -pub static MUTEX_INIT: Mutex = Mutex { +/// A native mutex with a destructor for clean-up. +/// +/// See `StaticNativeMutex` for a version that is suitable for storing in +/// statics. +pub struct NativeMutex { + priv inner: StaticNativeMutex +} + +/// 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 StaticNativeMutex +} + +pub static NATIVE_MUTEX_INIT: StaticNativeMutex = StaticNativeMutex { inner: imp::MUTEX_INIT, }; -impl Mutex { - /// Creates a new mutex - pub unsafe fn new() -> Mutex { - Mutex { inner: imp::Mutex::new() } +impl StaticNativeMutex { + /// Creates a new mutex. + /// + /// Note that a mutex created in this way needs to be explicit + /// freed with a call to `destroy` or it will leak. + pub unsafe fn new() -> StaticNativeMutex { + StaticNativeMutex { inner: imp::Mutex::new() } } /// 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 + /// ```rust + /// use std::unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + /// static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + /// unsafe { + /// 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(); + + 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 + } + } - /// 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() } + /// Acquire the lock without creating a `LockGuard`. + /// + /// These needs to be paired with a call to `.unlock_noguard`. 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. + /// + /// If `true` is returned, this needs to be paired with a call to + /// `.unlock_noguard`. 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 +166,96 @@ impl Mutex { pub unsafe fn destroy(&mut self) { self.inner.destroy() } } +impl NativeMutex { + /// Creates a new mutex. + /// + /// The user must be careful to ensure the mutex is not locked when its is + /// being destroyed. + pub unsafe fn new() -> NativeMutex { + NativeMutex { inner: StaticNativeMutex::new() } + } + + /// Acquires this lock. This assumes that the current thread does not + /// already hold the lock. + /// + /// # Example + /// ```rust + /// use std::unstable::mutex::NativeMutex; + /// unsafe { + /// let mut lock = NativeMutex::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 `Some` if + /// the attempt succeeded. + pub unsafe fn trylock<'a>(&'a mut self) -> Option<LockGuard<'a>> { + self.inner.trylock() + } + + /// Acquire the lock without creating a `LockGuard`. + /// + /// These needs to be paired with a call to `.unlock_noguard`. Prefer using + /// `.lock`. + pub unsafe fn lock_noguard(&mut self) { self.inner.lock_noguard() } + + /// Attempts to acquire the lock without creating a + /// `LockGuard`. The value returned is whether the lock was + /// acquired or not. + /// + /// If `true` is returned, this needs to be paired with a call to + /// `.unlock_noguard`. Prefer using `.trylock`. + pub unsafe fn trylock_noguard(&mut self) -> bool { + self.inner.trylock_noguard() + } + + /// Unlocks the lock. This assumes that the current thread already holds the + /// lock. + pub unsafe fn unlock_noguard(&mut self) { self.inner.unlock_noguard() } + + /// Block on the internal condition variable. + /// + /// 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_noguard() } + + /// Signals a thread in `wait` to wake up + pub unsafe fn signal_noguard(&mut self) { self.inner.signal_noguard() } +} + +impl Drop for NativeMutex { + fn drop(&mut self) { + unsafe {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,30 +551,56 @@ mod imp { mod test { use prelude::*; - use super::{Mutex, MUTEX_INIT}; + use mem::drop; + use super::{StaticNativeMutex, NATIVE_MUTEX_INIT}; use rt::thread::Thread; #[test] - fn somke_lock() { - static mut lock: Mutex = MUTEX_INIT; + fn smoke_lock() { + static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT; unsafe { - lock.lock(); - lock.unlock(); + let _guard = lock.lock(); } } #[test] - fn somke_cond() { - static mut lock: Mutex = MUTEX_INIT; + fn smoke_cond() { + static mut lock: StaticNativeMutex = NATIVE_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(); + } + } + + #[test] + fn smoke_lock_noguard() { + static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT; + unsafe { + lock.lock_noguard(); + lock.unlock_noguard(); + } + } + + #[test] + fn smoke_cond_noguard() { + static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT; + unsafe { + lock.lock_noguard(); + let t = Thread::start(proc() { + lock.lock_noguard(); + lock.signal_noguard(); + lock.unlock_noguard(); + }); + lock.wait_noguard(); + lock.unlock_noguard(); + t.join(); } } @@ -413,7 +608,7 @@ mod test { #[test] fn destroy_immediately() { unsafe { - let mut m = Mutex::new(); + let mut m = StaticNativeMutex::new(); m.destroy(); } } diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index c2fa168a478..93322977bc1 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -10,63 +10,11 @@ use clone::Clone; use kinds::Send; -use ops::Drop; -use option::{Option,Some,None}; use sync::arc::UnsafeArc; -use unstable::mutex::Mutex; - -pub struct LittleLock { - priv l: Mutex, -} - -pub struct LittleGuard<'a> { - priv l: &'a mut Mutex, -} - -impl Drop for LittleLock { - fn drop(&mut self) { - unsafe { self.l.destroy(); } - } -} - -#[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 } - } - - pub unsafe fn try_lock<'a>(&'a mut self) -> Option<LittleGuard<'a>> { - if self.l.trylock() { - Some(LittleGuard { l: &mut self.l }) - } else { - None - } - } - - pub unsafe fn signal(&mut self) { - self.l.signal(); - } -} - -impl<'a> LittleGuard<'a> { - pub unsafe fn wait(&mut self) { - self.l.wait(); - } -} +use unstable::mutex::NativeMutex; struct ExData<T> { - lock: LittleLock, + lock: NativeMutex, failed: bool, data: T, } @@ -95,7 +43,7 @@ impl<T:Send> Clone for Exclusive<T> { impl<T:Send> Exclusive<T> { pub fn new(user_data: T) -> Exclusive<T> { let data = ExData { - lock: LittleLock::new(), + lock: unsafe {NativeMutex::new()}, failed: false, data: user_data }; @@ -104,8 +52,8 @@ impl<T:Send> Exclusive<T> { } } - // Exactly like std::arc::MutexArc,access(), but with the LittleLock - // instead of a proper mutex. Same reason for being unsafe. + // Exactly like sync::MutexArc.access(). Same reason for being + // unsafe. // // Currently, scheduling operations (i.e., descheduling, receiving on a pipe, // accessing the provided condition variable) are prohibited while inside @@ -131,14 +79,14 @@ impl<T:Send> Exclusive<T> { #[inline] pub unsafe fn hold_and_signal(&self, f: |x: &mut T|) { let rec = self.x.get(); - let _l = (*rec).lock.lock(); + let mut guard = (*rec).lock.lock(); if (*rec).failed { fail!("Poisoned Exclusive::new - another task failed inside!"); } (*rec).failed = true; f(&mut (*rec).data); (*rec).failed = false; - (*rec).lock.signal(); + guard.signal(); } #[inline] |
