aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src
diff options
context:
space:
mode:
authorRonald Kinard <[email protected]>2017-02-23 19:56:00 -0600
committerGitHub <[email protected]>2017-02-23 19:56:00 -0600
commit1c98cb543bf5264f3bbeba54e0049fbde66f3786 (patch)
treee7432275077b8ba29b39160eec9a17cab62e8138 /ctr-std/src
parentMerge pull request #19 from FenrirWolf/thread_local (diff)
parentAdd sync::mutex (diff)
downloadctru-rs-1c98cb543bf5264f3bbeba54e0049fbde66f3786.tar.xz
ctru-rs-1c98cb543bf5264f3bbeba54e0049fbde66f3786.zip
Merge pull request #21 from FenrirWolf/mutex
Add sync::mutex
Diffstat (limited to 'ctr-std/src')
-rw-r--r--ctr-std/src/lib.rs3
-rw-r--r--ctr-std/src/sync/mod.rs10
-rw-r--r--ctr-std/src/sync/mutex.rs681
-rw-r--r--ctr-std/src/sys/unix/mod.rs1
-rw-r--r--ctr-std/src/sys/unix/mutex.rs84
-rw-r--r--ctr-std/src/sys_common/mod.rs3
-rw-r--r--ctr-std/src/sys_common/mutex.rs66
-rw-r--r--ctr-std/src/sys_common/poison.rs199
-rw-r--r--ctr-std/src/sys_common/remutex.rs236
-rw-r--r--ctr-std/src/thread/mod.rs5
10 files changed, 1283 insertions, 5 deletions
diff --git a/ctr-std/src/lib.rs b/ctr-std/src/lib.rs
index e0058f5..d4dfd45 100644
--- a/ctr-std/src/lib.rs
+++ b/ctr-std/src/lib.rs
@@ -9,8 +9,10 @@
#![feature(compiler_builtins_lib)]
#![feature(core_intrinsics)]
#![feature(char_escape_debug)]
+#![feature(dropck_eyepatch)]
#![feature(float_extras)]
#![feature(fused)]
+#![feature(generic_param_attrs)]
#![feature(int_error_internals)]
#![feature(lang_items)]
#![feature(macro_reexport)]
@@ -51,6 +53,7 @@ extern crate compiler_builtins;
// 3ds-specific dependencies
extern crate ctr_libc as libc;
+extern crate ctru_sys as libctru;
// stealing spin's mutex implementation for now
extern crate spin;
diff --git a/ctr-std/src/sync/mod.rs b/ctr-std/src/sync/mod.rs
index 487c4c9..df954cd 100644
--- a/ctr-std/src/sync/mod.rs
+++ b/ctr-std/src/sync/mod.rs
@@ -21,9 +21,9 @@
pub use alloc::arc::{Arc, Weak};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::sync::atomic;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::mutex::{Mutex, MutexGuard};
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use sys_common::poison::{PoisonError, TryLockError, TryLockResult, LockResult};
-// Easy cheat until we get proper locks based on libctru code
-#[stable(feature = "3ds", since = "1.0.0")]
-pub use spin::{Mutex, MutexGuard};
-#[stable(feature = "3ds", since = "1.0.0")]
-pub use spin::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+mod mutex;
diff --git a/ctr-std/src/sync/mutex.rs b/ctr-std/src/sync/mutex.rs
new file mode 100644
index 0000000..0d6ad5e
--- /dev/null
+++ b/ctr-std/src/sync/mutex.rs
@@ -0,0 +1,681 @@
+// 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 cell::UnsafeCell;
+use fmt;
+use marker;
+use mem;
+use ops::{Deref, DerefMut};
+use ptr;
+use sys_common::mutex as sys;
+use sys_common::poison::{self, TryLockError, TryLockResult, LockResult};
+
+/// A mutual exclusion primitive useful for protecting shared data
+///
+/// This mutex will block threads waiting for the lock to become available. The
+/// mutex can also be statically initialized or created via a `new`
+/// constructor. Each mutex has a type parameter which represents the data that
+/// it is protecting. The data can only be accessed through the RAII guards
+/// returned from `lock` and `try_lock`, which guarantees that the data is only
+/// ever accessed when the mutex is locked.
+///
+/// # Poisoning
+///
+/// The mutexes in this module implement a strategy called "poisoning" where a
+/// mutex is considered poisoned whenever a thread panics while holding the
+/// lock. Once a mutex is poisoned, all other threads are unable to access the
+/// data by default as it is likely tainted (some invariant is not being
+/// upheld).
+///
+/// For a mutex, this means that the `lock` and `try_lock` methods return a
+/// `Result` which indicates whether a mutex has been poisoned or not. Most
+/// usage of a mutex will simply `unwrap()` these results, propagating panics
+/// among threads to ensure that a possibly invalid invariant is not witnessed.
+///
+/// A poisoned mutex, however, does not prevent all access to the underlying
+/// data. The `PoisonError` type has an `into_inner` method which will return
+/// the guard that would have otherwise been returned on a successful lock. This
+/// allows access to the data, despite the lock being poisoned.
+///
+/// # Examples
+///
+/// ```
+/// use std::sync::{Arc, Mutex};
+/// use std::thread;
+/// use std::sync::mpsc::channel;
+///
+/// const N: usize = 10;
+///
+/// // Spawn a few threads to increment a shared variable (non-atomically), and
+/// // let the main thread know once all increments are done.
+/// //
+/// // Here we're using an Arc to share memory among threads, and the data inside
+/// // the Arc is protected with a mutex.
+/// let data = Arc::new(Mutex::new(0));
+///
+/// let (tx, rx) = channel();
+/// for _ in 0..10 {
+/// let (data, tx) = (data.clone(), tx.clone());
+/// thread::spawn(move || {
+/// // The shared state can only be accessed once the lock is held.
+/// // Our non-atomic increment is safe because we're the only thread
+/// // which can access the shared state when the lock is held.
+/// //
+/// // We unwrap() the return value to assert that we are not expecting
+/// // threads to ever fail while holding the lock.
+/// let mut data = data.lock().unwrap();
+/// *data += 1;
+/// if *data == N {
+/// tx.send(()).unwrap();
+/// }
+/// // the lock is unlocked here when `data` goes out of scope.
+/// });
+/// }
+///
+/// rx.recv().unwrap();
+/// ```
+///
+/// To recover from a poisoned mutex:
+///
+/// ```
+/// use std::sync::{Arc, Mutex};
+/// use std::thread;
+///
+/// let lock = Arc::new(Mutex::new(0_u32));
+/// let lock2 = lock.clone();
+///
+/// let _ = thread::spawn(move || -> () {
+/// // This thread will acquire the mutex first, unwrapping the result of
+/// // `lock` because the lock has not been poisoned.
+/// let _guard = lock2.lock().unwrap();
+///
+/// // This panic while holding the lock (`_guard` is in scope) will poison
+/// // the mutex.
+/// panic!();
+/// }).join();
+///
+/// // The lock is poisoned by this point, but the returned result can be
+/// // pattern matched on to return the underlying guard on both branches.
+/// let mut guard = match lock.lock() {
+/// Ok(guard) => guard,
+/// Err(poisoned) => poisoned.into_inner(),
+/// };
+///
+/// *guard += 1;
+/// ```
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct Mutex<T: ?Sized> {
+ // Note that this mutex is in a *box*, not inlined into the struct itself.
+ // 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.
+ inner: Box<sys::Mutex>,
+ poison: poison::Flag,
+ data: UnsafeCell<T>,
+}
+
+// these are the only places where `T: Send` matters; all other
+// functionality works fine on a single thread.
+#[stable(feature = "rust1", since = "1.0.0")]
+unsafe impl<T: ?Sized + Send> Send for Mutex<T> { }
+#[stable(feature = "rust1", since = "1.0.0")]
+unsafe impl<T: ?Sized + Send> Sync for Mutex<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 access through this guard via its
+/// `Deref` and `DerefMut` implementations.
+///
+/// This structure is created by the [`lock()`] and [`try_lock()`] methods on
+/// [`Mutex`].
+///
+/// [`lock()`]: struct.Mutex.html#method.lock
+/// [`try_lock()`]: struct.Mutex.html#method.try_lock
+/// [`Mutex`]: struct.Mutex.html
+#[must_use]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct MutexGuard<'a, T: ?Sized + 'a> {
+ // funny underscores due to how Deref/DerefMut currently work (they
+ // disregard field privacy).
+ __lock: &'a Mutex<T>,
+ __poison: poison::Guard,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, T: ?Sized> !marker::Send for MutexGuard<'a, T> {}
+
+impl<T> Mutex<T> {
+ /// Creates a new mutex in an unlocked state ready for use.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::Mutex;
+ ///
+ /// let mutex = Mutex::new(0);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn new(t: T) -> Mutex<T> {
+ let mut m = Mutex {
+ inner: box sys::Mutex::new(),
+ poison: poison::Flag::new(),
+ data: UnsafeCell::new(t),
+ };
+ unsafe {
+ m.inner.init();
+ }
+ m
+ }
+}
+
+impl<T: ?Sized> Mutex<T> {
+ /// Acquires a mutex, blocking the current thread until it is able to do so.
+ ///
+ /// This function will block the local thread until it is available to acquire
+ /// the mutex. Upon returning, the thread is the only thread 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.
+ ///
+ /// The exact behavior on locking a mutex in the thread which already holds
+ /// the lock is left unspecified. However, this function will not return on
+ /// the second call (it might panic or deadlock, for example).
+ ///
+ /// # Errors
+ ///
+ /// If another user of this mutex panicked while holding the mutex, then
+ /// this call will return an error once the mutex is acquired.
+ ///
+ /// # Panics
+ ///
+ /// This function might panic when called if the lock is already held by
+ /// the current thread.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::{Arc, Mutex};
+ /// use std::thread;
+ ///
+ /// let mutex = Arc::new(Mutex::new(0));
+ /// let c_mutex = mutex.clone();
+ ///
+ /// thread::spawn(move || {
+ /// *c_mutex.lock().unwrap() = 10;
+ /// }).join().expect("thread::spawn failed");
+ /// assert_eq!(*mutex.lock().unwrap(), 10);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn lock(&self) -> LockResult<MutexGuard<T>> {
+ unsafe {
+ self.inner.lock();
+ MutexGuard::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. The lock will be unlocked when the
+ /// guard is dropped.
+ ///
+ /// This function does not block.
+ ///
+ /// # Errors
+ ///
+ /// If another user of this mutex panicked while holding the mutex, then
+ /// this call will return failure if the mutex would otherwise be
+ /// acquired.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::{Arc, Mutex};
+ /// use std::thread;
+ ///
+ /// let mutex = Arc::new(Mutex::new(0));
+ /// let c_mutex = mutex.clone();
+ ///
+ /// thread::spawn(move || {
+ /// let mut lock = c_mutex.try_lock();
+ /// if let Ok(ref mut mutex) = lock {
+ /// **mutex = 10;
+ /// } else {
+ /// println!("try_lock failed");
+ /// }
+ /// }).join().expect("thread::spawn failed");
+ /// assert_eq!(*mutex.lock().unwrap(), 10);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> {
+ unsafe {
+ if self.inner.try_lock() {
+ Ok(MutexGuard::new(self)?)
+ } else {
+ Err(TryLockError::WouldBlock)
+ }
+ }
+ }
+
+ /// Determines whether the lock is poisoned.
+ ///
+ /// If another thread is active, the lock can still become poisoned at any
+ /// time. You should not trust a `false` value for program correctness
+ /// without additional synchronization.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::{Arc, Mutex};
+ /// use std::thread;
+ ///
+ /// let mutex = Arc::new(Mutex::new(0));
+ /// let c_mutex = mutex.clone();
+ ///
+ /// let _ = thread::spawn(move || {
+ /// let _lock = c_mutex.lock().unwrap();
+ /// panic!(); // the mutex gets poisoned
+ /// }).join();
+ /// assert_eq!(mutex.is_poisoned(), true);
+ /// ```
+ #[inline]
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ pub fn is_poisoned(&self) -> bool {
+ self.poison.get()
+ }
+
+ /// Consumes this mutex, returning the underlying data.
+ ///
+ /// # Errors
+ ///
+ /// If another user of this mutex panicked while holding the mutex, then
+ /// this call will return an error instead.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::Mutex;
+ ///
+ /// let mutex = Mutex::new(0);
+ /// assert_eq!(mutex.into_inner().unwrap(), 0);
+ /// ```
+ #[stable(feature = "mutex_into_inner", since = "1.6.0")]
+ pub fn into_inner(self) -> LockResult<T> where T: Sized {
+ // We know statically that there are no outstanding references to
+ // `self` so there's no need to lock the inner lock.
+ //
+ // To get the inner value, we'd like to call `data.into_inner()`,
+ // but because `Mutex` impl-s `Drop`, we can't move out of it, so
+ // we'll have to destructure it manually instead.
+ unsafe {
+ // Like `let Mutex { inner, poison, data } = self`.
+ let (inner, poison, data) = {
+ let Mutex { ref inner, ref poison, ref data } = self;
+ (ptr::read(inner), ptr::read(poison), ptr::read(data))
+ };
+ mem::forget(self);
+ inner.destroy(); // Keep in sync with the `Drop` impl.
+ drop(inner);
+
+ poison::map_result(poison.borrow(), |_| data.into_inner())
+ }
+ }
+
+ /// Returns a mutable reference to the underlying data.
+ ///
+ /// Since this call borrows the `Mutex` mutably, no actual locking needs to
+ /// take place---the mutable borrow statically guarantees no locks exist.
+ ///
+ /// # Errors
+ ///
+ /// If another user of this mutex panicked while holding the mutex, then
+ /// this call will return an error instead.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::sync::Mutex;
+ ///
+ /// let mut mutex = Mutex::new(0);
+ /// *mutex.get_mut().unwrap() = 10;
+ /// assert_eq!(*mutex.lock().unwrap(), 10);
+ /// ```
+ #[stable(feature = "mutex_get_mut", since = "1.6.0")]
+ pub fn get_mut(&mut self) -> LockResult<&mut T> {
+ // We know statically that there are no other references to `self`, so
+ // there's no need to lock the inner lock.
+ let data = unsafe { &mut *self.data.get() };
+ poison::map_result(self.poison.borrow(), |_| data )
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+unsafe impl<#[may_dangle] T: ?Sized> Drop for Mutex<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)
+ //
+ // IMPORTANT: This code must be kept in sync with `Mutex::into_inner`.
+ unsafe { self.inner.destroy() }
+ }
+}
+
+#[stable(feature = "mutex_default", since = "1.9.0")]
+impl<T: ?Sized + Default> Default for Mutex<T> {
+ /// Creates a `Mutex<T>`, with the `Default` value for T.
+ fn default() -> Mutex<T> {
+ Mutex::new(Default::default())
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.try_lock() {
+ Ok(guard) => write!(f, "Mutex {{ data: {:?} }}", &*guard),
+ Err(TryLockError::Poisoned(err)) => {
+ write!(f, "Mutex {{ data: Poisoned({:?}) }}", &**err.get_ref())
+ },
+ Err(TryLockError::WouldBlock) => write!(f, "Mutex {{ <locked> }}")
+ }
+ }
+}
+
+impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
+ unsafe fn new(lock: &'mutex Mutex<T>) -> LockResult<MutexGuard<'mutex, T>> {
+ poison::map_result(lock.poison.borrow(), |guard| {
+ MutexGuard {
+ __lock: lock,
+ __poison: guard,
+ }
+ })
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'mutex, T: ?Sized> Deref for MutexGuard<'mutex, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ unsafe { &*self.__lock.data.get() }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ unsafe { &mut *self.__lock.data.get() }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ self.__lock.poison.done(&self.__poison);
+ self.__lock.inner.unlock();
+ }
+ }
+}
+
+#[stable(feature = "std_debug", since = "1.16.0")]
+impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'a, T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("MutexGuard")
+ .field("lock", &self.__lock)
+ .finish()
+ }
+}
+
+pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
+ &guard.__lock.inner
+}
+
+pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
+ &guard.__lock.poison
+}
+
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests {
+ use sync::mpsc::channel;
+ use sync::{Arc, Mutex, Condvar};
+ use sync::atomic::{AtomicUsize, Ordering};
+ use thread;
+
+ struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
+
+ #[derive(Eq, PartialEq, Debug)]
+ struct NonCopy(i32);
+
+ unsafe impl<T: Send> Send for Packet<T> {}
+ unsafe impl<T> Sync for Packet<T> {}
+
+ #[test]
+ fn smoke() {
+ let m = Mutex::new(());
+ drop(m.lock().unwrap());
+ drop(m.lock().unwrap());
+ }
+
+ #[test]
+ fn lots_and_lots() {
+ const J: u32 = 1000;
+ const K: u32 = 3;
+
+ let m = Arc::new(Mutex::new(0));
+
+ fn inc(m: &Mutex<u32>) {
+ for _ in 0..J {
+ *m.lock().unwrap() += 1;
+ }
+ }
+
+ let (tx, rx) = channel();
+ for _ in 0..K {
+ let tx2 = tx.clone();
+ let m2 = m.clone();
+ thread::spawn(move|| { inc(&m2); tx2.send(()).unwrap(); });
+ let tx2 = tx.clone();
+ let m2 = m.clone();
+ thread::spawn(move|| { inc(&m2); tx2.send(()).unwrap(); });
+ }
+
+ drop(tx);
+ for _ in 0..2 * K {
+ rx.recv().unwrap();
+ }
+ assert_eq!(*m.lock().unwrap(), J * K * 2);
+ }
+
+ #[test]
+ fn try_lock() {
+ let m = Mutex::new(());
+ *m.try_lock().unwrap() = ();
+ }
+
+ #[test]
+ fn test_into_inner() {
+ let m = Mutex::new(NonCopy(10));
+ assert_eq!(m.into_inner().unwrap(), NonCopy(10));
+ }
+
+ #[test]
+ fn test_into_inner_drop() {
+ struct Foo(Arc<AtomicUsize>);
+ impl Drop for Foo {
+ fn drop(&mut self) {
+ self.0.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+ let num_drops = Arc::new(AtomicUsize::new(0));
+ let m = Mutex::new(Foo(num_drops.clone()));
+ assert_eq!(num_drops.load(Ordering::SeqCst), 0);
+ {
+ let _inner = m.into_inner().unwrap();
+ assert_eq!(num_drops.load(Ordering::SeqCst), 0);
+ }
+ assert_eq!(num_drops.load(Ordering::SeqCst), 1);
+ }
+
+ #[test]
+ fn test_into_inner_poison() {
+ let m = Arc::new(Mutex::new(NonCopy(10)));
+ let m2 = m.clone();
+ let _ = thread::spawn(move || {
+ let _lock = m2.lock().unwrap();
+ panic!("test panic in inner thread to poison mutex");
+ }).join();
+
+ assert!(m.is_poisoned());
+ match Arc::try_unwrap(m).unwrap().into_inner() {
+ Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
+ Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {:?}", x),
+ }
+ }
+
+ #[test]
+ fn test_get_mut() {
+ let mut m = Mutex::new(NonCopy(10));
+ *m.get_mut().unwrap() = NonCopy(20);
+ assert_eq!(m.into_inner().unwrap(), NonCopy(20));
+ }
+
+ #[test]
+ fn test_get_mut_poison() {
+ let m = Arc::new(Mutex::new(NonCopy(10)));
+ let m2 = m.clone();
+ let _ = thread::spawn(move || {
+ let _lock = m2.lock().unwrap();
+ panic!("test panic in inner thread to poison mutex");
+ }).join();
+
+ assert!(m.is_poisoned());
+ match Arc::try_unwrap(m).unwrap().get_mut() {
+ Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
+ Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {:?}", x),
+ }
+ }
+
+ #[test]
+ fn test_mutex_arc_condvar() {
+ let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
+ let packet2 = Packet(packet.0.clone());
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move|| {
+ // wait until parent gets in
+ rx.recv().unwrap();
+ let &(ref lock, ref cvar) = &*packet2.0;
+ let mut lock = lock.lock().unwrap();
+ *lock = true;
+ cvar.notify_one();
+ });
+
+ let &(ref lock, ref cvar) = &*packet.0;
+ let mut lock = lock.lock().unwrap();
+ tx.send(()).unwrap();
+ assert!(!*lock);
+ while !*lock {
+ lock = cvar.wait(lock).unwrap();
+ }
+ }
+
+ #[test]
+ fn test_arc_condvar_poison() {
+ let packet = Packet(Arc::new((Mutex::new(1), Condvar::new())));
+ let packet2 = Packet(packet.0.clone());
+ let (tx, rx) = channel();
+
+ let _t = thread::spawn(move || -> () {
+ rx.recv().unwrap();
+ let &(ref lock, ref cvar) = &*packet2.0;
+ let _g = lock.lock().unwrap();
+ cvar.notify_one();
+ // Parent should fail when it wakes up.
+ panic!();
+ });
+
+ let &(ref lock, ref cvar) = &*packet.0;
+ let mut lock = lock.lock().unwrap();
+ tx.send(()).unwrap();
+ while *lock == 1 {
+ match cvar.wait(lock) {
+ Ok(l) => {
+ lock = l;
+ assert_eq!(*lock, 1);
+ }
+ Err(..) => break,
+ }
+ }
+ }
+
+ #[test]
+ fn test_mutex_arc_poison() {
+ let arc = Arc::new(Mutex::new(1));
+ assert!(!arc.is_poisoned());
+ let arc2 = arc.clone();
+ let _ = thread::spawn(move|| {
+ let lock = arc2.lock().unwrap();
+ assert_eq!(*lock, 2);
+ }).join();
+ assert!(arc.lock().is_err());
+ assert!(arc.is_poisoned());
+ }
+
+ #[test]
+ fn test_mutex_arc_nested() {
+ // Tests nested mutexes and access
+ // to underlying data.
+ let arc = Arc::new(Mutex::new(1));
+ let arc2 = Arc::new(Mutex::new(arc));
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move|| {
+ let lock = arc2.lock().unwrap();
+ let lock2 = lock.lock().unwrap();
+ assert_eq!(*lock2, 1);
+ tx.send(()).unwrap();
+ });
+ rx.recv().unwrap();
+ }
+
+ #[test]
+ fn test_mutex_arc_access_in_unwind() {
+ let arc = Arc::new(Mutex::new(1));
+ let arc2 = arc.clone();
+ let _ = thread::spawn(move|| -> () {
+ struct Unwinder {
+ i: Arc<Mutex<i32>>,
+ }
+ impl Drop for Unwinder {
+ fn drop(&mut self) {
+ *self.i.lock().unwrap() += 1;
+ }
+ }
+ let _u = Unwinder { i: arc2 };
+ panic!();
+ }).join();
+ let lock = arc.lock().unwrap();
+ assert_eq!(*lock, 2);
+ }
+
+ #[test]
+ fn test_mutex_unsized() {
+ let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]);
+ {
+ let b = &mut *mutex.lock().unwrap();
+ b[0] = 4;
+ b[2] = 5;
+ }
+ let comp: &[i32] = &[4, 2, 5];
+ assert_eq!(&*mutex.lock().unwrap(), comp);
+ }
+}
diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs
index 13639eb..5ea31af 100644
--- a/ctr-std/src/sys/unix/mod.rs
+++ b/ctr-std/src/sys/unix/mod.rs
@@ -13,6 +13,7 @@
pub mod ext;
pub mod fast_thread_local;
pub mod memchr;
+pub mod mutex;
pub mod os;
pub mod os_str;
pub mod path;
diff --git a/ctr-std/src/sys/unix/mutex.rs b/ctr-std/src/sys/unix/mutex.rs
new file mode 100644
index 0000000..d4fd2c2
--- /dev/null
+++ b/ctr-std/src/sys/unix/mutex.rs
@@ -0,0 +1,84 @@
+// 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 cell::UnsafeCell;
+use mem;
+
+use libctru::synchronization;
+
+pub struct Mutex { inner: UnsafeCell<synchronization::LightLock> }
+
+#[inline]
+pub unsafe fn raw(m: &Mutex) -> *mut synchronization::LightLock {
+ m.inner.get()
+}
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {}
+
+#[allow(dead_code)] // sys isn't exported yet
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex { inner: UnsafeCell::new(0) }
+ }
+ #[inline]
+ pub unsafe fn init(&mut self) {
+ synchronization::LightLock_Init(self.inner.get());
+ }
+ #[inline]
+ pub unsafe fn lock(&self) {
+ synchronization::LightLock_Lock(self.inner.get());
+ }
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ synchronization::LightLock_Unlock(self.inner.get());
+ }
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ match synchronization::LightLock_TryLock(self.inner.get()) {
+ 0 => true,
+ _ => false,
+ }
+ }
+ #[inline]
+ pub unsafe fn destroy(&self) {}
+}
+
+pub struct ReentrantMutex { inner: UnsafeCell<synchronization::RecursiveLock> }
+
+unsafe impl Send for ReentrantMutex {}
+unsafe impl Sync for ReentrantMutex {}
+
+impl ReentrantMutex {
+ pub unsafe fn uninitialized() -> ReentrantMutex {
+ ReentrantMutex { inner: mem::uninitialized() }
+ }
+ #[inline]
+ pub unsafe fn init(&mut self) {
+ synchronization::RecursiveLock_Init(self.inner.get());
+ }
+ #[inline]
+ pub unsafe fn lock(&self) {
+ synchronization::RecursiveLock_Lock(self.inner.get());
+ }
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ synchronization::RecursiveLock_Unlock(self.inner.get());
+ }
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ match synchronization::RecursiveLock_TryLock(self.inner.get()) {
+ 0 => true,
+ _ => false,
+ }
+ }
+ #[inline]
+ pub unsafe fn destroy(&self) {}
+}
diff --git a/ctr-std/src/sys_common/mod.rs b/ctr-std/src/sys_common/mod.rs
index 701ab09..c6f94ed 100644
--- a/ctr-std/src/sys_common/mod.rs
+++ b/ctr-std/src/sys_common/mod.rs
@@ -25,6 +25,9 @@
#![allow(missing_docs)]
pub mod io;
+pub mod mutex;
+pub mod poison;
+pub mod remutex;
pub mod thread_local;
// common error constructors
diff --git a/ctr-std/src/sys_common/mutex.rs b/ctr-std/src/sys_common/mutex.rs
new file mode 100644
index 0000000..d1a7387
--- /dev/null
+++ b/ctr-std/src/sys_common/mutex.rs
@@ -0,0 +1,66 @@
+// 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 sys::mutex as imp;
+
+/// An OS-based mutual exclusion lock.
+///
+/// This is the thinnest cross-platform wrapper around OS mutexes. All usage of
+/// this mutex is unsafe and it is recommended to instead use the safe wrapper
+/// at the top level of the crate instead of this type.
+pub struct Mutex(imp::Mutex);
+
+unsafe impl Sync for Mutex {}
+
+impl Mutex {
+ /// Creates a new mutex for use.
+ ///
+ /// Behavior is undefined if the mutex is moved after it is
+ /// first used with any of the functions below.
+ pub const fn new() -> Mutex { Mutex(imp::Mutex::new()) }
+
+ /// Prepare the mutex for use.
+ ///
+ /// This should be called once the mutex is at a stable memory address.
+ #[inline]
+ pub unsafe fn init(&mut self) { self.0.init() }
+
+ /// Locks the mutex blocking the current thread until it is available.
+ ///
+ /// Behavior is undefined if the mutex has been moved between this and any
+ /// previous function call.
+ #[inline]
+ pub unsafe fn lock(&self) { self.0.lock() }
+
+ /// Attempts to lock the mutex without blocking, returning whether it was
+ /// successfully acquired or not.
+ ///
+ /// Behavior is undefined if the mutex has been moved between this and any
+ /// previous function call.
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool { self.0.try_lock() }
+
+ /// Unlocks the mutex.
+ ///
+ /// Behavior is undefined if the current thread does not actually hold the
+ /// mutex.
+ #[inline]
+ pub unsafe fn unlock(&self) { self.0.unlock() }
+
+ /// Deallocates all resources associated with this mutex.
+ ///
+ /// Behavior is undefined if there are current or will be future users of
+ /// this mutex.
+ #[inline]
+ pub unsafe fn destroy(&self) { self.0.destroy() }
+}
+
+// not meant to be exported to the outside world, just the containing module
+pub fn raw(mutex: &Mutex) -> &imp::Mutex { &mutex.0 }
diff --git a/ctr-std/src/sys_common/poison.rs b/ctr-std/src/sys_common/poison.rs
new file mode 100644
index 0000000..bdc727f
--- /dev/null
+++ b/ctr-std/src/sys_common/poison.rs
@@ -0,0 +1,199 @@
+// 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 error::{Error};
+use fmt;
+use sync::atomic::{AtomicBool, Ordering};
+use thread;
+
+pub struct Flag { failed: AtomicBool }
+
+// Note that the Ordering uses to access the `failed` field of `Flag` below is
+// always `Relaxed`, and that's because this isn't actually protecting any data,
+// it's just a flag whether we've panicked or not.
+//
+// The actual location that this matters is when a mutex is **locked** which is
+// where we have external synchronization ensuring that we see memory
+// reads/writes to this flag.
+//
+// As a result, if it matters, we should see the correct value for `failed` in
+// all cases.
+
+impl Flag {
+ pub const fn new() -> Flag {
+ Flag { failed: AtomicBool::new(false) }
+ }
+
+ #[inline]
+ pub fn borrow(&self) -> LockResult<Guard> {
+ let ret = Guard { panicking: thread::panicking() };
+ if self.get() {
+ Err(PoisonError::new(ret))
+ } else {
+ Ok(ret)
+ }
+ }
+
+ #[inline]
+ pub fn done(&self, guard: &Guard) {
+ if !guard.panicking && thread::panicking() {
+ self.failed.store(true, Ordering::Relaxed);
+ }
+ }
+
+ #[inline]
+ pub fn get(&self) -> bool {
+ self.failed.load(Ordering::Relaxed)
+ }
+}
+
+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 thread 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 thread failed while holding
+ /// the lock.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ Poisoned(#[stable(feature = "rust1", since = "1.0.0")] 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)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> Error for PoisonError<T> {
+ fn description(&self) -> &str {
+ "poisoned lock: another task failed inside"
+ }
+}
+
+impl<T> PoisonError<T> {
+ /// Creates a `PoisonError`.
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ 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.
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ 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.
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ 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.
+ #[stable(feature = "sync_poison", since = "1.2.0")]
+ pub fn get_mut(&mut self) -> &mut T { &mut self.guard }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+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> fmt::Display for TryLockError<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
+ TryLockError::WouldBlock => "try_lock failed because the operation would block"
+ }.fmt(f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> 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/ctr-std/src/sys_common/remutex.rs b/ctr-std/src/sys_common/remutex.rs
new file mode 100644
index 0000000..4d0407c
--- /dev/null
+++ b/ctr-std/src/sys_common/remutex.rs
@@ -0,0 +1,236 @@
+// 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.
+
+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 implementation.
+///
+/// # Mutability
+///
+/// Unlike `MutexGuard`, `ReentrantMutexGuard` does not implement `DerefMut`,
+/// because implementation of the trait would violate Rust’s reference aliasing
+/// rules. Use interior mutability (usually `RefCell`) in order to mutate the
+/// guarded data.
+#[must_use]
+pub struct ReentrantMutexGuard<'a, T: 'a> {
+ // funny underscores due to how Deref currently works (it disregards 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> {
+ unsafe {
+ let mut mutex = ReentrantMutex {
+ inner: box sys::ReentrantMutex::uninitialized(),
+ poison: poison::Flag::new(),
+ data: t,
+ };
+ mutex.inner.init();
+ mutex
+ }
+ }
+
+ /// 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.
+ ///
+ /// # Errors
+ ///
+ /// 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.
+ ///
+ /// # Errors
+ ///
+ /// 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(ReentrantMutexGuard::new(&self)?)
+ } else {
+ Err(TryLockError::WouldBlock)
+ }
+ }
+}
+
+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(&self) -> &T {
+ &self.__lock.data
+ }
+}
+
+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(all(test, not(target_os = "emscripten")))]
+mod tests {
+ use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
+ use cell::RefCell;
+ use sync::Arc;
+ 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 = Arc::new(ReentrantMutex::new(RefCell::new(0)));
+ let m2 = m.clone();
+ let lock = m.lock().unwrap();
+ let child = thread::spawn(move || {
+ let lock = m2.lock().unwrap();
+ assert_eq!(*lock.borrow(), 4950);
+ });
+ for i in 0..100 {
+ let lock = m.lock().unwrap();
+ *lock.borrow_mut() += i;
+ }
+ drop(lock);
+ child.join().unwrap();
+ }
+
+ #[test]
+ fn trylock_works() {
+ let m = Arc::new(ReentrantMutex::new(()));
+ let m2 = m.clone();
+ let _lock = m.try_lock().unwrap();
+ let _lock2 = m.try_lock().unwrap();
+ thread::spawn(move || {
+ let lock = m2.try_lock();
+ assert!(lock.is_err());
+ }).join().unwrap();
+ 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?");
+ }).join();
+ assert!(result.is_err());
+ let r = m.lock().err().unwrap().into_inner();
+ assert_eq!(*r.borrow(), 42);
+ }
+}
diff --git a/ctr-std/src/thread/mod.rs b/ctr-std/src/thread/mod.rs
index 9b8f76c..8b27bed 100644
--- a/ctr-std/src/thread/mod.rs
+++ b/ctr-std/src/thread/mod.rs
@@ -193,3 +193,8 @@ pub use self::local::{LocalKey, LocalKeyState};
#[doc(hidden)] pub use sys::fast_thread_local::Key as __FastLocalKeyInner;
#[unstable(feature = "libstd_thread_internals", issue = "0")]
#[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner;
+
+// We don't have stack unwinding, so this should always be false
+pub fn panicking() -> bool {
+ false
+}