about summary refs log tree commit diff
path: root/src/libstd/sys/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/common')
-rw-r--r--src/libstd/sys/common/mod.rs2
-rw-r--r--src/libstd/sys/common/poison.rs187
-rw-r--r--src/libstd/sys/common/remutex.rs233
3 files changed, 422 insertions, 0 deletions
diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
index d2e2f1044d6..8a01eace889 100644
--- a/src/libstd/sys/common/mod.rs
+++ b/src/libstd/sys/common/mod.rs
@@ -29,6 +29,8 @@ pub mod condvar;
 pub mod mutex;
 pub mod net;
 pub mod net2;
+pub mod poison;
+pub mod remutex;
 pub mod rwlock;
 pub mod stack;
 pub mod thread;
diff --git a/src/libstd/sys/common/poison.rs b/src/libstd/sys/common/poison.rs
new file mode 100644
index 00000000000..347cd0b464e
--- /dev/null
+++ b/src/libstd/sys/common/poison.rs
@@ -0,0 +1,187 @@
+// 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.
+
+use prelude::v1::*;
+
+use cell::UnsafeCell;
+use error::{Error};
+use fmt;
+use thread;
+
+pub struct Flag { failed: UnsafeCell<bool> }
+
+// This flag is only ever accessed with a lock previously held. Note that this
+// a totally private structure.
+unsafe impl Send for Flag {}
+unsafe impl Sync for Flag {}
+
+pub const FLAG_INIT: Flag = Flag { failed: UnsafeCell { value: false } };
+
+impl Flag {
+    #[inline]
+    pub fn borrow(&self) -> LockResult<Guard> {
+        let ret = Guard { panicking: thread::panicking() };
+        if unsafe { *self.failed.get() } {
+            Err(PoisonError::new(ret))
+        } else {
+            Ok(ret)
+        }
+    }
+
+    #[inline]
+    pub fn done(&self, guard: &Guard) {
+        if !guard.panicking && thread::panicking() {
+            unsafe { *self.failed.get() = true; }
+        }
+    }
+
+    #[inline]
+    pub fn get(&self) -> bool {
+        unsafe { *self.failed.get() }
+    }
+}
+
+pub struct Guard {
+    panicking: bool,
+}
+
+/// A type of error which can be returned whenever a lock is acquired.
+///
+/// Both Mutexes and RwLocks are poisoned whenever a task fails while the lock
+/// is held. The precise semantics for when a lock is poisoned is documented on
+/// each lock, but once a lock is poisoned then all future acquisitions will
+/// return this error.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct PoisonError<T> {
+    guard: T,
+}
+
+/// An enumeration of possible errors which can occur while calling the
+/// `try_lock` method.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub enum TryLockError<T> {
+    /// The lock could not be acquired because another task failed while holding
+    /// the lock.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    Poisoned(PoisonError<T>),
+    /// The lock could not be acquired at this time because the operation would
+    /// otherwise block.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    WouldBlock,
+}
+
+/// A type alias for the result of a lock method which can be poisoned.
+///
+/// The `Ok` variant of this result indicates that the primitive was not
+/// poisoned, and the `Guard` is contained within. The `Err` variant indicates
+/// that the primitive was poisoned. Note that the `Err` variant *also* carries
+/// the associated guard, and it can be acquired through the `into_inner`
+/// method.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
+
+/// A type alias for the result of a nonblocking locking method.
+///
+/// For more information, see `LockResult`. A `TryLockResult` doesn't
+/// necessarily hold the associated guard in the `Err` type as the lock may not
+/// have been acquired for other reasons.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Debug for PoisonError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        "PoisonError { inner: .. }".fmt(f)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Display for PoisonError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        "poisoned lock: another task failed inside".fmt(f)
+    }
+}
+
+impl<T: Send> Error for PoisonError<T> {
+    fn description(&self) -> &str {
+        "poisoned lock: another task failed inside"
+    }
+}
+
+impl<T> PoisonError<T> {
+    /// Create a `PoisonError`.
+    #[unstable(feature = "std_misc")]
+    pub fn new(guard: T) -> PoisonError<T> {
+        PoisonError { guard: guard }
+    }
+
+    /// Consumes this error indicating that a lock is poisoned, returning the
+    /// underlying guard to allow access regardless.
+    #[unstable(feature = "std_misc")]
+    pub fn into_inner(self) -> T { self.guard }
+
+    /// Reaches into this error indicating that a lock is poisoned, returning a
+    /// reference to the underlying guard to allow access regardless.
+    #[unstable(feature = "std_misc")]
+    pub fn get_ref(&self) -> &T { &self.guard }
+
+    /// Reaches into this error indicating that a lock is poisoned, returning a
+    /// mutable reference to the underlying guard to allow access regardless.
+    #[unstable(feature = "std_misc")]
+    pub fn get_mut(&mut self) -> &mut T { &mut self.guard }
+}
+
+impl<T> From<PoisonError<T>> for TryLockError<T> {
+    fn from(err: PoisonError<T>) -> TryLockError<T> {
+        TryLockError::Poisoned(err)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> fmt::Debug for TryLockError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
+            TryLockError::WouldBlock => "WouldBlock".fmt(f)
+        }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: Send> fmt::Display for TryLockError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.description().fmt(f)
+    }
+}
+
+impl<T: Send> Error for TryLockError<T> {
+    fn description(&self) -> &str {
+        match *self {
+            TryLockError::Poisoned(ref p) => p.description(),
+            TryLockError::WouldBlock => "try_lock failed because the operation would block"
+        }
+    }
+
+    fn cause(&self) -> Option<&Error> {
+        match *self {
+            TryLockError::Poisoned(ref p) => Some(p),
+            _ => None
+        }
+    }
+}
+
+pub fn map_result<T, U, F>(result: LockResult<T>, f: F)
+                           -> LockResult<U>
+                           where F: FnOnce(T) -> U {
+    match result {
+        Ok(t) => Ok(f(t)),
+        Err(PoisonError { guard }) => Err(PoisonError::new(f(guard)))
+    }
+}
diff --git a/src/libstd/sys/common/remutex.rs b/src/libstd/sys/common/remutex.rs
new file mode 100644
index 00000000000..b35063c0e23
--- /dev/null
+++ b/src/libstd/sys/common/remutex.rs
@@ -0,0 +1,233 @@
+// Copyright 2015 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.
+#![unstable(feature = "reentrant_mutex", reason = "new API")]
+
+use prelude::v1::*;
+
+use fmt;
+use marker;
+use ops::Deref;
+use sys_common::poison::{self, TryLockError, TryLockResult, LockResult};
+use sys::mutex as sys;
+
+/// A re-entrant mutual exclusion
+///
+/// This mutex will block *other* threads waiting for the lock to become available. The thread
+/// which has already locked the mutex can lock it multiple times without blocking, preventing a
+/// common source of deadlocks.
+pub struct ReentrantMutex<T> {
+    inner: Box<sys::ReentrantMutex>,
+    poison: poison::Flag,
+    data: T,
+}
+
+unsafe impl<T: Send> Send for ReentrantMutex<T> {}
+unsafe impl<T: Send> Sync for ReentrantMutex<T> {}
+
+
+/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
+/// dropped (falls out of scope), the lock will be unlocked.
+///
+/// The data protected by the mutex can be accessed through this guard via its
+/// Deref and DerefMut implementations
+#[must_use]
+pub struct ReentrantMutexGuard<'a, T: 'a> {
+    // funny underscores due to how Deref/DerefMut currently work (they
+    // disregard field privacy).
+    __lock: &'a ReentrantMutex<T>,
+    __poison: poison::Guard,
+}
+
+impl<'a, T> !marker::Send for ReentrantMutexGuard<'a, T> {}
+
+
+impl<T> ReentrantMutex<T> {
+    /// Creates a new reentrant mutex in an unlocked state.
+    pub fn new(t: T) -> ReentrantMutex<T> {
+        ReentrantMutex {
+            inner: box unsafe { sys::ReentrantMutex::new() },
+            poison: poison::FLAG_INIT,
+            data: t,
+        }
+    }
+
+    /// Acquires a mutex, blocking the current thread until it is able to do so.
+    ///
+    /// This function will block the caller until it is available to acquire the mutex.
+    /// Upon returning, the thread is the only thread with the mutex held. When the thread
+    /// calling this method already holds the lock, the call shall succeed without
+    /// blocking.
+    ///
+    /// # Failure
+    ///
+    /// If another user of this mutex panicked while holding the mutex, then
+    /// this call will return failure if the mutex would otherwise be
+    /// acquired.
+    pub fn lock(&self) -> LockResult<ReentrantMutexGuard<T>> {
+        unsafe { self.inner.lock() }
+        ReentrantMutexGuard::new(&self)
+    }
+
+    /// Attempts to acquire this lock.
+    ///
+    /// If the lock could not be acquired at this time, then `Err` is returned.
+    /// Otherwise, an RAII guard is returned.
+    ///
+    /// This function does not block.
+    ///
+    /// # Failure
+    ///
+    /// If another user of this mutex panicked while holding the mutex, then
+    /// this call will return failure if the mutex would otherwise be
+    /// acquired.
+    pub fn try_lock(&self) -> TryLockResult<ReentrantMutexGuard<T>> {
+        if unsafe { self.inner.try_lock() } {
+            Ok(try!(ReentrantMutexGuard::new(&self)))
+        } else {
+            Err(TryLockError::WouldBlock)
+        }
+    }
+}
+
+#[unsafe_destructor]
+impl<T> Drop for ReentrantMutex<T> {
+    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.inner.destroy() }
+    }
+}
+
+impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self.try_lock() {
+            Ok(guard) => write!(f, "ReentrantMutex {{ data: {:?} }}", &*guard),
+            Err(TryLockError::Poisoned(err)) => {
+                write!(f, "ReentrantMutex {{ data: Poisoned({:?}) }}", &**err.get_ref())
+            },
+            Err(TryLockError::WouldBlock) => write!(f, "ReentrantMutex {{ <locked> }}")
+        }
+    }
+}
+
+impl<'mutex, T> ReentrantMutexGuard<'mutex, T> {
+    fn new(lock: &'mutex ReentrantMutex<T>)
+            -> LockResult<ReentrantMutexGuard<'mutex, T>> {
+        poison::map_result(lock.poison.borrow(), |guard| {
+            ReentrantMutexGuard {
+                __lock: lock,
+                __poison: guard,
+            }
+        })
+    }
+}
+
+impl<'mutex, T> Deref for ReentrantMutexGuard<'mutex, T> {
+    type Target = T;
+
+    fn deref<'a>(&'a self) -> &'a T {
+        &self.__lock.data
+    }
+}
+
+#[unsafe_destructor]
+impl<'a, T> Drop for ReentrantMutexGuard<'a, T> {
+    #[inline]
+    fn drop(&mut self) {
+        unsafe {
+            self.__lock.poison.done(&self.__poison);
+            self.__lock.inner.unlock();
+        }
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+    use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
+    use cell::RefCell;
+    use sync::Arc;
+    use boxed;
+    use thread;
+
+    #[test]
+    fn smoke() {
+        let m = ReentrantMutex::new(());
+        {
+            let a = m.lock().unwrap();
+            {
+                let b = m.lock().unwrap();
+                {
+                    let c = m.lock().unwrap();
+                    assert_eq!(*c, ());
+                }
+                assert_eq!(*b, ());
+            }
+            assert_eq!(*a, ());
+        }
+    }
+
+    #[test]
+    fn is_mutex() {
+        let m = ReentrantMutex::new(RefCell::new(0));
+        let lock = m.lock().unwrap();
+        let handle = thread::scoped(|| {
+            let lock = m.lock().unwrap();
+            assert_eq!(*lock.borrow(), 4950);
+        });
+        for i in 0..100 {
+            let mut lock = m.lock().unwrap();
+            *lock.borrow_mut() += i;
+        }
+        drop(lock);
+        drop(handle);
+    }
+
+    #[test]
+    fn trylock_works() {
+        let m = ReentrantMutex::new(());
+        let lock = m.try_lock().unwrap();
+        let lock2 = m.try_lock().unwrap();
+        {
+            thread::scoped(|| {
+                let lock = m.try_lock();
+                assert!(lock.is_err());
+            });
+        }
+        let lock3 = m.try_lock().unwrap();
+    }
+
+    pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
+    impl<'a> Drop for Answer<'a> {
+        fn drop(&mut self) {
+            *self.0.borrow_mut() = 42;
+        }
+    }
+
+    #[test]
+    fn poison_works() {
+        let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
+        let mc = m.clone();
+        let result = thread::spawn(move ||{
+            let lock = mc.lock().unwrap();
+            *lock.borrow_mut() = 1;
+            let lock2 = mc.lock().unwrap();
+            *lock.borrow_mut() = 2;
+            let answer = Answer(lock2);
+            panic!("What the answer to my lifetimes dilemma is?");
+            drop(answer);
+        }).join();
+        assert!(result.is_err());
+        let r = m.lock().err().unwrap().into_inner();
+        assert_eq!(*r.borrow(), 42);
+    }
+}