Skip to content

Commit

Permalink
std: add a NativeMutex type as a wrapper to destroy StaticNativeMutex.
Browse files Browse the repository at this point in the history
This obsoletes LittleLock, and so it is removed.
  • Loading branch information
huonw committed Feb 15, 2014
1 parent b87ed60 commit 0937f65
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 73 deletions.
10 changes: 5 additions & 5 deletions src/libgreen/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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();
}
}

Expand All @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/librustuv/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<Message>,
}

Expand Down Expand Up @@ -112,7 +112,7 @@ impl QueuePool {
let handle = UvHandle::alloc(None::<AsyncWatcher>, uvll::UV_ASYNC);
let state = UnsafeArc::new(State {
handle: handle,
lock: LittleLock::new(),
lock: unsafe {NativeMutex::new()},
queue: mpsc::Queue::new(),
});
let q = ~QueuePool {
Expand Down
118 changes: 100 additions & 18 deletions src/libstd/unstable/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)];

Expand All @@ -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.
///
Expand Down Expand Up @@ -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<LockGuard<'a>> {
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) {
Expand Down
54 changes: 7 additions & 47 deletions src/libstd/unstable/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<LittleGuard<'a>> {
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<T> {
lock: LittleLock,
lock: NativeMutex,
failed: bool,
data: T,
}
Expand Down Expand Up @@ -83,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
};
Expand All @@ -92,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
Expand All @@ -119,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]
Expand Down

0 comments on commit 0937f65

Please sign in to comment.