From 0937f659993631da3b3ddc198dd502c58d1634c6 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 15 Feb 2014 12:01:52 +1100 Subject: [PATCH] std: add a NativeMutex type as a wrapper to destroy StaticNativeMutex. This obsoletes LittleLock, and so it is removed. --- src/libgreen/simple.rs | 10 +-- src/librustuv/queue.rs | 6 +- src/libstd/unstable/mutex.rs | 118 +++++++++++++++++++++++++++++------ src/libstd/unstable/sync.rs | 54 +++------------- 4 files changed, 115 insertions(+), 73 deletions(-) diff --git a/src/libgreen/simple.rs b/src/libgreen/simple.rs index 8db95f55d18..297c22e2cd6 100644 --- a/src/libgreen/simple.rs +++ b/src/libgreen/simple.rs @@ -17,10 +17,10 @@ use std::rt::local::Local; use std::rt::rtio; use std::rt::task::{Task, BlockedTask}; use std::task::TaskOpts; -use std::unstable::sync::LittleLock; +use std::unstable::mutex::NativeMutex; struct SimpleTask { - lock: LittleLock, + lock: NativeMutex, awoken: bool, } @@ -59,9 +59,9 @@ impl Runtime for SimpleTask { to_wake.put_runtime(self as ~Runtime); unsafe { cast::forget(to_wake); - let _l = (*me).lock.lock(); + let mut guard = (*me).lock.lock(); (*me).awoken = true; - (*me).lock.signal(); + guard.signal(); } } @@ -83,7 +83,7 @@ impl Runtime for SimpleTask { pub fn task() -> ~Task { let mut task = ~Task::new(); task.put_runtime(~SimpleTask { - lock: LittleLock::new(), + lock: unsafe {NativeMutex::new()}, awoken: false, } as ~Runtime); return task; diff --git a/src/librustuv/queue.rs b/src/librustuv/queue.rs index 5b697e0d73d..da502ca72de 100644 --- a/src/librustuv/queue.rs +++ b/src/librustuv/queue.rs @@ -23,7 +23,7 @@ use std::cast; use std::libc::{c_void, c_int}; use std::rt::task::BlockedTask; -use std::unstable::sync::LittleLock; +use std::unstable::mutex::NativeMutex; use std::sync::arc::UnsafeArc; use mpsc = std::sync::mpsc_queue; @@ -39,7 +39,7 @@ enum Message { struct State { handle: *uvll::uv_async_t, - lock: LittleLock, // see comments in async_cb for why this is needed + lock: NativeMutex, // see comments in async_cb for why this is needed queue: mpsc::Queue, } @@ -112,7 +112,7 @@ impl QueuePool { let handle = UvHandle::alloc(None::, uvll::UV_ASYNC); let state = UnsafeArc::new(State { handle: handle, - lock: LittleLock::new(), + lock: unsafe {NativeMutex::new()}, queue: mpsc::Queue::new(), }); let q = ~QueuePool { diff --git a/src/libstd/unstable/mutex.rs b/src/libstd/unstable/mutex.rs index d8d051236b3..d217e90522c 100644 --- a/src/libstd/unstable/mutex.rs +++ b/src/libstd/unstable/mutex.rs @@ -8,44 +8,50 @@ // 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, `StaticNativeMutex`, which can be -//! statically initialized via the `NATIVE_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. //! //! # Example //! //! use std::unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; //! //! // Use a statically initialized mutex -//! static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT; +//! static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; //! //! unsafe { -//! let _guard = lock.lock(); +//! let _guard = LOCK.lock(); //! } // automatically unlocked here //! //! // Use a normally initialized mutex //! unsafe { -//! let mut lock = StaticNativeMutex::new(); +//! let mut lock = NativeMutex::new(); +//! +//! { +//! let _guard = lock.lock(); +//! } // unlocked here //! //! // sometimes the RAII guard isn't appropriate //! lock.lock_noguard(); //! lock.unlock_noguard(); -//! -//! lock.destroy(); -//! } +//! } // `lock` is deallocated here #[allow(non_camel_case_types)]; @@ -54,10 +60,20 @@ 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. pub struct StaticNativeMutex { priv inner: imp::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. /// @@ -144,6 +160,72 @@ impl StaticNativeMutex { 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; + /// let mut lock = NativeMutex::new(); + /// 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() + } + + /// Attempts to acquire the lock. The value returned is `Some` if + /// the attempt succeeded. + pub unsafe fn trylock<'a>(&'a mut self) -> Option> { + self.inner.trylock() + } + + /// Acquire the lock without creating a `LockGuard`. + /// + /// 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. + /// + /// 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) { diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index 0a6de50bf23..93322977bc1 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -10,51 +10,11 @@ use clone::Clone; use kinds::Send; -use ops::Drop; -use option::Option; use sync::arc::UnsafeArc; -use unstable::mutex::{StaticNativeMutex, LockGuard}; - -pub struct LittleLock { - priv l: StaticNativeMutex, -} - -pub struct LittleGuard<'a> { - priv l: LockGuard<'a> -} - -impl Drop for LittleLock { - fn drop(&mut self) { - unsafe { self.l.destroy(); } - } -} - -impl LittleLock { - pub fn new() -> LittleLock { - unsafe { LittleLock { l: StaticNativeMutex::new() } } - } - - pub unsafe fn lock<'a>(&'a mut self) -> LittleGuard<'a> { - LittleGuard { l: self.l.lock() } - } - - pub unsafe fn try_lock<'a>(&'a mut self) -> Option> { - self.l.trylock().map(|guard| LittleGuard { l: guard }) - } - - pub unsafe fn signal(&mut self) { - self.l.signal_noguard(); - } -} - -impl<'a> LittleGuard<'a> { - pub unsafe fn wait(&mut self) { - self.l.wait(); - } -} +use unstable::mutex::NativeMutex; struct ExData { - lock: LittleLock, + lock: NativeMutex, failed: bool, data: T, } @@ -83,7 +43,7 @@ impl Clone for Exclusive { impl Exclusive { pub fn new(user_data: T) -> Exclusive { let data = ExData { - lock: LittleLock::new(), + lock: unsafe {NativeMutex::new()}, failed: false, data: user_data }; @@ -92,8 +52,8 @@ impl Exclusive { } } - // 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 @@ -119,14 +79,14 @@ impl Exclusive { #[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]