]> git.lizzy.rs Git - rust.git/commitdiff
Implement a native mutex type
authorAlex Crichton <alex@alexcrichton.com>
Thu, 14 Nov 2013 07:17:18 +0000 (23:17 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 19 Nov 2013 04:06:39 +0000 (20:06 -0800)
This mutex is built on top of pthreads for unix and the related windows apis on
windows. This is a straight port of the lock_and_signal type from C++ to rust.
Almost all operations on the type are unsafe, and it's definitely not
recommended for general use.

Closes #9105

src/libstd/unstable/mod.rs
src/libstd/unstable/mutex.rs [new file with mode: 0644]
src/rt/rust_builtin.cpp
src/rt/rustrt.def.in

index ea03ea6f551c2a2dece7c1ff0bfb5047c5cd4ddf..9b8c81979a90e31d98247f171cccc55f853a248c 100644 (file)
@@ -24,6 +24,7 @@
 #[cfg(not(test))]
 pub mod lang;
 pub mod sync;
+pub mod mutex;
 pub mod atomics;
 pub mod raw;
 
diff --git a/src/libstd/unstable/mutex.rs b/src/libstd/unstable/mutex.rs
new file mode 100644 (file)
index 0000000..5be9be7
--- /dev/null
@@ -0,0 +1,336 @@
+// Copyright 2013 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 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.
+//!
+//! 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.
+//!
+//! 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.
+//!
+//! # Example
+//!
+//!     use std::unstable::mutex::{Mutex, MUTEX_INIT};
+//!
+//!     // Use a statically initialized mutex
+//!     static mut lock: Mutex = MUTEX_INIT;
+//!
+//!     unsafe {
+//!         lock.lock();
+//!         lock.unlock();
+//!     }
+//!
+//!     // Use a normally initialied mutex
+//!     let mut lock = Mutex::new();
+//!     unsafe {
+//!         lock.lock();
+//!         lock.unlock();
+//!         lock.destroy();
+//!     }
+
+#[allow(non_camel_case_types)];
+
+use libc::c_void;
+use unstable::atomics;
+
+pub struct Mutex {
+    // pointers for the lock/cond handles, atomically updated
+    priv lock: atomics::AtomicUint,
+    priv cond: atomics::AtomicUint,
+}
+
+pub static MUTEX_INIT: Mutex = Mutex {
+    lock: atomics::INIT_ATOMIC_UINT,
+    cond: atomics::INIT_ATOMIC_UINT,
+};
+
+impl Mutex {
+    /// Creates a new mutex, with the lock/condition variable pre-initialized
+    pub unsafe fn new() -> Mutex {
+        Mutex {
+            lock: atomics::AtomicUint::new(imp::init_lock() as uint),
+            cond: atomics::AtomicUint::new(imp::init_cond() as uint),
+        }
+    }
+
+    /// Creates a new copy of this mutex. This is an unsafe operation because
+    /// there is no reference counting performed on this type.
+    ///
+    /// This function may only be called on mutexes which have had both the
+    /// internal condition variable and lock initialized. This means that the
+    /// mutex must have been created via `new`, or usage of it has already
+    /// initialized the internal handles.
+    ///
+    /// This is a dangerous function to call as both this mutex and the returned
+    /// mutex will share the same handles to the underlying mutex/condition
+    /// variable. Care must be taken to ensure that deallocation happens
+    /// accordingly.
+    pub unsafe fn clone(&self) -> Mutex {
+        let lock = self.lock.load(atomics::Relaxed);
+        let cond = self.cond.load(atomics::Relaxed);
+        assert!(lock != 0);
+        assert!(cond != 0);
+        Mutex {
+            lock: atomics::AtomicUint::new(lock),
+            cond: atomics::AtomicUint::new(cond),
+        }
+    }
+
+    /// Acquires this lock. This assumes that the current thread does not
+    /// already hold the lock.
+    pub unsafe fn lock(&mut self) { imp::lock(self.getlock()) }
+
+    /// Attempts to acquire the lock. The value returned is whether the lock was
+    /// acquired or not
+    pub unsafe fn trylock(&mut self) -> bool { imp::trylock(self.getlock()) }
+
+    /// Unlocks the lock. This assumes that the current thread already holds the
+    /// lock.
+    pub unsafe fn unlock(&mut self) { imp::unlock(self.getlock()) }
+
+    /// Block on the internal condition variable.
+    ///
+    /// This function assumes that the lock is already held
+    pub unsafe fn wait(&mut self) { imp::wait(self.getcond(), self.getlock()) }
+
+    /// Signals a thread in `wait` to wake up
+    pub unsafe fn signal(&mut self) { imp::signal(self.getcond()) }
+
+    /// This function is especially unsafe because there are no guarantees made
+    /// that no other thread is currently holding the lock or waiting on the
+    /// condition variable contained inside.
+    pub unsafe fn destroy(&mut self) {
+        imp::free_lock(self.lock.swap(0, atomics::Relaxed));
+        imp::free_cond(self.cond.swap(0, atomics::Relaxed));
+    }
+
+    unsafe fn getlock(&mut self) -> *c_void {
+        match self.lock.load(atomics::Relaxed) {
+            0 => {}
+            n => return n as *c_void
+        }
+        let lock = imp::init_lock();
+        match self.lock.compare_and_swap(0, lock, atomics::SeqCst) {
+            0 => return lock as *c_void,
+            _ => {}
+        }
+        imp::free_lock(lock);
+        return self.lock.load(atomics::Relaxed) as *c_void;
+    }
+
+    unsafe fn getcond(&mut self) -> *c_void {
+        match self.cond.load(atomics::Relaxed) {
+            0 => {}
+            n => return n as *c_void
+        }
+        let cond = imp::init_cond();
+        match self.cond.compare_and_swap(0, cond, atomics::SeqCst) {
+            0 => return cond as *c_void,
+            _ => {}
+        }
+        imp::free_cond(cond);
+        return self.cond.load(atomics::Relaxed) as *c_void;
+    }
+}
+
+#[cfg(unix)]
+mod imp {
+    use libc::c_void;
+    use libc;
+    use ptr;
+
+    type pthread_mutex_t = libc::c_void;
+    type pthread_mutexattr_t = libc::c_void;
+    type pthread_cond_t = libc::c_void;
+    type pthread_condattr_t = libc::c_void;
+
+    pub unsafe fn init_lock() -> uint {
+        let block = libc::malloc(rust_pthread_mutex_t_size() as libc::size_t);
+        assert!(!block.is_null());
+        let n = pthread_mutex_init(block, ptr::null());
+        assert_eq!(n, 0);
+        return block as uint;
+    }
+
+    pub unsafe fn init_cond() -> uint {
+        let block = libc::malloc(rust_pthread_cond_t_size() as libc::size_t);
+        assert!(!block.is_null());
+        let n = pthread_cond_init(block, ptr::null());
+        assert_eq!(n, 0);
+        return block as uint;
+    }
+
+    pub unsafe fn free_lock(h: uint) {
+        let block = h as *c_void;
+        assert_eq!(pthread_mutex_destroy(block), 0);
+        libc::free(block);
+    }
+
+    pub unsafe fn free_cond(h: uint) {
+        let block = h as *c_void;
+        assert_eq!(pthread_cond_destroy(block), 0);
+        libc::free(block);
+    }
+
+    pub unsafe fn lock(l: *pthread_mutex_t) {
+        assert_eq!(pthread_mutex_lock(l), 0);
+    }
+
+    pub unsafe fn trylock(l: *c_void) -> bool {
+        pthread_mutex_trylock(l) == 0
+    }
+
+    pub unsafe fn unlock(l: *pthread_mutex_t) {
+        assert_eq!(pthread_mutex_unlock(l), 0);
+    }
+
+    pub unsafe fn wait(cond: *pthread_cond_t, m: *pthread_mutex_t) {
+        assert_eq!(pthread_cond_wait(cond, m), 0);
+    }
+
+    pub unsafe fn signal(cond: *pthread_cond_t) {
+        assert_eq!(pthread_cond_signal(cond), 0);
+    }
+
+    extern {
+        fn rust_pthread_mutex_t_size() -> libc::c_int;
+        fn rust_pthread_cond_t_size() -> libc::c_int;
+    }
+
+    extern {
+        fn pthread_mutex_init(lock: *pthread_mutex_t,
+                              attr: *pthread_mutexattr_t) -> libc::c_int;
+        fn pthread_mutex_destroy(lock: *pthread_mutex_t) -> libc::c_int;
+        fn pthread_cond_init(cond: *pthread_cond_t,
+                              attr: *pthread_condattr_t) -> libc::c_int;
+        fn pthread_cond_destroy(cond: *pthread_cond_t) -> libc::c_int;
+        fn pthread_mutex_lock(lock: *pthread_mutex_t) -> libc::c_int;
+        fn pthread_mutex_trylock(lock: *pthread_mutex_t) -> libc::c_int;
+        fn pthread_mutex_unlock(lock: *pthread_mutex_t) -> libc::c_int;
+
+        fn pthread_cond_wait(cond: *pthread_cond_t,
+                             lock: *pthread_mutex_t) -> libc::c_int;
+        fn pthread_cond_signal(cond: *pthread_cond_t) -> libc::c_int;
+    }
+}
+
+#[cfg(windows)]
+mod imp {
+    use libc;
+    use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR};
+    use ptr;
+    type LPCRITICAL_SECTION = *c_void;
+    static SPIN_COUNT: DWORD = 4000;
+
+    pub unsafe fn init_lock() -> uint {
+        let block = libc::malloc(rust_crit_section_size() as libc::size_t);
+        assert!(!block.is_null());
+        InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT);
+        return block as uint;
+    }
+
+    pub unsafe fn init_cond() -> uint {
+        return CreateEventA(ptr::mut_null(), libc::FALSE, libc::FALSE,
+                            ptr::null()) as uint;
+    }
+
+    pub unsafe fn free_lock(h: uint) {
+        DeleteCriticalSection(h as LPCRITICAL_SECTION);
+        libc::free(h as *c_void);
+    }
+
+    pub unsafe fn free_cond(h: uint) {
+        let block = h as HANDLE;
+        libc::CloseHandle(block);
+    }
+
+    pub unsafe fn lock(l: *c_void) {
+        EnterCriticalSection(l as LPCRITICAL_SECTION)
+    }
+
+    pub unsafe fn trylock(l: *c_void) -> bool {
+        TryEnterCriticalSection(l as LPCRITICAL_SECTION) != 0
+    }
+
+    pub unsafe fn unlock(l: *c_void) {
+        LeaveCriticalSection(l as LPCRITICAL_SECTION)
+    }
+
+    pub unsafe fn wait(cond: *c_void, m: *c_void) {
+        unlock(m);
+        WaitForSingleObject(cond as HANDLE, 0);
+        lock(m);
+    }
+
+    pub unsafe fn signal(cond: *c_void) {
+        assert!(SetEvent(cond as HANDLE) != 0);
+    }
+
+    extern {
+        fn rust_crit_section_size() -> libc::c_int;
+    }
+
+    extern "system" {
+        fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
+                        bManualReset: BOOL,
+                        bInitialState: BOOL,
+                        lpName: LPCSTR) -> HANDLE;
+        fn InitializeCriticalSectionAndSpinCount(
+                        lpCriticalSection: LPCRITICAL_SECTION,
+                        dwSpinCount: DWORD) -> BOOL;
+        fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
+        fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
+        fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
+        fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL;
+        fn SetEvent(hEvent: HANDLE) -> BOOL;
+        fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::{Mutex, MUTEX_INIT};
+    use rt::thread::Thread;
+
+    #[test]
+    fn somke_lock() {
+        static mut lock: Mutex = MUTEX_INIT;
+        unsafe {
+            lock.lock();
+            lock.unlock();
+        }
+    }
+
+    #[test]
+    fn somke_cond() {
+        static mut lock: Mutex = MUTEX_INIT;
+        unsafe {
+            let t = do Thread::start {
+                lock.lock();
+                lock.signal();
+                lock.unlock();
+            };
+            lock.lock();
+            lock.wait();
+            lock.unlock();
+            t.join();
+        }
+    }
+}
index 7d75d557bf76786237a182a5a382b5e246649a56..28dd4b51a13138286175f850994fc17affd4ca54 100644 (file)
@@ -671,6 +671,27 @@ rust_win32_rand_release() {
 }
 
 #endif
+
+#if defined(__WIN32__)
+
+extern "C" CDECL int
+rust_crit_section_size() { return sizeof(CRITICAL_SECTION); }
+extern "C" CDECL int
+rust_pthread_mutex_t_size() { return 0; }
+extern "C" CDECL int
+rust_pthread_cond_t_size() { return 0; }
+
+#else
+
+extern "C" CDECL int
+rust_crit_section_size() { return 0; }
+extern "C" CDECL int
+rust_pthread_mutex_t_size() { return sizeof(pthread_mutex_t); }
+extern "C" CDECL int
+rust_pthread_cond_t_size() { return sizeof(pthread_cond_t); }
+
+#endif
+
 //
 // Local Variables:
 // mode: C++
index 49b6ebce2840069390ee60a1c645c42ee4c72231..d78584f33dfd830b01aa5d707a72a9f62bfd9c08 100644 (file)
@@ -73,5 +73,6 @@ rust_get_global_args_ptr
 rust_take_global_args_lock
 rust_drop_global_args_lock
 rust_get_test_int
-rust_take_dlerror_lock
-rust_drop_dlerror_lock
+rust_pthread_mutex_t_size
+rust_pthread_cond_t_size
+rust_crit_section_size