Fenrir
8 years ago
17 changed files with 3400 additions and 7 deletions
@ -0,0 +1,394 @@
@@ -0,0 +1,394 @@
|
||||
// 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.
|
||||
|
||||
//! Panic support in the standard library
|
||||
|
||||
#![stable(feature = "std_panic", since = "1.9.0")] |
||||
|
||||
use any::Any; |
||||
use cell::UnsafeCell; |
||||
use fmt; |
||||
use ops::{Deref, DerefMut}; |
||||
use panicking; |
||||
use ptr::{Unique, Shared}; |
||||
use rc::Rc; |
||||
use sync::{Arc, Mutex, RwLock, atomic}; |
||||
use thread::Result; |
||||
|
||||
//#[stable(feature = "panic_hooks", since = "1.10.0")]
|
||||
//pub use panicking::{take_hook, set_hook, PanicInfo, Location};
|
||||
|
||||
/// A marker trait which represents "panic safe" types in Rust.
|
||||
///
|
||||
/// This trait is implemented by default for many types and behaves similarly in
|
||||
/// terms of inference of implementation to the `Send` and `Sync` traits. The
|
||||
/// purpose of this trait is to encode what types are safe to cross a `catch_unwind`
|
||||
/// boundary with no fear of unwind safety.
|
||||
///
|
||||
/// ## What is unwind safety?
|
||||
///
|
||||
/// In Rust a function can "return" early if it either panics or calls a
|
||||
/// function which transitively panics. This sort of control flow is not always
|
||||
/// anticipated, and has the possibility of causing subtle bugs through a
|
||||
/// combination of two cricial components:
|
||||
///
|
||||
/// 1. A data structure is in a temporarily invalid state when the thread
|
||||
/// panics.
|
||||
/// 2. This broken invariant is then later observed.
|
||||
///
|
||||
/// Typically in Rust, it is difficult to perform step (2) because catching a
|
||||
/// panic involves either spawning a thread (which in turns makes it difficult
|
||||
/// to later witness broken invariants) or using the `catch_unwind` function in this
|
||||
/// module. Additionally, even if an invariant is witnessed, it typically isn't a
|
||||
/// problem in Rust because there are no uninitialized values (like in C or C++).
|
||||
///
|
||||
/// It is possible, however, for **logical** invariants to be broken in Rust,
|
||||
/// which can end up causing behavioral bugs. Another key aspect of unwind safety
|
||||
/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to
|
||||
/// memory unsafety.
|
||||
///
|
||||
/// That was a bit of a whirlwind tour of unwind safety, but for more information
|
||||
/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc].
|
||||
///
|
||||
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
|
||||
///
|
||||
/// ## What is `UnwindSafe`?
|
||||
///
|
||||
/// Now that we've got an idea of what unwind safety is in Rust, it's also
|
||||
/// important to understand what this trait represents. As mentioned above, one
|
||||
/// way to witness broken invariants is through the `catch_unwind` function in this
|
||||
/// module as it allows catching a panic and then re-using the environment of
|
||||
/// the closure.
|
||||
///
|
||||
/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow
|
||||
/// witnessing a broken invariant through the use of `catch_unwind` (catching a
|
||||
/// panic). This trait is a marker trait, so it is automatically implemented for
|
||||
/// many types, and it is also structurally composed (e.g. a struct is unwind
|
||||
/// safe if all of its components are unwind safe).
|
||||
///
|
||||
/// Note, however, that this is not an unsafe trait, so there is not a succinct
|
||||
/// contract that this trait is providing. Instead it is intended as more of a
|
||||
/// "speed bump" to alert users of `catch_unwind` that broken invariants may be
|
||||
/// witnessed and may need to be accounted for.
|
||||
///
|
||||
/// ## Who implements `UnwindSafe`?
|
||||
///
|
||||
/// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
|
||||
/// unwind safe. The general idea is that any mutable state which can be shared
|
||||
/// across `catch_unwind` is not unwind safe by default. This is because it is very
|
||||
/// easy to witness a broken invariant outside of `catch_unwind` as the data is
|
||||
/// simply accessed as usual.
|
||||
///
|
||||
/// Types like `&Mutex<T>`, however, are unwind safe because they implement
|
||||
/// poisoning by default. They still allow witnessing a broken invariant, but
|
||||
/// they already provide their own "speed bumps" to do so.
|
||||
///
|
||||
/// ## When should `UnwindSafe` be used?
|
||||
///
|
||||
/// Is not intended that most types or functions need to worry about this trait.
|
||||
/// It is only used as a bound on the `catch_unwind` function and as mentioned above,
|
||||
/// the lack of `unsafe` means it is mostly an advisory. The `AssertUnwindSafe`
|
||||
/// wrapper struct in this module can be used to force this trait to be
|
||||
/// implemented for any closed over variables passed to the `catch_unwind` function
|
||||
/// (more on this below).
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \ |
||||
across an unwind boundary"] |
||||
pub trait UnwindSafe {} |
||||
|
||||
/// A marker trait representing types where a shared reference is considered
|
||||
/// unwind safe.
|
||||
///
|
||||
/// This trait is namely not implemented by `UnsafeCell`, the root of all
|
||||
/// interior mutability.
|
||||
///
|
||||
/// This is a "helper marker trait" used to provide impl blocks for the
|
||||
/// `UnwindSafe` trait, for more information see that documentation.
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
#[rustc_on_unimplemented = "the type {Self} contains interior mutability \ |
||||
and a reference may not be safely transferrable \ |
||||
across a catch_unwind boundary"] |
||||
pub trait RefUnwindSafe {} |
||||
|
||||
/// A simple wrapper around a type to assert that it is unwind safe.
|
||||
///
|
||||
/// When using `catch_unwind` it may be the case that some of the closed over
|
||||
/// variables are not unwind safe. For example if `&mut T` is captured the
|
||||
/// compiler will generate a warning indicating that it is not unwind safe. It
|
||||
/// may not be the case, however, that this is actually a problem due to the
|
||||
/// specific usage of `catch_unwind` if unwind safety is specifically taken into
|
||||
/// account. This wrapper struct is useful for a quick and lightweight
|
||||
/// annotation that a variable is indeed unwind safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// One way to use `AssertUnwindSafe` is to assert that the entire closure
|
||||
/// itself is unwind safe, bypassing all checks for all variables:
|
||||
///
|
||||
/// ```
|
||||
/// use std::panic::{self, AssertUnwindSafe};
|
||||
///
|
||||
/// let mut variable = 4;
|
||||
///
|
||||
/// // This code will not compile because the closure captures `&mut variable`
|
||||
/// // which is not considered unwind safe by default.
|
||||
///
|
||||
/// // panic::catch_unwind(|| {
|
||||
/// // variable += 3;
|
||||
/// // });
|
||||
///
|
||||
/// // This, however, will compile due to the `AssertUnwindSafe` wrapper
|
||||
/// let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
/// variable += 3;
|
||||
/// }));
|
||||
/// // ...
|
||||
/// ```
|
||||
///
|
||||
/// Wrapping the entire closure amounts to a blanket assertion that all captured
|
||||
/// variables are unwind safe. This has the downside that if new captures are
|
||||
/// added in the future, they will also be considered unwind safe. Therefore,
|
||||
/// you may prefer to just wrap individual captures, as shown below. This is
|
||||
/// more annotation, but it ensures that if a new capture is added which is not
|
||||
/// unwind safe, you will get a compilation error at that time, which will
|
||||
/// allow you to consider whether that new capture in fact represent a bug or
|
||||
/// not.
|
||||
///
|
||||
/// ```
|
||||
/// use std::panic::{self, AssertUnwindSafe};
|
||||
///
|
||||
/// let mut variable = 4;
|
||||
/// let other_capture = 3;
|
||||
///
|
||||
/// let result = {
|
||||
/// let mut wrapper = AssertUnwindSafe(&mut variable);
|
||||
/// panic::catch_unwind(move || {
|
||||
/// **wrapper += other_capture;
|
||||
/// })
|
||||
/// };
|
||||
/// // ...
|
||||
/// ```
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
pub struct AssertUnwindSafe<T>( |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
pub T |
||||
); |
||||
|
||||
// Implementations of the `UnwindSafe` trait:
|
||||
//
|
||||
// * By default everything is unwind safe
|
||||
// * pointers T contains mutability of some form are not unwind safe
|
||||
// * Unique, an owning pointer, lifts an implementation
|
||||
// * Types like Mutex/RwLock which are explicilty poisoned are unwind safe
|
||||
// * Our custom AssertUnwindSafe wrapper is indeed unwind safe
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl UnwindSafe for .. {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<'a, T: ?Sized> !UnwindSafe for &'a mut T {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<'a, T: RefUnwindSafe + ?Sized> UnwindSafe for &'a T {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *const T {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *mut T {} |
||||
#[unstable(feature = "unique", issue = "27730")] |
||||
impl<T: UnwindSafe + ?Sized> UnwindSafe for Unique<T> {} |
||||
#[unstable(feature = "shared", issue = "27730")] |
||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Shared<T> {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T: ?Sized> UnwindSafe for Mutex<T> {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T: ?Sized> UnwindSafe for RwLock<T> {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T> UnwindSafe for AssertUnwindSafe<T> {} |
||||
|
||||
// not covered via the Shared impl above b/c the inner contents use
|
||||
// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the
|
||||
// impl up one level to Arc/Rc itself
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Arc<T> {} |
||||
|
||||
// Pretty simple implementations for the `RefUnwindSafe` marker trait,
|
||||
// basically just saying that this is a marker trait and `UnsafeCell` is the
|
||||
// only thing which doesn't implement it (which then transitively applies to
|
||||
// everything else).
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl RefUnwindSafe for .. {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T: ?Sized> !RefUnwindSafe for UnsafeCell<T> {} |
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T> RefUnwindSafe for AssertUnwindSafe<T> {} |
||||
|
||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] |
||||
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {} |
||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] |
||||
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {} |
||||
|
||||
#[cfg(target_has_atomic = "ptr")] |
||||
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] |
||||
impl RefUnwindSafe for atomic::AtomicIsize {} |
||||
#[cfg(target_has_atomic = "8")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicI8 {} |
||||
#[cfg(target_has_atomic = "16")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicI16 {} |
||||
#[cfg(target_has_atomic = "32")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicI32 {} |
||||
#[cfg(target_has_atomic = "64")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicI64 {} |
||||
|
||||
#[cfg(target_has_atomic = "ptr")] |
||||
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] |
||||
impl RefUnwindSafe for atomic::AtomicUsize {} |
||||
#[cfg(target_has_atomic = "8")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicU8 {} |
||||
#[cfg(target_has_atomic = "16")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicU16 {} |
||||
#[cfg(target_has_atomic = "32")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicU32 {} |
||||
#[cfg(target_has_atomic = "64")] |
||||
#[unstable(feature = "integer_atomics", issue = "32976")] |
||||
impl RefUnwindSafe for atomic::AtomicU64 {} |
||||
|
||||
#[cfg(target_has_atomic = "8")] |
||||
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] |
||||
impl RefUnwindSafe for atomic::AtomicBool {} |
||||
|
||||
#[cfg(target_has_atomic = "ptr")] |
||||
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] |
||||
impl<T> RefUnwindSafe for atomic::AtomicPtr<T> {} |
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T> Deref for AssertUnwindSafe<T> { |
||||
type Target = T; |
||||
|
||||
fn deref(&self) -> &T { |
||||
&self.0 |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<T> DerefMut for AssertUnwindSafe<T> { |
||||
fn deref_mut(&mut self) -> &mut T { |
||||
&mut self.0 |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> { |
||||
type Output = R; |
||||
|
||||
extern "rust-call" fn call_once(self, _args: ()) -> R { |
||||
(self.0)() |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")] |
||||
impl<T: fmt::Debug> fmt::Debug for AssertUnwindSafe<T> { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
f.debug_tuple("AssertUnwindSafe") |
||||
.field(&self.0) |
||||
.finish() |
||||
} |
||||
} |
||||
|
||||
/// Invokes a closure, capturing the cause of an unwinding panic if one occurs.
|
||||
///
|
||||
/// This function will return `Ok` with the closure's result if the closure
|
||||
/// does not panic, and will return `Err(cause)` if the closure panics. The
|
||||
/// `cause` returned is the object with which panic was originally invoked.
|
||||
///
|
||||
/// It is currently undefined behavior to unwind from Rust code into foreign
|
||||
/// code, so this function is particularly useful when Rust is called from
|
||||
/// another language (normally C). This can run arbitrary Rust code, capturing a
|
||||
/// panic and allowing a graceful handling of the error.
|
||||
///
|
||||
/// It is **not** recommended to use this function for a general try/catch
|
||||
/// mechanism. The `Result` type is more appropriate to use for functions that
|
||||
/// can fail on a regular basis. Additionally, this function is not guaranteed
|
||||
/// to catch all panics, see the "Notes" section below.
|
||||
///
|
||||
/// The closure provided is required to adhere to the `UnwindSafe` trait to ensure
|
||||
/// that all captured variables are safe to cross this boundary. The purpose of
|
||||
/// this bound is to encode the concept of [exception safety][rfc] in the type
|
||||
/// system. Most usage of this function should not need to worry about this
|
||||
/// bound as programs are naturally unwind safe without `unsafe` code. If it
|
||||
/// becomes a problem the associated `AssertUnwindSafe` wrapper type in this
|
||||
/// module can be used to quickly assert that the usage here is indeed unwind
|
||||
/// safe.
|
||||
///
|
||||
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Note that this function **may not catch all panics** in Rust. A panic in
|
||||
/// Rust is not always implemented via unwinding, but can be implemented by
|
||||
/// aborting the process as well. This function *only* catches unwinding panics,
|
||||
/// not those that abort the process.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::panic;
|
||||
///
|
||||
/// let result = panic::catch_unwind(|| {
|
||||
/// println!("hello!");
|
||||
/// });
|
||||
/// assert!(result.is_ok());
|
||||
///
|
||||
/// let result = panic::catch_unwind(|| {
|
||||
/// panic!("oh no!");
|
||||
/// });
|
||||
/// assert!(result.is_err());
|
||||
/// ```
|
||||
#[stable(feature = "catch_unwind", since = "1.9.0")] |
||||
pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> { |
||||
unsafe { |
||||
panicking::try(f) |
||||
} |
||||
} |
||||
|
||||
/// Triggers a panic without invoking the panic hook.
|
||||
///
|
||||
/// This is designed to be used in conjunction with `catch_unwind` to, for
|
||||
/// example, carry a panic across a layer of C code.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Note that panics in Rust are not always implemented via unwinding, but they
|
||||
/// may be implemented by aborting the process. If this function is called when
|
||||
/// panics are implemented this way then this function will abort the process,
|
||||
/// not trigger an unwind.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use std::panic;
|
||||
///
|
||||
/// let result = panic::catch_unwind(|| {
|
||||
/// panic!("oh no!");
|
||||
/// });
|
||||
///
|
||||
/// if let Err(err) = result {
|
||||
/// panic::resume_unwind(err);
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "resume_unwind", since = "1.9.0")] |
||||
// we always abort so I'm pretty sure there's no reason to ever call this
|
||||
pub fn resume_unwind(_payload: Box<Any + Send>) -> ! { |
||||
unimplemented!() |
||||
} |
@ -0,0 +1,589 @@
@@ -0,0 +1,589 @@
|
||||
// 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 fmt; |
||||
use sync::atomic::{AtomicUsize, Ordering}; |
||||
use sync::{mutex, MutexGuard, PoisonError}; |
||||
use sys_common::condvar as sys; |
||||
use sys_common::mutex as sys_mutex; |
||||
use sys_common::poison::{self, LockResult}; |
||||
use time::Duration; |
||||
|
||||
/// A type indicating whether a timed wait on a condition variable returned
|
||||
/// due to a time out or not.
|
||||
///
|
||||
/// It is returned by the [`wait_timeout`] method.
|
||||
///
|
||||
/// [`wait_timeout`]: struct.Condvar.html#method.wait_timeout
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
||||
#[stable(feature = "wait_timeout", since = "1.5.0")] |
||||
pub struct WaitTimeoutResult(bool); |
||||
|
||||
impl WaitTimeoutResult { |
||||
/// Returns whether the wait was known to have timed out.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This example spawns a thread which will update the boolean value and
|
||||
/// then wait 100 milliseconds before notifying the condvar.
|
||||
///
|
||||
/// The main thread will wait with a timeout on the condvar and then leave
|
||||
/// once the boolean has been updated and notified.
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = pair.clone();
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// let &(ref lock, ref cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // We update the boolean value.
|
||||
/// *started = true;
|
||||
/// // Let's wait 20 milliseconds before notifying the condvar.
|
||||
/// thread::sleep(Duration::from_millis(20));
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let &(ref lock, ref cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// loop {
|
||||
/// // Let's put a timeout on the condvar's wait.
|
||||
/// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap();
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")] |
||||
pub fn timed_out(&self) -> bool { |
||||
self.0 |
||||
} |
||||
} |
||||
|
||||
/// A Condition Variable
|
||||
///
|
||||
/// Condition variables represent the ability to block a thread such that it
|
||||
/// consumes no CPU time while waiting for an event to occur. Condition
|
||||
/// variables are typically associated with a boolean predicate (a condition)
|
||||
/// and a mutex. The predicate is always verified inside of the mutex before
|
||||
/// determining that a thread must block.
|
||||
///
|
||||
/// Functions in this module will block the current **thread** of execution and
|
||||
/// are bindings to system-provided condition variables where possible. Note
|
||||
/// that this module places one additional restriction over the system condition
|
||||
/// variables: each condvar can be used with precisely one mutex at runtime. Any
|
||||
/// attempt to use multiple mutexes on the same condition variable will result
|
||||
/// in a runtime panic. If this is not desired, then the unsafe primitives in
|
||||
/// `sys` do not have this restriction but may result in undefined behavior.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = pair.clone();
|
||||
///
|
||||
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
/// thread::spawn(move|| {
|
||||
/// let &(ref lock, ref cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let &(ref lock, ref cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub struct Condvar { |
||||
inner: Box<sys::Condvar>, |
||||
mutex: AtomicUsize, |
||||
} |
||||
|
||||
impl Condvar { |
||||
/// Creates a new condition variable which is ready to be waited on and
|
||||
/// notified.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Condvar;
|
||||
///
|
||||
/// let condvar = Condvar::new();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn new() -> Condvar { |
||||
let mut c = Condvar { |
||||
inner: box sys::Condvar::new(), |
||||
mutex: AtomicUsize::new(0), |
||||
}; |
||||
unsafe { |
||||
c.inner.init(); |
||||
} |
||||
c |
||||
} |
||||
|
||||
/// Blocks the current thread until this condition variable receives a
|
||||
/// notification.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one()`] or [`notify_all()`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// Note that this function is susceptible to spurious wakeups. Condition
|
||||
/// variables normally have a boolean predicate associated with them, and
|
||||
/// the predicate must always be checked each time this function returns to
|
||||
/// protect against spurious wakeups.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the mutex being waited on is
|
||||
/// poisoned when this thread re-acquires the lock. For more information,
|
||||
/// see information about [poisoning] on the [`Mutex`] type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will [`panic!()`] if it is used with more than one mutex
|
||||
/// over time. Each condition variable is dynamically bound to exactly one
|
||||
/// mutex to ensure defined behavior across platforms. If this functionality
|
||||
/// is not desired, then unsafe primitives in `sys` are provided.
|
||||
///
|
||||
/// [`notify_one()`]: #method.notify_one
|
||||
/// [`notify_all()`]: #method.notify_all
|
||||
/// [poisoning]: ../sync/struct.Mutex.html#poisoning
|
||||
/// [`Mutex`]: ../sync/struct.Mutex.html
|
||||
/// [`panic!()`]: ../../std/macro.panic.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = pair.clone();
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// let &(ref lock, ref cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let &(ref lock, ref cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex` is false, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) |
||||
-> LockResult<MutexGuard<'a, T>> { |
||||
let poisoned = unsafe { |
||||
let lock = mutex::guard_lock(&guard); |
||||
self.verify(lock); |
||||
self.inner.wait(lock); |
||||
mutex::guard_poison(&guard).get() |
||||
}; |
||||
if poisoned { |
||||
Err(PoisonError::new(guard)) |
||||
} else { |
||||
Ok(guard) |
||||
} |
||||
} |
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`]
|
||||
/// except that the thread will be blocked for roughly no longer
|
||||
/// than `ms` milliseconds. This method should not be used for
|
||||
/// precise timing due to anomalies such as preemption or platform
|
||||
/// differences that may not cause the maximum amount of time
|
||||
/// waited to be precisely `ms`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned boolean is `false` only if the timeout is known
|
||||
/// to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will be re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: #method.wait
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = pair.clone();
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// let &(ref lock, ref cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let &(ref lock, ref cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex` is false, we wait.
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout_ms(started, 10).unwrap();
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
#[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::sync::Condvar::wait_timeout`")] |
||||
pub fn wait_timeout_ms<'a, T>(&self, guard: MutexGuard<'a, T>, ms: u32) |
||||
-> LockResult<(MutexGuard<'a, T>, bool)> { |
||||
let res = self.wait_timeout(guard, Duration::from_millis(ms as u64)); |
||||
poison::map_result(res, |(a, b)| { |
||||
(a, !b.timed_out()) |
||||
}) |
||||
} |
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`] except that
|
||||
/// the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that may not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will be re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: #method.wait
|
||||
/// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = pair.clone();
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// let &(ref lock, ref cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let &(ref lock, ref cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // as long as the value inside the `Mutex` is false, we wait
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap();
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")] |
||||
pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, |
||||
dur: Duration) |
||||
-> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { |
||||
let (poisoned, result) = unsafe { |
||||
let lock = mutex::guard_lock(&guard); |
||||
self.verify(lock); |
||||
let success = self.inner.wait_timeout(lock, dur); |
||||
(mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) |
||||
}; |
||||
if poisoned { |
||||
Err(PoisonError::new((guard, result))) |
||||
} else { |
||||
Ok((guard, result)) |
||||
} |
||||
} |
||||
|
||||
/// Wakes up one blocked thread on this condvar.
|
||||
///
|
||||
/// If there is a blocked thread on this condition variable, then it will
|
||||
/// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
|
||||
/// `notify_one` are not buffered in any way.
|
||||
///
|
||||
/// To wake up all threads, see [`notify_all()`].
|
||||
///
|
||||
/// [`wait`]: #method.wait
|
||||
/// [`wait_timeout`]: #method.wait_timeout
|
||||
/// [`notify_all()`]: #method.notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = pair.clone();
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// let &(ref lock, ref cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let &(ref lock, ref cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex` is false, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn notify_one(&self) { |
||||
unsafe { self.inner.notify_one() } |
||||
} |
||||
|
||||
/// Wakes up all blocked threads on this condvar.
|
||||
///
|
||||
/// This method will ensure that any current waiters on the condition
|
||||
/// variable are awoken. Calls to `notify_all()` are not buffered in any
|
||||
/// way.
|
||||
///
|
||||
/// To wake up only one thread, see [`notify_one()`].
|
||||
///
|
||||
/// [`notify_one()`]: #method.notify_one
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = pair.clone();
|
||||
///
|
||||
/// thread::spawn(move|| {
|
||||
/// let &(ref lock, ref cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_all();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let &(ref lock, ref cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex` is false, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn notify_all(&self) { |
||||
unsafe { self.inner.notify_all() } |
||||
} |
||||
|
||||
fn verify(&self, mutex: &sys_mutex::Mutex) { |
||||
let addr = mutex as *const _ as usize; |
||||
match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) { |
||||
// If we got out 0, then we have successfully bound the mutex to
|
||||
// this cvar.
|
||||
0 => {} |
||||
|
||||
// If we get out a value that's the same as `addr`, then someone
|
||||
// already beat us to the punch.
|
||||
n if n == addr => {} |
||||
|
||||
// Anything else and we're using more than one mutex on this cvar,
|
||||
// which is currently disallowed.
|
||||
_ => panic!("attempted to use a condition variable with two \ |
||||
mutexes"), |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")] |
||||
impl fmt::Debug for Condvar { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
f.pad("Condvar { .. }") |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "condvar_default", since = "1.9.0")] |
||||
impl Default for Condvar { |
||||
/// Creates a `Condvar` which is ready to be waited on and notified.
|
||||
fn default() -> Condvar { |
||||
Condvar::new() |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl Drop for Condvar { |
||||
fn drop(&mut self) { |
||||
unsafe { self.inner.destroy() } |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use sync::mpsc::channel; |
||||
use sync::{Condvar, Mutex, Arc}; |
||||
use thread; |
||||
use time::Duration; |
||||
use u32; |
||||
|
||||
#[test] |
||||
fn smoke() { |
||||
let c = Condvar::new(); |
||||
c.notify_one(); |
||||
c.notify_all(); |
||||
} |
||||
|
||||
#[test] |
||||
#[cfg_attr(target_os = "emscripten", ignore)] |
||||
fn notify_one() { |
||||
let m = Arc::new(Mutex::new(())); |
||||
let m2 = m.clone(); |
||||
let c = Arc::new(Condvar::new()); |
||||
let c2 = c.clone(); |
||||
|
||||
let g = m.lock().unwrap(); |
||||
let _t = thread::spawn(move|| { |
||||
let _g = m2.lock().unwrap(); |
||||
c2.notify_one(); |
||||
}); |
||||
let g = c.wait(g).unwrap(); |
||||
drop(g); |
||||
} |
||||
|
||||
#[test] |
||||
#[cfg_attr(target_os = "emscripten", ignore)] |
||||
fn notify_all() { |
||||
const N: usize = 10; |
||||
|
||||
let data = Arc::new((Mutex::new(0), Condvar::new())); |
||||
let (tx, rx) = channel(); |
||||
for _ in 0..N { |
||||
let data = data.clone(); |
||||
let tx = tx.clone(); |
||||
thread::spawn(move|| { |
||||
let &(ref lock, ref cond) = &*data; |
||||
let mut cnt = lock.lock().unwrap(); |
||||
*cnt += 1; |
||||
if *cnt == N { |
||||
tx.send(()).unwrap(); |
||||
} |
||||
while *cnt != 0 { |
||||
cnt = cond.wait(cnt).unwrap(); |
||||
} |
||||
tx.send(()).unwrap(); |
||||
}); |
||||
} |
||||
drop(tx); |
||||
|
||||
let &(ref lock, ref cond) = &*data; |
||||
rx.recv().unwrap(); |
||||
let mut cnt = lock.lock().unwrap(); |
||||
*cnt = 0; |
||||
cond.notify_all(); |
||||
drop(cnt); |
||||
|
||||
for _ in 0..N { |
||||
rx.recv().unwrap(); |
||||
} |
||||
} |
||||
|
||||
#[test] |
||||
#[cfg_attr(target_os = "emscripten", ignore)] |
||||
fn wait_timeout_ms() { |
||||
let m = Arc::new(Mutex::new(())); |
||||
let m2 = m.clone(); |
||||
let c = Arc::new(Condvar::new()); |
||||
let c2 = c.clone(); |
||||
|
||||
let g = m.lock().unwrap(); |
||||
let (g, _no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); |
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// assert!(!no_timeout);
|
||||
let _t = thread::spawn(move || { |
||||
let _g = m2.lock().unwrap(); |
||||
c2.notify_one(); |
||||
}); |
||||
let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap(); |
||||
assert!(!timeout_res.timed_out()); |
||||
drop(g); |
||||
} |
||||
|
||||
#[test] |
||||
#[should_panic] |
||||
#[cfg_attr(target_os = "emscripten", ignore)] |
||||
fn two_mutexes() { |
||||
let m = Arc::new(Mutex::new(())); |
||||
let m2 = m.clone(); |
||||
let c = Arc::new(Condvar::new()); |
||||
let c2 = c.clone(); |
||||
|
||||
let mut g = m.lock().unwrap(); |
||||
let _t = thread::spawn(move|| { |
||||
let _g = m2.lock().unwrap(); |
||||
c2.notify_one(); |
||||
}); |
||||
g = c.wait(g).unwrap(); |
||||
drop(g); |
||||
|
||||
let m = Mutex::new(()); |
||||
let _ = c.wait(m.lock().unwrap()).unwrap(); |
||||
} |
||||
} |
@ -0,0 +1,666 @@
@@ -0,0 +1,666 @@
|
||||
// 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::poison::{self, LockResult, TryLockError, TryLockResult}; |
||||
use sys_common::rwlock as sys; |
||||
|
||||
/// A reader-writer lock
|
||||
///
|
||||
/// This type of lock allows a number of readers or at most one writer at any
|
||||
/// point in time. The write portion of this lock typically allows modification
|
||||
/// of the underlying data (exclusive access) and the read portion of this lock
|
||||
/// typically allows for read-only access (shared access).
|
||||
///
|
||||
/// The priority policy of the lock is dependent on the underlying operating
|
||||
/// system's implementation, and this type does not guarantee that any
|
||||
/// particular policy will be used.
|
||||
///
|
||||
/// The type parameter `T` represents the data that this lock protects. It is
|
||||
/// required that `T` satisfies `Send` to be shared across threads and `Sync` to
|
||||
/// allow concurrent access through readers. The RAII guards returned from the
|
||||
/// locking methods implement `Deref` (and `DerefMut` for the `write` methods)
|
||||
/// to allow access to the contained of the lock.
|
||||
///
|
||||
/// # Poisoning
|
||||
///
|
||||
/// An `RwLock`, like `Mutex`, will become poisoned on a panic. Note, however,
|
||||
/// that an `RwLock` may only be poisoned if a panic occurs while it is locked
|
||||
/// exclusively (write mode). If a panic occurs in any reader, then the lock
|
||||
/// will not be poisoned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let lock = RwLock::new(5);
|
||||
///
|
||||
/// // many reader locks can be held at once
|
||||
/// {
|
||||
/// let r1 = lock.read().unwrap();
|
||||
/// let r2 = lock.read().unwrap();
|
||||
/// assert_eq!(*r1, 5);
|
||||
/// assert_eq!(*r2, 5);
|
||||
/// } // read locks are dropped at this point
|
||||
///
|
||||
/// // only one write lock may be held, however
|
||||
/// {
|
||||
/// let mut w = lock.write().unwrap();
|
||||
/// *w += 1;
|
||||
/// assert_eq!(*w, 6);
|
||||
/// } // write lock is dropped here
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub struct RwLock<T: ?Sized> { |
||||
inner: Box<sys::RWLock>, |
||||
poison: poison::Flag, |
||||
data: UnsafeCell<T>, |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
unsafe impl<T: ?Sized + Send + Sync> Send for RwLock<T> {} |
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {} |
||||
|
||||
/// RAII structure used to release the shared read access of a lock when
|
||||
/// dropped.
|
||||
///
|
||||
/// This structure is created by the [`read()`] and [`try_read()`] methods on
|
||||
/// [`RwLock`].
|
||||
///
|
||||
/// [`read()`]: struct.RwLock.html#method.read
|
||||
/// [`try_read()`]: struct.RwLock.html#method.try_read
|
||||
/// [`RwLock`]: struct.RwLock.html
|
||||
#[must_use] |
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { |
||||
__lock: &'a RwLock<T>, |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl<'a, T: ?Sized> !marker::Send for RwLockReadGuard<'a, T> {} |
||||
|
||||
/// RAII structure used to release the exclusive write access of a lock when
|
||||
/// dropped.
|
||||
///
|
||||
/// This structure is created by the [`write()`] and [`try_write()`] methods
|
||||
/// on [`RwLock`].
|
||||
///
|
||||
/// [`write()`]: struct.RwLock.html#method.write
|
||||
/// [`try_write()`]: struct.RwLock.html#method.try_write
|
||||
/// [`RwLock`]: struct.RwLock.html
|
||||
#[must_use] |
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { |
||||
__lock: &'a RwLock<T>, |
||||
__poison: poison::Guard, |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl<'a, T: ?Sized> !marker::Send for RwLockWriteGuard<'a, T> {} |
||||
|
||||
impl<T> RwLock<T> { |
||||
/// Creates a new instance of an `RwLock<T>` which is unlocked.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let lock = RwLock::new(5);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn new(t: T) -> RwLock<T> { |
||||
RwLock { |
||||
inner: box sys::RWLock::new(), |
||||
poison: poison::Flag::new(), |
||||
data: UnsafeCell::new(t), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<T: ?Sized> RwLock<T> { |
||||
/// Locks this rwlock with shared read access, blocking the current thread
|
||||
/// until it can be acquired.
|
||||
///
|
||||
/// The calling thread will be blocked until there are no more writers which
|
||||
/// hold the lock. There may be other readers currently inside the lock when
|
||||
/// this method returns. This method does not provide any guarantees with
|
||||
/// respect to the ordering of whether contentious readers or writers will
|
||||
/// acquire the lock first.
|
||||
///
|
||||
/// Returns an RAII guard which will release this thread's shared access
|
||||
/// once it is dropped.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the RwLock is poisoned. An RwLock
|
||||
/// is poisoned whenever a writer panics while holding an exclusive lock.
|
||||
/// The failure will occur immediately after the lock has been acquired.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function might panic when called if the lock is already held by the current thread.
|
||||
#[inline] |
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn read(&self) -> LockResult<RwLockReadGuard<T>> { |
||||
unsafe { |
||||
self.inner.read(); |
||||
RwLockReadGuard::new(self) |
||||
} |
||||
} |
||||
|
||||
/// Attempts to acquire this rwlock with shared read access.
|
||||
///
|
||||
/// If the access could not be granted at this time, then `Err` is returned.
|
||||
/// Otherwise, an RAII guard is returned which will release the shared access
|
||||
/// when it is dropped.
|
||||
///
|
||||
/// This function does not block.
|
||||
///
|
||||
/// This function does not provide any guarantees with respect to the ordering
|
||||
/// of whether contentious readers or writers will acquire the lock first.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the RwLock is poisoned. An RwLock
|
||||
/// is poisoned whenever a writer panics while holding an exclusive lock. An
|
||||
/// error will only be returned if the lock would have otherwise been
|
||||
/// acquired.
|
||||
#[inline] |
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<T>> { |
||||
unsafe { |
||||
if self.inner.try_read() { |
||||
Ok(RwLockReadGuard::new(self)?) |
||||
} else { |
||||
Err(TryLockError::WouldBlock) |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Locks this rwlock with exclusive write access, blocking the current
|
||||
/// thread until it can be acquired.
|
||||
///
|
||||
/// This function will not return while other writers or other readers
|
||||
/// currently have access to the lock.
|
||||
///
|
||||
/// Returns an RAII guard which will drop the write access of this rwlock
|
||||
/// when dropped.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the RwLock is poisoned. An RwLock
|
||||
/// is poisoned whenever a writer panics while holding an exclusive lock.
|
||||
/// An error will be returned when the lock is acquired.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function might panic when called if the lock is already held by the current thread.
|
||||
#[inline] |
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> { |
||||
unsafe { |
||||
self.inner.write(); |
||||
RwLockWriteGuard::new(self) |
||||
} |
||||
} |
||||
|
||||
/// Attempts to lock this rwlock with exclusive write access.
|
||||
///
|
||||
/// If the lock could not be acquired at this time, then `Err` is returned.
|
||||
/// Otherwise, an RAII guard is returned which will release the lock when
|
||||
/// it is dropped.
|
||||
///
|
||||
/// This function does not block.
|
||||
///
|
||||
/// This function does not provide any guarantees with respect to the ordering
|
||||
/// of whether contentious readers or writers will acquire the lock first.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the RwLock is poisoned. An RwLock
|
||||
/// is poisoned whenever a writer panics while holding an exclusive lock. An
|
||||
/// error will only be returned if the lock would have otherwise been
|
||||
/// acquired.
|
||||
#[inline] |
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<T>> { |
||||
unsafe { |
||||
if self.inner.try_write() { |
||||
Ok(RwLockWriteGuard::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.
|
||||
#[inline] |
||||
#[stable(feature = "sync_poison", since = "1.2.0")] |
||||
pub fn is_poisoned(&self) -> bool { |
||||
self.poison.get() |
||||
} |
||||
|
||||
/// Consumes this `RwLock`, returning the underlying data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the RwLock is poisoned. An RwLock
|
||||
/// is poisoned whenever a writer panics while holding an exclusive lock. An
|
||||
/// error will only be returned if the lock would have otherwise been
|
||||
/// acquired.
|
||||
#[stable(feature = "rwlock_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 `RwLock` impl-s `Drop`, we can't move out of it, so
|
||||
// we'll have to destructure it manually instead.
|
||||
unsafe { |
||||
// Like `let RwLock { inner, poison, data } = self`.
|
||||
let (inner, poison, data) = { |
||||
let RwLock { 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 `RwLock` mutably, no actual locking needs to
|
||||
/// take place---the mutable borrow statically guarantees no locks exist.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the RwLock is poisoned. An RwLock
|
||||
/// is poisoned whenever a writer panics while holding an exclusive lock. An
|
||||
/// error will only be returned if the lock would have otherwise been
|
||||
/// acquired.
|
||||
#[stable(feature = "rwlock_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 RwLock<T> { |
||||
fn drop(&mut self) { |
||||
// IMPORTANT: This code needs to be kept in sync with `RwLock::into_inner`.
|
||||
unsafe { self.inner.destroy() } |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
match self.try_read() { |
||||
Ok(guard) => write!(f, "RwLock {{ data: {:?} }}", &*guard), |
||||
Err(TryLockError::Poisoned(err)) => { |
||||
write!(f, "RwLock {{ data: Poisoned({:?}) }}", &**err.get_ref()) |
||||
}, |
||||
Err(TryLockError::WouldBlock) => write!(f, "RwLock {{ <locked> }}") |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "rw_lock_default", since = "1.9.0")] |
||||
impl<T: Default> Default for RwLock<T> { |
||||
/// Creates a new `RwLock<T>`, with the `Default` value for T.
|
||||
fn default() -> RwLock<T> { |
||||
RwLock::new(Default::default()) |
||||
} |
||||
} |
||||
|
||||
impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { |
||||
unsafe fn new(lock: &'rwlock RwLock<T>) |
||||
-> LockResult<RwLockReadGuard<'rwlock, T>> { |
||||
poison::map_result(lock.poison.borrow(), |_| { |
||||
RwLockReadGuard { |
||||
__lock: lock, |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { |
||||
unsafe fn new(lock: &'rwlock RwLock<T>) |
||||
-> LockResult<RwLockWriteGuard<'rwlock, T>> { |
||||
poison::map_result(lock.poison.borrow(), |guard| { |
||||
RwLockWriteGuard { |
||||
__lock: lock, |
||||
__poison: guard, |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")] |
||||
impl<'a, T: fmt::Debug> fmt::Debug for RwLockReadGuard<'a, T> { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
f.debug_struct("RwLockReadGuard") |
||||
.field("lock", &self.__lock) |
||||
.finish() |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")] |
||||
impl<'a, T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'a, T> { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
f.debug_struct("RwLockWriteGuard") |
||||
.field("lock", &self.__lock) |
||||
.finish() |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl<'rwlock, T: ?Sized> Deref for RwLockReadGuard<'rwlock, T> { |
||||
type Target = T; |
||||
|
||||
fn deref(&self) -> &T { |
||||
unsafe { &*self.__lock.data.get() } |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl<'rwlock, T: ?Sized> Deref for RwLockWriteGuard<'rwlock, T> { |
||||
type Target = T; |
||||
|
||||
fn deref(&self) -> &T { |
||||
unsafe { &*self.__lock.data.get() } |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl<'rwlock, T: ?Sized> DerefMut for RwLockWriteGuard<'rwlock, 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 RwLockReadGuard<'a, T> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.__lock.inner.read_unlock(); } |
||||
} |
||||
} |
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")] |
||||
impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> { |
||||
fn drop(&mut self) { |
||||
self.__lock.poison.done(&self.__poison); |
||||
unsafe { self.__lock.inner.write_unlock(); } |
||||
} |
||||
} |
||||
|
||||
#[cfg(all(test, not(target_os = "emscripten")))] |
||||
mod tests { |
||||
#![allow(deprecated)] // rand
|
||||
|
||||
use rand::{self, Rng}; |
||||
use sync::mpsc::channel; |
||||
use thread; |
||||
use sync::{Arc, RwLock, TryLockError}; |
||||
use sync::atomic::{AtomicUsize, Ordering}; |
||||
|
||||
#[derive(Eq, PartialEq, Debug)] |
||||
struct NonCopy(i32); |
||||
|
||||
#[test] |
||||
fn smoke() { |
||||
let l = RwLock::new(()); |
||||
drop(l.read().unwrap()); |
||||
drop(l.write().unwrap()); |
||||
drop((l.read().unwrap(), l.read().unwrap())); |
||||
drop(l.write().unwrap()); |
||||
} |
||||
|
||||
#[test] |
||||
fn frob() { |
||||
const N: usize = 10; |
||||
const M: usize = 1000; |
||||
|
||||
let r = Arc::new(RwLock::new(())); |
||||
|
||||
let (tx, rx) = channel::<()>(); |
||||
for _ in 0..N { |
||||
let tx = tx.clone(); |
||||
let r = r.clone(); |
||||
thread::spawn(move || { |
||||
let mut rng = rand::thread_rng(); |
||||
for _ in 0..M { |
||||
if rng.gen_weighted_bool(N) { |
||||
drop(r.write().unwrap()); |
||||
} else { |
||||
drop(r.read().unwrap()); |
||||
} |
||||
} |
||||
drop(tx); |
||||
}); |
||||
} |
||||
drop(tx); |
||||
let _ = rx.recv(); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_rw_arc_poison_wr() { |
||||
let arc = Arc::new(RwLock::new(1)); |
||||
let arc2 = arc.clone(); |
||||
let _: Result<(), _> = thread::spawn(move|| { |
||||
let _lock = arc2.write().unwrap(); |
||||
panic!(); |
||||
}).join(); |
||||
assert!(arc.read().is_err()); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_rw_arc_poison_ww() { |
||||
let arc = Arc::new(RwLock::new(1)); |
||||
assert!(!arc.is_poisoned()); |
||||
let arc2 = arc.clone(); |
||||
let _: Result<(), _> = thread::spawn(move|| { |
||||
let _lock = arc2.write().unwrap(); |
||||
panic!(); |
||||
}).join(); |
||||
assert!(arc.write().is_err()); |
||||
assert!(arc.is_poisoned()); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_rw_arc_no_poison_rr() { |
||||
let arc = Arc::new(RwLock::new(1)); |
||||
let arc2 = arc.clone(); |
||||
let _: Result<(), _> = thread::spawn(move|| { |
||||
let _lock = arc2.read().unwrap(); |
||||
panic!(); |
||||
}).join(); |
||||
let lock = arc.read().unwrap(); |
||||
assert_eq!(*lock, 1); |
||||
} |
||||
#[test] |
||||
fn test_rw_arc_no_poison_rw() { |
||||
let arc = Arc::new(RwLock::new(1)); |
||||
let arc2 = arc.clone(); |
||||
let _: Result<(), _> = thread::spawn(move|| { |
||||
let _lock = arc2.read().unwrap(); |
||||
panic!() |
||||
}).join(); |
||||
let lock = arc.write().unwrap(); |
||||
assert_eq!(*lock, 1); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_rw_arc() { |
||||
let arc = Arc::new(RwLock::new(0)); |
||||
let arc2 = arc.clone(); |
||||
let (tx, rx) = channel(); |
||||
|
||||
thread::spawn(move|| { |
||||
let mut lock = arc2.write().unwrap(); |
||||
for _ in 0..10 { |
||||
let tmp = *lock; |
||||
*lock = -1; |
||||
thread::yield_now(); |
||||
*lock = tmp + 1; |
||||
} |
||||
tx.send(()).unwrap(); |
||||
}); |
||||
|
||||
// Readers try to catch the writer in the act
|
||||
let mut children = Vec::new(); |
||||
for _ in 0..5 { |
||||
let arc3 = arc.clone(); |
||||
children.push(thread::spawn(move|| { |
||||
let lock = arc3.read().unwrap(); |
||||
assert!(*lock >= 0); |
||||
})); |
||||
} |
||||
|
||||
// Wait for children to pass their asserts
|
||||
for r in children { |
||||
assert!(r.join().is_ok()); |
||||
} |
||||
|
||||
// Wait for writer to finish
|
||||
rx.recv().unwrap(); |
||||
let lock = arc.read().unwrap(); |
||||
assert_eq!(*lock, 10); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_rw_arc_access_in_unwind() { |
||||
let arc = Arc::new(RwLock::new(1)); |
||||
let arc2 = arc.clone(); |
||||
let _ = thread::spawn(move|| -> () { |
||||
struct Unwinder { |
||||
i: Arc<RwLock<isize>>, |
||||
} |
||||
impl Drop for Unwinder { |
||||
fn drop(&mut self) { |
||||
let mut lock = self.i.write().unwrap(); |
||||
*lock += 1; |
||||
} |
||||
} |
||||
let _u = Unwinder { i: arc2 }; |
||||
panic!(); |
||||
}).join(); |
||||
let lock = arc.read().unwrap(); |
||||
assert_eq!(*lock, 2); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_rwlock_unsized() { |
||||
let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]); |
||||
{ |
||||
let b = &mut *rw.write().unwrap(); |
||||
b[0] = 4; |
||||
b[2] = 5; |
||||
} |
||||
let comp: &[i32] = &[4, 2, 5]; |
||||
assert_eq!(&*rw.read().unwrap(), comp); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_rwlock_try_write() { |
||||
let lock = RwLock::new(0isize); |
||||
let read_guard = lock.read().unwrap(); |
||||
|
||||
let write_result = lock.try_write(); |
||||
match write_result { |
||||
Err(TryLockError::WouldBlock) => (), |
||||
Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"), |
||||
Err(_) => assert!(false, "unexpected error"), |
||||
} |
||||
|
||||
drop(read_guard); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_into_inner() { |
||||
let m = RwLock::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 = RwLock::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(RwLock::new(NonCopy(10))); |
||||
let m2 = m.clone(); |
||||
let _ = thread::spawn(move || { |
||||
let _lock = m2.write().unwrap(); |
||||
panic!("test panic in inner thread to poison RwLock"); |
||||
}).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 RwLock is Ok: {:?}", x), |
||||
} |
||||
} |
||||
|
||||
#[test] |
||||
fn test_get_mut() { |
||||
let mut m = RwLock::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(RwLock::new(NonCopy(10))); |
||||
let m2 = m.clone(); |
||||
let _ = thread::spawn(move || { |
||||
let _lock = m2.write().unwrap(); |
||||
panic!("test panic in inner thread to poison RwLock"); |
||||
}).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 RwLock is Ok: {:?}", x), |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// *Implementation adapted from `/sys/redox/condvar.rs`
|
||||
|
||||
use cell::UnsafeCell; |
||||
use intrinsics::atomic_cxchg; |
||||
use ptr; |
||||
use time::Duration; |
||||
|
||||
use sys::mutex::{self, Mutex}; |
||||
|
||||
use libctru::synchronization::{__sync_get_arbiter, LightLock}; |
||||
use libctru::svc::{svcArbitrateAddress, ArbitrationType}; |
||||
|
||||
pub struct Condvar { |
||||
lock: UnsafeCell<*mut LightLock>, |
||||
} |
||||
|
||||
unsafe impl Send for Condvar {} |
||||
unsafe impl Sync for Condvar {} |
||||
|
||||
impl Condvar { |
||||
pub const fn new() -> Condvar { |
||||
Condvar { |
||||
lock: UnsafeCell::new(ptr::null_mut()), |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn init(&self) { |
||||
*self.lock.get() = ptr::null_mut(); |
||||
} |
||||
|
||||
#[inline] |
||||
pub fn notify_one(&self) { |
||||
unsafe { |
||||
let arbiter = __sync_get_arbiter(); |
||||
|
||||
svcArbitrateAddress(arbiter, |
||||
*self.lock.get() as u32, |
||||
ArbitrationType::ARBITRATION_SIGNAL, |
||||
1, |
||||
0); |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
pub fn notify_all(&self) { |
||||
unsafe { |
||||
let lock = self.lock.get(); |
||||
|
||||
if *lock == ptr::null_mut() { |
||||
return; |
||||
} |
||||
|
||||
let arbiter = __sync_get_arbiter(); |
||||
|
||||
svcArbitrateAddress(arbiter, |
||||
*self.lock.get() as u32, |
||||
ArbitrationType::ARBITRATION_SIGNAL, |
||||
-1, |
||||
0); |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
pub fn wait(&self, mutex: &Mutex) { |
||||
unsafe { |
||||
let lock = self.lock.get(); |
||||
|
||||
if *lock != mutex::raw(mutex) { |
||||
if *lock != ptr::null_mut() { |
||||
panic!("Condvar used with more than one Mutex"); |
||||
} |
||||
|
||||
atomic_cxchg(lock as *mut usize, 0, mutex::raw(mutex) as usize); |
||||
} |
||||
|
||||
mutex.unlock(); |
||||
|
||||
let arbiter = __sync_get_arbiter(); |
||||
|
||||
svcArbitrateAddress(arbiter, |
||||
*self.lock.get() as u32, |
||||
ArbitrationType::ARBITRATION_WAIT_IF_LESS_THAN, |
||||
0, |
||||
0); |
||||
|
||||
mutex.lock(); |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
pub fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { |
||||
::sys_common::util::dumb_print(format_args!("condvar wait_timeout\n")); |
||||
unimplemented!(); |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn destroy(&self) { |
||||
*self.lock.get() = ptr::null_mut(); |
||||
} |
||||
} |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
// Copyright 2016 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 super::mutex::Mutex; |
||||
|
||||
pub struct RWLock { |
||||
mutex: Mutex |
||||
} |
||||
|
||||
unsafe impl Send for RWLock {} |
||||
unsafe impl Sync for RWLock {} |
||||
|
||||
impl RWLock { |
||||
pub const fn new() -> RWLock { |
||||
RWLock { |
||||
mutex: Mutex::new() |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn read(&self) { |
||||
self.mutex.lock(); |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn try_read(&self) -> bool { |
||||
self.mutex.try_lock() |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn write(&self) { |
||||
self.mutex.lock(); |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn try_write(&self) -> bool { |
||||
self.mutex.try_lock() |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn read_unlock(&self) { |
||||
self.mutex.unlock(); |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn write_unlock(&self) { |
||||
self.mutex.unlock(); |
||||
} |
||||
|
||||
#[inline] |
||||
pub unsafe fn destroy(&self) { |
||||
self.mutex.destroy(); |
||||
} |
||||
} |
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
// Copyright 2016 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 alloc::boxed::FnBox; |
||||
use libc; |
||||
use cmp; |
||||
use ffi::CStr; |
||||
use io; |
||||
use mem; |
||||
use ptr; |
||||
use sys_common::thread::start_thread; |
||||
use time::Duration; |
||||
|
||||
use libctru::svc::svcSleepThread; |
||||
use libctru::thread::{threadCreate, threadJoin, threadFree}; |
||||
use libctru::thread::Thread as ThreadHandle; |
||||
|
||||
pub struct Thread { |
||||
handle: ThreadHandle, |
||||
} |
||||
|
||||
// Some platforms may have pthread_t as a pointer in which case we still want
|
||||
// a thread to be Send/Sync
|
||||
unsafe impl Send for Thread {} |
||||
unsafe impl Sync for Thread {} |
||||
|
||||
impl Thread { |
||||
pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> { |
||||
let p = box p; |
||||
let stack_size = cmp::max(stack, 4 * 1024); |
||||
|
||||
let handle = threadCreate(Some(thread_func), &*p as *const _ as *mut _, |
||||
stack_size, 0x29, -2, 0); |
||||
|
||||
return if handle == ptr::null_mut() { |
||||
Err(io::Error::from_raw_os_error(libc::EAGAIN)) |
||||
} else { |
||||
mem::forget(p); // ownership passed to the new thread
|
||||
Ok(Thread { handle: handle }) |
||||
}; |
||||
|
||||
extern "C" fn thread_func(start: *mut libc::c_void) { |
||||
unsafe { start_thread(start) } |
||||
} |
||||
} |
||||
|
||||
pub fn yield_now() { |
||||
unimplemented!() |
||||
} |
||||
|
||||
pub fn set_name(_name: &CStr) { |
||||
// can't set thread names on the 3DS
|
||||
} |
||||
|
||||
pub fn sleep(dur: Duration) { |
||||
unsafe { |
||||
let nanos = dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64; |
||||
svcSleepThread(nanos as i64) |
||||
} |
||||
} |
||||
|
||||
pub fn join(self) { |
||||
unsafe { |
||||
let ret = threadJoin(self.handle, u64::max_value()); |
||||
threadFree(self.handle); |
||||
mem::forget(self); |
||||
debug_assert_eq!(ret, 0); |
||||
} |
||||
} |
||||
|
||||
pub fn id(&self) -> usize { |
||||
unimplemented!() |
||||
} |
||||
|
||||
pub fn into_id(self) -> usize { |
||||
unimplemented!() |
||||
} |
||||
} |
||||
|
||||
pub mod guard { |
||||
pub unsafe fn current() -> Option<usize> { None } |
||||
pub unsafe fn init() -> Option<usize> { None } |
||||
} |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
// 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 time::Duration; |
||||
use sys_common::mutex::{self, Mutex}; |
||||
use sys::condvar as imp; |
||||
|
||||
/// An OS-based condition variable.
|
||||
///
|
||||
/// This structure is the lowest layer possible on top of the OS-provided
|
||||
/// condition variables. It is consequently entirely unsafe to use. It is
|
||||
/// recommended to use the safer types at the top level of this crate instead of
|
||||
/// this type.
|
||||
pub struct Condvar(imp::Condvar); |
||||
|
||||
impl Condvar { |
||||
/// Creates a new condition variable for use.
|
||||
///
|
||||
/// Behavior is undefined if the condition variable is moved after it is
|
||||
/// first used with any of the functions below.
|
||||
pub const fn new() -> Condvar { Condvar(imp::Condvar::new()) } |
||||
|
||||
/// Prepares the condition variable for use.
|
||||
///
|
||||
/// This should be called once the condition variable is at a stable memory
|
||||
/// address.
|
||||
#[inline] |
||||
pub unsafe fn init(&mut self) { self.0.init() } |
||||
|
||||
/// Signals one waiter on this condition variable to wake up.
|
||||
#[inline] |
||||
pub unsafe fn notify_one(&self) { self.0.notify_one() } |
||||
|
||||
/// Awakens all current waiters on this condition variable.
|
||||
#[inline] |
||||
pub unsafe fn notify_all(&self) { self.0.notify_all() } |
||||
|
||||
/// Waits for a signal on the specified mutex.
|
||||
///
|
||||
/// Behavior is undefined if the mutex is not locked by the current thread.
|
||||
/// Behavior is also undefined if more than one mutex is used concurrently
|
||||
/// on this condition variable.
|
||||
#[inline] |
||||
pub unsafe fn wait(&self, mutex: &Mutex) { self.0.wait(mutex::raw(mutex)) } |
||||
|
||||
/// Waits for a signal on the specified mutex with a timeout duration
|
||||
/// specified by `dur` (a relative time into the future).
|
||||
///
|
||||
/// Behavior is undefined if the mutex is not locked by the current thread.
|
||||
/// Behavior is also undefined if more than one mutex is used concurrently
|
||||
/// on this condition variable.
|
||||
#[inline] |
||||
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { |
||||
self.0.wait_timeout(mutex::raw(mutex), dur) |
||||
} |
||||
|
||||
/// Deallocates all resources associated with this condition variable.
|
||||
///
|
||||
/// Behavior is undefined if there are current or will be future users of
|
||||
/// this condition variable.
|
||||
#[inline] |
||||
pub unsafe fn destroy(&self) { self.0.destroy() } |
||||
} |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
// 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::rwlock as imp; |
||||
|
||||
/// An OS-based reader-writer lock.
|
||||
///
|
||||
/// This structure is entirely unsafe and serves as the lowest layer of a
|
||||
/// cross-platform binding of system rwlocks. It is recommended to use the
|
||||
/// safer types at the top level of this crate instead of this type.
|
||||
pub struct RWLock(imp::RWLock); |
||||
|
||||
impl RWLock { |
||||
/// Creates a new reader-writer lock for use.
|
||||
///
|
||||
/// Behavior is undefined if the reader-writer lock is moved after it is
|
||||
/// first used with any of the functions below.
|
||||
pub const fn new() -> RWLock { RWLock(imp::RWLock::new()) } |
||||
|
||||
/// Acquires shared access to the underlying lock, blocking the current
|
||||
/// thread to do so.
|
||||
///
|
||||
/// Behavior is undefined if the rwlock has been moved between this and any
|
||||
/// previous method call.
|
||||
#[inline] |
||||
pub unsafe fn read(&self) { self.0.read() } |
||||
|
||||
/// Attempts to acquire shared access to this lock, returning whether it
|
||||
/// succeeded or not.
|
||||
///
|
||||
/// This function does not block the current thread.
|
||||
///
|
||||
/// Behavior is undefined if the rwlock has been moved between this and any
|
||||
/// previous method call.
|
||||
#[inline] |
||||
pub unsafe fn try_read(&self) -> bool { self.0.try_read() } |
||||
|
||||
/// Acquires write access to the underlying lock, blocking the current thread
|
||||
/// to do so.
|
||||
///
|
||||
/// Behavior is undefined if the rwlock has been moved between this and any
|
||||
/// previous method call.
|
||||
#[inline] |
||||
pub unsafe fn write(&self) { self.0.write() } |
||||
|
||||
/// Attempts to acquire exclusive access to this lock, returning whether it
|
||||
/// succeeded or not.
|
||||
///
|
||||
/// This function does not block the current thread.
|
||||
///
|
||||
/// Behavior is undefined if the rwlock has been moved between this and any
|
||||
/// previous method call.
|
||||
#[inline] |
||||
pub unsafe fn try_write(&self) -> bool { self.0.try_write() } |
||||
|
||||
/// Unlocks previously acquired shared access to this lock.
|
||||
///
|
||||
/// Behavior is undefined if the current thread does not have shared access.
|
||||
#[inline] |
||||
pub unsafe fn read_unlock(&self) { self.0.read_unlock() } |
||||
|
||||
/// Unlocks previously acquired exclusive access to this lock.
|
||||
///
|
||||
/// Behavior is undefined if the current thread does not currently have
|
||||
/// exclusive access.
|
||||
#[inline] |
||||
pub unsafe fn write_unlock(&self) { self.0.write_unlock() } |
||||
|
||||
/// Destroys OS-related resources with this RWLock.
|
||||
///
|
||||
/// Behavior is undefined if there are any currently active users of this
|
||||
/// lock.
|
||||
#[inline] |
||||
pub unsafe fn destroy(&self) { self.0.destroy() } |
||||
} |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
// 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 alloc::boxed::FnBox; |
||||
use libc; |
||||
//use sys::stack_overflow;
|
||||
|
||||
pub unsafe fn start_thread(main: *mut libc::c_void) { |
||||
// Next, set up our stack overflow handler which may get triggered if we run
|
||||
// out of stack.
|
||||
// let _handler = stack_overflow::Handler::new();
|
||||
|
||||
// Finally, let's run some code.
|
||||
Box::from_raw(main as *mut Box<FnBox()>)() |
||||
} |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
#![allow(dead_code)] // stack_guard isn't used right now on all platforms
|
||||
|
||||
use cell::RefCell; |
||||
use thread::Thread; |
||||
use thread::LocalKeyState; |
||||
|
||||
struct ThreadInfo { |
||||
stack_guard: Option<usize>, |
||||
thread: Thread, |
||||
} |
||||
|
||||
thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) } |
||||
|
||||
impl ThreadInfo { |
||||
fn with<R, F>(f: F) -> Option<R> where F: FnOnce(&mut ThreadInfo) -> R { |
||||
if THREAD_INFO.state() == LocalKeyState::Destroyed { |
||||
return None |
||||
} |
||||
|
||||
THREAD_INFO.with(move |c| { |
||||
if c.borrow().is_none() { |
||||
*c.borrow_mut() = Some(ThreadInfo { |
||||
stack_guard: None, |
||||
thread: NewThread::new(None), |
||||
}) |
||||
} |
||||
Some(f(c.borrow_mut().as_mut().unwrap())) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
pub fn current_thread() -> Option<Thread> { |
||||
ThreadInfo::with(|info| info.thread.clone()) |
||||
} |
||||
|
||||
pub fn stack_guard() -> Option<usize> { |
||||
ThreadInfo::with(|info| info.stack_guard).and_then(|o| o) |
||||
} |
||||
|
||||
pub fn set(stack_guard: Option<usize>, thread: Thread) { |
||||
THREAD_INFO.with(|c| assert!(c.borrow().is_none())); |
||||
THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{ |
||||
stack_guard: stack_guard, |
||||
thread: thread, |
||||
})); |
||||
} |
||||
|
||||
// a hack to get around privacy restrictions; implemented by `std::thread`
|
||||
pub trait NewThread { |
||||
fn new(name: Option<String>) -> Self; |
||||
} |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
use fmt; |
||||
use io::prelude::*; |
||||
use sync::atomic::{self, Ordering}; |
||||
use sys::stdio::Stderr; |
||||
|
||||
pub fn min_stack() -> usize { |
||||
static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0); |
||||
match MIN.load(Ordering::SeqCst) { |
||||
0 => {} |
||||
n => return n - 1, |
||||
} |
||||
|
||||
// NOTE: We don't have env variable support on the 3DS so let's just use the
|
||||
// default minimum
|
||||
|
||||
// let amt = env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok());
|
||||
// let amt = amt.unwrap_or(2 * 1024 * 1024);
|
||||
|
||||
let amt = 2 * 1024 * 1024; |
||||
|
||||
// 0 is our sentinel value, so ensure that we'll never see 0 after
|
||||
// initialization has run
|
||||
MIN.store(amt + 1, Ordering::SeqCst); |
||||
amt |
||||
} |
||||
|
||||
pub fn dumb_print(args: fmt::Arguments) { |
||||
let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args)); |
||||
} |
||||
|
||||
// Other platforms should use the appropriate platform-specific mechanism for
|
||||
// aborting the process. If no platform-specific mechanism is available,
|
||||
// ::intrinsics::abort() may be used instead. The above implementations cover
|
||||
// all targets currently supported by libstd.
|
||||
|
||||
pub fn abort(args: fmt::Arguments) -> ! { |
||||
dumb_print(format_args!("fatal runtime error: {}\n", args)); |
||||
unsafe { ::sys::abort_internal(); } |
||||
} |
Loading…
Reference in new issue