about summary refs log tree commit diff
path: root/src/libstd/sync/mutex.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sync/mutex.rs')
-rw-r--r--src/libstd/sync/mutex.rs218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs
new file mode 100644
index 00000000000..c9e90210c30
--- /dev/null
+++ b/src/libstd/sync/mutex.rs
@@ -0,0 +1,218 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A simple native mutex implementation. Warning: this API is likely
+//! to change soon.
+
+#![allow(dead_code)]
+
+use core::prelude::*;
+use alloc::boxed::Box;
+use rustrt::mutex;
+
+pub const LOCKED: uint = 1 << 0;
+pub const BLOCKED: uint = 1 << 1;
+
+/// A mutual exclusion primitive useful for protecting shared data
+///
+/// This mutex will properly block tasks waiting for the lock to become
+/// available. The mutex can also be statically initialized or created via a
+/// `new` constructor.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// use std::sync::mutex::Mutex;
+///
+/// let m = Mutex::new();
+/// let guard = m.lock();
+/// // do some work
+/// drop(guard); // unlock the lock
+/// ```
+pub struct Mutex {
+    // Note that this static mutex is in a *box*, not inlined into the struct
+    // itself. This is done for memory safety reasons with the usage of a
+    // StaticNativeMutex inside the static mutex above. Once a native mutex has
+    // been used once, its address can never change (it can't be moved). This
+    // mutex type can be safely moved at any time, so to ensure that the native
+    // mutex is used correctly we box the inner lock to give it a constant
+    // address.
+    lock: Box<StaticMutex>,
+}
+
+/// The static mutex type is provided to allow for static allocation of mutexes.
+///
+/// Note that this is a separate type because using a Mutex correctly means that
+/// it needs to have a destructor run. In Rust, statics are not allowed to have
+/// destructors. As a result, a `StaticMutex` has one extra method when compared
+/// to a `Mutex`, a `destroy` method. This method is unsafe to call, and
+/// documentation can be found directly on the method.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// use std::sync::mutex::{StaticMutex, MUTEX_INIT};
+///
+/// static LOCK: StaticMutex = MUTEX_INIT;
+///
+/// {
+///     let _g = LOCK.lock();
+///     // do some productive work
+/// }
+/// // lock is unlocked here.
+/// ```
+pub struct StaticMutex {
+    lock: mutex::StaticNativeMutex,
+}
+
+/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
+/// dropped (falls out of scope), the lock will be unlocked.
+#[must_use]
+pub struct Guard<'a> {
+    guard: mutex::LockGuard<'a>,
+}
+
+fn lift_guard(guard: mutex::LockGuard) -> Guard {
+    Guard { guard: guard }
+}
+
+/// Static initialization of a mutex. This constant can be used to initialize
+/// other mutex constants.
+pub const MUTEX_INIT: StaticMutex = StaticMutex {
+    lock: mutex::NATIVE_MUTEX_INIT
+};
+
+impl StaticMutex {
+    /// Attempts to grab this lock, see `Mutex::try_lock`
+    pub fn try_lock<'a>(&'a self) -> Option<Guard<'a>> {
+        unsafe { self.lock.trylock().map(lift_guard) }
+    }
+
+    /// Acquires this lock, see `Mutex::lock`
+    pub fn lock<'a>(&'a self) -> Guard<'a> {
+        lift_guard(unsafe { self.lock.lock() })
+    }
+
+    /// Deallocates resources associated with this static mutex.
+    ///
+    /// This method is unsafe because it provides no guarantees that there are
+    /// no active users of this mutex, and safety is not guaranteed if there are
+    /// active users of this mutex.
+    ///
+    /// This method is required to ensure that there are no memory leaks on
+    /// *all* platforms. It may be the case that some platforms do not leak
+    /// memory if this method is not called, but this is not guaranteed to be
+    /// true on all platforms.
+    pub unsafe fn destroy(&self) {
+        self.lock.destroy()
+    }
+}
+
+impl Mutex {
+    /// Creates a new mutex in an unlocked state ready for use.
+    pub fn new() -> Mutex {
+        Mutex {
+            lock: box StaticMutex {
+                lock: unsafe { mutex::StaticNativeMutex::new() },
+            }
+        }
+    }
+
+    /// Attempts to acquire this lock.
+    ///
+    /// If the lock could not be acquired at this time, then `None` is returned.
+    /// Otherwise, an RAII guard is returned. The lock will be unlocked when the
+    /// guard is dropped.
+    ///
+    /// This function does not block.
+    pub fn try_lock<'a>(&'a self) -> Option<Guard<'a>> {
+        self.lock.try_lock()
+    }
+
+    /// Acquires a mutex, blocking the current task until it is able to do so.
+    ///
+    /// This function will block the local task until it is available to acquire
+    /// the mutex. Upon returning, the task is the only task with the mutex
+    /// held. An RAII guard is returned to allow scoped unlock of the lock. When
+    /// the guard goes out of scope, the mutex will be unlocked.
+    pub fn lock<'a>(&'a self) -> Guard<'a> { self.lock.lock() }
+}
+
+impl Drop for Mutex {
+    fn drop(&mut self) {
+        // This is actually safe b/c we know that there is no further usage of
+        // this mutex (it's up to the user to arrange for a mutex to get
+        // dropped, that's not our job)
+        unsafe { self.lock.destroy() }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::*;
+    use super::{Mutex, StaticMutex, MUTEX_INIT};
+
+    #[test]
+    fn smoke() {
+        let m = Mutex::new();
+        drop(m.lock());
+        drop(m.lock());
+    }
+
+    #[test]
+    fn smoke_static() {
+        static M: StaticMutex = MUTEX_INIT;
+        unsafe {
+            drop(M.lock());
+            drop(M.lock());
+            M.destroy();
+        }
+    }
+
+    #[test]
+    fn lots_and_lots() {
+        static M: StaticMutex = MUTEX_INIT;
+        static mut CNT: uint = 0;
+        static J: uint = 1000;
+        static K: uint = 3;
+
+        fn inc() {
+            for _ in range(0, J) {
+                unsafe {
+                    let _g = M.lock();
+                    CNT += 1;
+                }
+            }
+        }
+
+        let (tx, rx) = channel();
+        for _ in range(0, K) {
+            let tx2 = tx.clone();
+            spawn(proc() { inc(); tx2.send(()); });
+            let tx2 = tx.clone();
+            spawn(proc() { inc(); tx2.send(()); });
+        }
+
+        drop(tx);
+        for _ in range(0, 2 * K) {
+            rx.recv();
+        }
+        assert_eq!(unsafe {CNT}, J * K * 2);
+        unsafe {
+            M.destroy();
+        }
+    }
+
+    #[test]
+    fn trylock() {
+        let m = Mutex::new();
+        assert!(m.try_lock().is_some());
+    }
+}