From 5299b505b79c48e788067d66a727636ff933de92 Mon Sep 17 00:00:00 2001 From: Fenrir Date: Sun, 5 Mar 2017 00:25:16 -0700 Subject: [PATCH] Initial thread support --- ctr-std/src/lib.rs | 9 + ctr-std/src/panic.rs | 394 +++++++++ ctr-std/src/panicking.rs | 111 ++- ctr-std/src/sync/condvar.rs | 589 ++++++++++++++ ctr-std/src/sync/mod.rs | 7 + ctr-std/src/sync/rwlock.rs | 666 +++++++++++++++ ctr-std/src/sys/unix/condvar.rs | 111 +++ ctr-std/src/sys/unix/mod.rs | 3 + ctr-std/src/sys/unix/rwlock.rs | 61 ++ ctr-std/src/sys/unix/thread.rs | 90 +++ ctr-std/src/sys_common/condvar.rs | 70 ++ ctr-std/src/sys_common/mod.rs | 5 + ctr-std/src/sys_common/rwlock.rs | 82 ++ ctr-std/src/sys_common/thread.rs | 22 + ctr-std/src/sys_common/thread_info.rs | 61 ++ ctr-std/src/sys_common/util.rs | 49 ++ ctr-std/src/thread/mod.rs | 1077 ++++++++++++++++++++++++- 17 files changed, 3400 insertions(+), 7 deletions(-) create mode 100644 ctr-std/src/panic.rs create mode 100644 ctr-std/src/sync/condvar.rs create mode 100644 ctr-std/src/sync/rwlock.rs create mode 100644 ctr-std/src/sys/unix/condvar.rs create mode 100644 ctr-std/src/sys/unix/rwlock.rs create mode 100644 ctr-std/src/sys/unix/thread.rs create mode 100644 ctr-std/src/sys_common/condvar.rs create mode 100644 ctr-std/src/sys_common/rwlock.rs create mode 100644 ctr-std/src/sys_common/thread.rs create mode 100644 ctr-std/src/sys_common/thread_info.rs create mode 100644 ctr-std/src/sys_common/util.rs diff --git a/ctr-std/src/lib.rs b/ctr-std/src/lib.rs index 307bb3c..9024871 100644 --- a/ctr-std/src/lib.rs +++ b/ctr-std/src/lib.rs @@ -1,6 +1,7 @@ #![feature(alloc)] #![feature(allow_internal_unstable)] #![feature(box_syntax)] +#![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(collections)] #![feature(collections_bound)] @@ -11,23 +12,30 @@ #![feature(char_escape_debug)] #![feature(dropck_eyepatch)] #![feature(float_extras)] +#![feature(fn_traits)] #![feature(fnbox)] #![feature(fused)] #![feature(generic_param_attrs)] #![feature(int_error_internals)] +#![feature(integer_atomics)] #![feature(lang_items)] #![feature(macro_reexport)] #![feature(oom)] +#![feature(on_unimplemented)] #![feature(optin_builtin_traits)] #![feature(prelude_import)] #![feature(raw)] +#![feature(shared)] #![feature(slice_concat_ext)] #![feature(slice_patterns)] #![feature(staged_api)] #![feature(str_internals)] #![feature(thread_local)] #![feature(try_from)] +#![feature(unboxed_closures)] #![feature(unicode)] +#![feature(unique)] +#![feature(untagged_unions)] #![feature(zero_one)] #![allow(non_camel_case_types, dead_code, unused_features)] #![no_std] @@ -151,6 +159,7 @@ pub mod ffi; pub mod io; pub mod num; pub mod os; +pub mod panic; pub mod path; pub mod sync; pub mod time; diff --git a/ctr-std/src/panic.rs b/ctr-std/src/panic.rs new file mode 100644 index 0000000..2e037cd --- /dev/null +++ b/ctr-std/src/panic.rs @@ -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 or the MIT license +// , 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` 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`, 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( + #[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 UnwindSafe for *const T {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for *mut T {} +#[unstable(feature = "unique", issue = "27730")] +impl UnwindSafe for Unique {} +#[unstable(feature = "shared", issue = "27730")] +impl UnwindSafe for Shared {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Mutex {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for RwLock {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for AssertUnwindSafe {} + +// 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 UnwindSafe for Rc {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Arc {} + +// 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 !RefUnwindSafe for UnsafeCell {} +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for AssertUnwindSafe {} + +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl RefUnwindSafe for Mutex {} +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl RefUnwindSafe for RwLock {} + +#[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 RefUnwindSafe for atomic::AtomicPtr {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl Deref for AssertUnwindSafe { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl DerefMut for AssertUnwindSafe { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl R> FnOnce<()> for AssertUnwindSafe { + type Output = R; + + extern "rust-call" fn call_once(self, _args: ()) -> R { + (self.0)() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for AssertUnwindSafe { + 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 R + UnwindSafe, R>(f: F) -> Result { + 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) -> ! { + unimplemented!() +} diff --git a/ctr-std/src/panicking.rs b/ctr-std/src/panicking.rs index e0f9477..3eb5bee 100644 --- a/ctr-std/src/panicking.rs +++ b/ctr-std/src/panicking.rs @@ -16,6 +16,9 @@ use io::prelude::*; use any::Any; use cell::RefCell; use fmt; +use mem; +use ptr; +use raw; use __core::fmt::Display; thread_local! { @@ -26,11 +29,11 @@ thread_local! { ///The compiler wants this to be here. Otherwise it won't be happy. And we like happy compilers. #[lang = "eh_personality"] -extern fn eh_personality() {} +pub extern fn eh_personality() {} /// Entry point of panic from the libcore crate. #[lang = "panic_fmt"] -extern fn rust_begin_panic(msg: fmt::Arguments, file: &'static str, line: u32) -> ! { +pub extern fn rust_begin_panic(msg: fmt::Arguments, file: &'static str, line: u32) -> ! { begin_panic_fmt(&msg, &(file, line)) } @@ -52,17 +55,115 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line: &(&'static str, u32)) -> begin_panic(s, file_line); } -/// This is where the main panic logic happens. +/// We don't have stack unwinding, so all we do is print the panic message +/// and then loop forever #[inline(never)] #[cold] pub fn begin_panic(msg: M, file_line: &(&'static str, u32)) -> ! { let msg = Box::new(msg); let (file, line) = *file_line; - print!("--------------------------------------------------"); + use libctru::console::consoleInit; + use libctru::gfx::gfxScreen_t; + + // set up a new console, overwriting whatever was on the top screen + // before we started panicking + let _console = unsafe { consoleInit(gfxScreen_t::GFX_TOP, ptr::null_mut()) }; + println!("PANIC in {} at line {}:", file, line); println!(" {}", msg); - print!("\x1b[29;00H--------------------------------------------------"); loop {} } + +/// Invoke a closure, capturing the cause of an unwinding panic if one occurs. +pub unsafe fn try R>(f: F) -> Result> { + #[allow(unions_with_drop_fields)] + union Data { + f: F, + r: R, + } + + // We do some sketchy operations with ownership here for the sake of + // performance. We can only pass pointers down to + // `__rust_maybe_catch_panic` (can't pass objects by value), so we do all + // the ownership tracking here manually using a union. + // + // We go through a transition where: + // + // * First, we set the data to be the closure that we're going to call. + // * When we make the function call, the `do_call` function below, we take + // ownership of the function pointer. At this point the `Data` union is + // entirely uninitialized. + // * If the closure successfully returns, we write the return value into the + // data's return slot. Note that `ptr::write` is used as it's overwriting + // uninitialized data. + // * Finally, when we come back out of the `__rust_maybe_catch_panic` we're + // in one of two states: + // + // 1. The closure didn't panic, in which case the return value was + // filled in. We move it out of `data` and return it. + // 2. The closure panicked, in which case the return value wasn't + // filled in. In this case the entire `data` union is invalid, so + // there is no need to drop anything. + // + // Once we stack all that together we should have the "most efficient' + // method of calling a catch panic whilst juggling ownership. + let mut any_data = 0; + let mut any_vtable = 0; + let mut data = Data { + f: f, + }; + + let r = __rust_maybe_catch_panic(do_call::, + &mut data as *mut _ as *mut u8, + &mut any_data, + &mut any_vtable); + + return if r == 0 { + debug_assert!(update_panic_count(0) == 0); + Ok(data.r) + } else { + update_panic_count(-1); + debug_assert!(update_panic_count(0) == 0); + Err(mem::transmute(raw::TraitObject { + data: any_data as *mut _, + vtable: any_vtable as *mut _, + })) + }; + + fn do_call R, R>(data: *mut u8) { + unsafe { + let data = data as *mut Data; + let f = ptr::read(&mut (*data).f); + ptr::write(&mut (*data).r, f()); + } + } +} + +#[cfg(not(test))] +#[doc(hidden)] +#[unstable(feature = "update_panic_count", issue = "0")] +pub fn update_panic_count(amt: isize) -> usize { + use cell::Cell; + thread_local! { static PANIC_COUNT: Cell = Cell::new(0) } + + PANIC_COUNT.with(|c| { + let next = (c.get() as isize + amt) as usize; + c.set(next); + return next + }) +} + +// *Implementation borrowed from the libpanic_abort crate* +// +// Rust's "try" function, but if we're aborting on panics we just call the +// function as there's nothing else we need to do here. +#[allow(improper_ctypes)] +extern fn __rust_maybe_catch_panic(f: fn(*mut u8), + data: *mut u8, + _data_ptr: *mut usize, + _vtable_ptr: *mut usize) -> u32 { + f(data); + 0 +} diff --git a/ctr-std/src/sync/condvar.rs b/ctr-std/src/sync/condvar.rs new file mode 100644 index 0000000..68c7e88 --- /dev/null +++ b/ctr-std/src/sync/condvar.rs @@ -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 or the MIT license +// , 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, + 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> { + 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(); + } +} diff --git a/ctr-std/src/sync/mod.rs b/ctr-std/src/sync/mod.rs index df954cd..245aaab 100644 --- a/ctr-std/src/sync/mod.rs +++ b/ctr-std/src/sync/mod.rs @@ -21,9 +21,16 @@ 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::condvar::{Condvar, WaitTimeoutResult}; #[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}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +mod condvar; mod mutex; +mod rwlock; diff --git a/ctr-std/src/sync/rwlock.rs b/ctr-std/src/sync/rwlock.rs new file mode 100644 index 0000000..a3db0ad --- /dev/null +++ b/ctr-std/src/sync/rwlock.rs @@ -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 or the MIT license +// , 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 { + inner: Box, + poison: poison::Flag, + data: UnsafeCell, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for RwLock {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for RwLock {} + +/// 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, +} + +#[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, + __poison: poison::Guard, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: ?Sized> !marker::Send for RwLockWriteGuard<'a, T> {} + +impl RwLock { + /// Creates a new instance of an `RwLock` 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 { + RwLock { + inner: box sys::RWLock::new(), + poison: poison::Flag::new(), + data: UnsafeCell::new(t), + } + } +} + +impl RwLock { + /// 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> { + 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> { + 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> { + 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> { + 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 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 { + 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 fmt::Debug for RwLock { + 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 {{ }}") + } + } +} + +#[stable(feature = "rw_lock_default", since = "1.9.0")] +impl Default for RwLock { + /// Creates a new `RwLock`, with the `Default` value for T. + fn default() -> RwLock { + RwLock::new(Default::default()) + } +} + +impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { + unsafe fn new(lock: &'rwlock RwLock) + -> LockResult> { + poison::map_result(lock.poison.borrow(), |_| { + RwLockReadGuard { + __lock: lock, + } + }) + } +} + +impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { + unsafe fn new(lock: &'rwlock RwLock) + -> LockResult> { + 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>, + } + 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); + 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), + } + } +} diff --git a/ctr-std/src/sys/unix/condvar.rs b/ctr-std/src/sys/unix/condvar.rs new file mode 100644 index 0000000..f19922c --- /dev/null +++ b/ctr-std/src/sys/unix/condvar.rs @@ -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 or the MIT license +// , 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(); + } +} diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs index 0da1d3b..76a4555 100644 --- a/ctr-std/src/sys/unix/mod.rs +++ b/ctr-std/src/sys/unix/mod.rs @@ -13,6 +13,7 @@ use io::{self, ErrorKind}; use libc; +pub mod condvar; pub mod ext; pub mod fast_thread_local; pub mod fd; @@ -22,6 +23,8 @@ pub mod mutex; pub mod os; pub mod os_str; pub mod path; +pub mod rwlock; +pub mod thread; pub mod thread_local; pub mod time; diff --git a/ctr-std/src/sys/unix/rwlock.rs b/ctr-std/src/sys/unix/rwlock.rs new file mode 100644 index 0000000..d74b614 --- /dev/null +++ b/ctr-std/src/sys/unix/rwlock.rs @@ -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 or the MIT license +// , 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(); + } +} diff --git a/ctr-std/src/sys/unix/thread.rs b/ctr-std/src/sys/unix/thread.rs new file mode 100644 index 0000000..4eac345 --- /dev/null +++ b/ctr-std/src/sys/unix/thread.rs @@ -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 or the MIT license +// , 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) -> io::Result { + 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 { None } + pub unsafe fn init() -> Option { None } +} diff --git a/ctr-std/src/sys_common/condvar.rs b/ctr-std/src/sys_common/condvar.rs new file mode 100644 index 0000000..b6f29dd --- /dev/null +++ b/ctr-std/src/sys_common/condvar.rs @@ -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 or the MIT license +// , 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() } +} diff --git a/ctr-std/src/sys_common/mod.rs b/ctr-std/src/sys_common/mod.rs index 7aedb69..936ff80 100644 --- a/ctr-std/src/sys_common/mod.rs +++ b/ctr-std/src/sys_common/mod.rs @@ -25,11 +25,16 @@ #![allow(missing_docs)] pub mod at_exit_imp; +pub mod condvar; pub mod io; pub mod mutex; pub mod poison; pub mod remutex; +pub mod rwlock; +pub mod thread; +pub mod thread_info; pub mod thread_local; +pub mod util; // common error constructors diff --git a/ctr-std/src/sys_common/rwlock.rs b/ctr-std/src/sys_common/rwlock.rs new file mode 100644 index 0000000..71a4f01 --- /dev/null +++ b/ctr-std/src/sys_common/rwlock.rs @@ -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 or the MIT license +// , 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() } +} diff --git a/ctr-std/src/sys_common/thread.rs b/ctr-std/src/sys_common/thread.rs new file mode 100644 index 0000000..bb6baae --- /dev/null +++ b/ctr-std/src/sys_common/thread.rs @@ -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 or the MIT license +// , 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)() +} diff --git a/ctr-std/src/sys_common/thread_info.rs b/ctr-std/src/sys_common/thread_info.rs new file mode 100644 index 0000000..95d8b6c --- /dev/null +++ b/ctr-std/src/sys_common/thread_info.rs @@ -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 or the MIT license +// , 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, + thread: Thread, +} + +thread_local! { static THREAD_INFO: RefCell> = RefCell::new(None) } + +impl ThreadInfo { + fn with(f: F) -> Option 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 { + ThreadInfo::with(|info| info.thread.clone()) +} + +pub fn stack_guard() -> Option { + ThreadInfo::with(|info| info.stack_guard).and_then(|o| o) +} + +pub fn set(stack_guard: Option, 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) -> Self; +} diff --git a/ctr-std/src/sys_common/util.rs b/ctr-std/src/sys_common/util.rs new file mode 100644 index 0000000..aad0680 --- /dev/null +++ b/ctr-std/src/sys_common/util.rs @@ -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 or the MIT license +// , 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(); } +} diff --git a/ctr-std/src/thread/mod.rs b/ctr-std/src/thread/mod.rs index a8a6490..705efd4 100644 --- a/ctr-std/src/thread/mod.rs +++ b/ctr-std/src/thread/mod.rs @@ -170,6 +170,22 @@ #![stable(feature = "rust1", since = "1.0.0")] +use any::Any; +use cell::UnsafeCell; +use ffi::{CStr, CString}; +use fmt; +use io; +use panic; +//use panicking; +use str; +use sync::{Mutex, Condvar, Arc}; +use sys::thread as imp; +use sys_common::mutex; +use sys_common::thread_info; +use sys_common::util; +use sys_common::{AsInner, IntoInner}; +use time::Duration; + //////////////////////////////////////////////////////////////////////////////// // Thread-local storage //////////////////////////////////////////////////////////////////////////////// @@ -194,9 +210,1066 @@ pub use self::local::{LocalKey, LocalKeyState}; #[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 +//////////////////////////////////////////////////////////////////////////////// +// Builder +//////////////////////////////////////////////////////////////////////////////// + +/// Thread configuration. Provides detailed control over the properties +/// and behavior of new threads. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let handler = builder.spawn(|| { +/// // thread code +/// }).unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Builder { + // A name for the thread-to-be, for identification in panic messages + name: Option, + // The size of the stack for the spawned thread in bytes + stack_size: Option, +} + +impl Builder { + /// Generates the base configuration for spawning a thread, from which + /// configuration methods can be chained. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()) + /// .stack_size(10); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> Builder { + Builder { + name: None, + stack_size: None, + } + } + + /// Names the thread-to-be. Currently the name is used for identification + /// only in panic messages. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn name(mut self, name: String) -> Builder { + self.name = Some(name); + self + } + + /// Sets the size of the stack (in bytes) for the new thread. + /// + /// The actual stack size may be greater than this value if + /// the platform specifies minimal stack size. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new().stack_size(32 * 1024); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn stack_size(mut self, size: usize) -> Builder { + self.stack_size = Some(size); + self + } + + /// Spawns a new thread, and returns a join handle for it. + /// + /// The child thread may outlive the parent (unless the parent thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the child thread, including recovering its panics. + /// + /// # Errors + /// + /// Unlike the [`spawn`] free function, this method yields an + /// [`io::Result`] to capture any failure to create the thread at + /// the OS level. + /// + /// [`spawn`]: ../../std/thread/fn.spawn.html + /// [`io::Result`]: ../../std/io/type.Result.html + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn spawn(self, f: F) -> io::Result> where + F: FnOnce() -> T, F: Send + 'static, T: Send + 'static + { + let Builder { name, stack_size } = self; + + let stack_size = stack_size.unwrap_or(util::min_stack()); + + let my_thread = Thread::new(name); + let their_thread = my_thread.clone(); + + let my_packet : Arc>>> + = Arc::new(UnsafeCell::new(None)); + let their_packet = my_packet.clone(); + + let main = move || { + if let Some(name) = their_thread.cname() { + imp::Thread::set_name(name); + } + unsafe { + thread_info::set(imp::guard::current(), their_thread); + let try_result = panic::catch_unwind(panic::AssertUnwindSafe(f)); + *their_packet.get() = Some(try_result); + } + }; + + Ok(JoinHandle(JoinInner { + native: unsafe { + Some(imp::Thread::new(stack_size, Box::new(main))?) + }, + thread: my_thread, + packet: Packet(my_packet), + })) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Free functions +//////////////////////////////////////////////////////////////////////////////// + +/// Spawns a new thread, returning a [`JoinHandle`] for it. +/// +/// The join handle will implicitly *detach* the child thread upon being +/// dropped. In this case, the child thread may outlive the parent (unless +/// the parent thread is the main thread; the whole process is terminated when +/// the main thread finishes). Additionally, the join handle provides a [`join`] +/// method that can be used to join the child thread. If the child thread +/// panics, [`join`] will return an [`Err`] containing the argument given to +/// [`panic`]. +/// +/// # Panics +/// +/// Panics if the OS fails to create a thread; use [`Builder::spawn`] +/// to recover from such errors. +/// +/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html +/// [`join`]: ../../std/thread/struct.JoinHandle.html#method.join +/// [`Err`]: ../../std/result/enum.Result.html#variant.Err +/// [`panic`]: ../../std/macro.panic.html +/// [`Builder::spawn`]: ../../std/thread/struct.Builder.html#method.spawn +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::spawn(|| { +/// // thread code +/// }); +/// +/// handler.join().unwrap(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn spawn(f: F) -> JoinHandle where + F: FnOnce() -> T, F: Send + 'static, T: Send + 'static +{ + Builder::new().spawn(f).unwrap() +} + +/// Gets a handle to the thread that invokes it. +/// +/// # Examples +/// +/// Getting a handle to the current thread with `thread::current()`: +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::Builder::new() +/// .name("named thread".into()) +/// .spawn(|| { +/// let handle = thread::current(); +/// assert_eq!(handle.name(), Some("named thread")); +/// }) +/// .unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn current() -> Thread { + thread_info::current_thread().expect("use of std::thread::current() is not \ + possible after the thread's local \ + data has been destroyed") +} + +/// Cooperatively gives up a timeslice to the OS scheduler. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// thread::yield_now(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn yield_now() { + imp::Thread::yield_now() +} + +/// Determines whether the current thread is unwinding because of panic. +/// +/// # Examples +/// +/// ```should_panic +/// use std::thread; +/// +/// struct SomeStruct; +/// +/// impl Drop for SomeStruct { +/// fn drop(&mut self) { +/// if thread::panicking() { +/// println!("dropped while unwinding"); +/// } else { +/// println!("dropped while not unwinding"); +/// } +/// } +/// } +/// +/// { +/// print!("a: "); +/// let a = SomeStruct; +/// } +/// +/// { +/// print!("b: "); +/// let b = SomeStruct; +/// panic!() +/// } +/// ``` +// We don't have stack unwinding on the 3DS, so we can leave this as false for now #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn panicking() -> bool { - false + false +} + +/// Puts the current thread to sleep for the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. +/// +/// # Platform behavior +/// +/// On Unix platforms this function will not return early due to a +/// signal being received or a spurious wakeup. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// +/// // Let's sleep for 2 seconds: +/// thread::sleep_ms(2000); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::thread::sleep`")] +pub fn sleep_ms(ms: u32) { + sleep(Duration::from_millis(ms as u64)) +} + +/// Puts the current thread to sleep for the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. +/// +/// # Platform behavior +/// +/// On Unix platforms this function will not return early due to a +/// signal being received or a spurious wakeup. Platforms which do not support +/// nanosecond precision for sleeping will have `dur` rounded up to the nearest +/// granularity of time they can sleep for. +/// +/// # Examples +/// +/// ```no_run +/// use std::{thread, time}; +/// +/// let ten_millis = time::Duration::from_millis(10); +/// let now = time::Instant::now(); +/// +/// thread::sleep(ten_millis); +/// +/// assert!(now.elapsed() >= ten_millis); +/// ``` +#[stable(feature = "thread_sleep", since = "1.4.0")] +pub fn sleep(dur: Duration) { + imp::Thread::sleep(dur) +} + +/// Blocks unless or until the current thread's token is made available. +/// +/// Every thread is equipped with some basic low-level blocking support, via +/// the `park()` function and the [`unpark()`][unpark] method. These can be +/// used as a more CPU-efficient implementation of a spinlock. +/// +/// [unpark]: struct.Thread.html#method.unpark +/// +/// The API is typically used by acquiring a handle to the current thread, +/// placing that handle in a shared data structure so that other threads can +/// find it, and then parking (in a loop with a check for the token actually +/// being acquired). +/// +/// A call to `park` does not guarantee that the thread will remain parked +/// forever, and callers should be prepared for this possibility. +/// +/// See the [module documentation][thread] for more detail. +/// +/// [thread]: index.html +// +// The implementation currently uses the trivial strategy of a Mutex+Condvar +// with wakeup flag, which does not actually allow spurious wakeups. In the +// future, this will be implemented in a more efficient way, perhaps along the lines of +// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp +// or futuxes, and in either case may allow spurious wakeups. +#[stable(feature = "rust1", since = "1.0.0")] +pub fn park() { + let thread = current(); + let mut guard = thread.inner.lock.lock().unwrap(); + while !*guard { + guard = thread.inner.cvar.wait(guard).unwrap(); + } + *guard = false; +} + +/// Use [park_timeout]. +/// +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to `park()` except that the +/// thread will be blocked for roughly no longer than `ms`. 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` long. +/// +/// See the [module documentation][thread] for more detail. +/// +/// [thread]: index.html +/// [park_timeout]: fn.park_timeout.html +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "1.6.0", reason = "replaced by `std::thread::park_timeout`")] +pub fn park_timeout_ms(ms: u32) { + park_timeout(Duration::from_millis(ms as u64)) +} + +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to `park()` 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` long. +/// +/// See the module doc for more detail. +/// +/// # Platform behavior +/// +/// Platforms which do not support nanosecond precision for sleeping will have +/// `dur` rounded up to the nearest granularity of time they can sleep for. +/// +/// # Example +/// +/// Waiting for the complete expiration of the timeout: +/// +/// ```rust,no_run +/// use std::thread::park_timeout; +/// use std::time::{Instant, Duration}; +/// +/// let timeout = Duration::from_secs(2); +/// let beginning_park = Instant::now(); +/// park_timeout(timeout); +/// +/// while beginning_park.elapsed() < timeout { +/// println!("restarting park_timeout after {:?}", beginning_park.elapsed()); +/// let timeout = timeout - beginning_park.elapsed(); +/// park_timeout(timeout); +/// } +/// ``` +#[stable(feature = "park_timeout", since = "1.4.0")] +pub fn park_timeout(dur: Duration) { + let thread = current(); + let mut guard = thread.inner.lock.lock().unwrap(); + if !*guard { + let (g, _) = thread.inner.cvar.wait_timeout(guard, dur).unwrap(); + guard = g; + } + *guard = false; +} + +//////////////////////////////////////////////////////////////////////////////// +// ThreadId +//////////////////////////////////////////////////////////////////////////////// + +/// A unique identifier for a running thread. +/// +/// A `ThreadId` is an opaque object that has a unique value for each thread +/// that creates one. `ThreadId`s do not correspond to a thread's system- +/// designated identifier. +/// +/// # Examples +/// +/// ``` +/// #![feature(thread_id)] +/// +/// use std::thread; +/// +/// let handler = thread::Builder::new() +/// .spawn(|| { +/// let thread = thread::current(); +/// let thread_id = thread.id(); +/// }) +/// .unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +#[unstable(feature = "thread_id", issue = "21507")] +#[derive(Eq, PartialEq, Copy, Clone)] +pub struct ThreadId(u64); + +impl ThreadId { + // Generate a new unique thread ID. + fn new() -> ThreadId { + static GUARD: mutex::Mutex = mutex::Mutex::new(); + static mut COUNTER: u64 = 0; + + unsafe { + GUARD.lock(); + + // If we somehow use up all our bits, panic so that we're not + // covering up subtle bugs of IDs being reused. + if COUNTER == ::u64::MAX { + GUARD.unlock(); + panic!("failed to generate unique thread ID: bitspace exhausted"); + } + + let id = COUNTER; + COUNTER += 1; + + GUARD.unlock(); + + ThreadId(id) + } + } +} + +#[unstable(feature = "thread_id", issue = "21507")] +impl fmt::Debug for ThreadId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("ThreadId { .. }") + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Thread +//////////////////////////////////////////////////////////////////////////////// + +/// The internal representation of a `Thread` handle +struct Inner { + name: Option, // Guaranteed to be UTF-8 + id: ThreadId, + lock: Mutex, // true when there is a buffered unpark + cvar: Condvar, +} + +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +/// A handle to a thread. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::Builder::new() +/// .name("foo".into()) +/// .spawn(|| { +/// let thread = thread::current(); +/// println!("thread name: {}", thread.name().unwrap()); +/// }) +/// .unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +pub struct Thread { + inner: Arc, +} + +impl Thread { + // Used only internally to construct a thread object without spawning + fn new(name: Option) -> Thread { + let cname = name.map(|n| { + CString::new(n).expect("thread name may not contain interior null bytes") + }); + Thread { + inner: Arc::new(Inner { + name: cname, + id: ThreadId::new(), + lock: Mutex::new(false), + cvar: Condvar::new(), + }) + } + } + + /// Atomically makes the handle's token available if it is not already. + /// + /// See the module doc for more detail. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let handler = thread::Builder::new() + /// .spawn(|| { + /// let thread = thread::current(); + /// thread.unpark(); + /// }) + /// .unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn unpark(&self) { + let mut guard = self.inner.lock.lock().unwrap(); + if !*guard { + *guard = true; + self.inner.cvar.notify_one(); + } + } + + /// Gets the thread's unique identifier. + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_id)] + /// + /// use std::thread; + /// + /// let handler = thread::Builder::new() + /// .spawn(|| { + /// let thread = thread::current(); + /// println!("thread id: {:?}", thread.id()); + /// }) + /// .unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[unstable(feature = "thread_id", issue = "21507")] + pub fn id(&self) -> ThreadId { + self.inner.id + } + + /// Gets the thread's name. + /// + /// # Examples + /// + /// Threads by default have no name specified: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// assert!(thread::current().name().is_none()); + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// Thread with a specified name: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn name(&self) -> Option<&str> { + self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) } ) + } + + fn cname(&self) -> Option<&CStr> { + self.inner.name.as_ref().map(|s| &**s) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Thread { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.name(), f) + } +} + +// a hack to get around privacy restrictions +impl thread_info::NewThread for Thread { + fn new(name: Option) -> Thread { Thread::new(name) } +} + +//////////////////////////////////////////////////////////////////////////////// +// JoinHandle +//////////////////////////////////////////////////////////////////////////////// + +/// Indicates the manner in which a thread exited. +/// +/// A thread that completes without panicking is considered to exit successfully. +#[stable(feature = "rust1", since = "1.0.0")] +pub type Result = ::result::Result>; + +// This packet is used to communicate the return value between the child thread +// and the parent thread. Memory is shared through the `Arc` within and there's +// no need for a mutex here because synchronization happens with `join()` (the +// parent thread never reads this packet until the child has exited). +// +// This packet itself is then stored into a `JoinInner` which in turns is placed +// in `JoinHandle` and `JoinGuard`. Due to the usage of `UnsafeCell` we need to +// manually worry about impls like Send and Sync. The type `T` should +// already always be Send (otherwise the thread could not have been created) and +// this type is inherently Sync because no methods take &self. Regardless, +// however, we add inheriting impls for Send/Sync to this type to ensure it's +// Send/Sync and that future modifications will still appropriately classify it. +struct Packet(Arc>>>); + +unsafe impl Send for Packet {} +unsafe impl Sync for Packet {} + +/// Inner representation for JoinHandle +struct JoinInner { + native: Option, + thread: Thread, + packet: Packet, +} + +impl JoinInner { + fn join(&mut self) -> Result { + self.native.take().unwrap().join(); + unsafe { + (*self.packet.0.get()).take().unwrap() + } + } +} + +/// An owned permission to join on a thread (block on its termination). +/// +/// A `JoinHandle` *detaches* the child thread when it is dropped. +/// +/// Due to platform restrictions, it is not possible to [`Clone`] this +/// handle: the ability to join a child thread is a uniquely-owned +/// permission. +/// +/// This `struct` is created by the [`thread::spawn`] function and the +/// [`thread::Builder::spawn`] method. +/// +/// # Examples +/// +/// Creation from [`thread::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { +/// // some work here +/// }); +/// ``` +/// +/// Creation from [`thread::Builder::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { +/// // some work here +/// }).unwrap(); +/// ``` +/// +/// [`Clone`]: ../../std/clone/trait.Clone.html +/// [`thread::spawn`]: fn.spawn.html +/// [`thread::Builder::spawn`]: struct.Builder.html#method.spawn +#[stable(feature = "rust1", since = "1.0.0")] +pub struct JoinHandle(JoinInner); + +impl JoinHandle { + /// Extracts a handle to the underlying thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_id)] + /// + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// + /// let thread = join_handle.thread(); + /// println!("thread id: {:?}", thread.id()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn thread(&self) -> &Thread { + &self.0.thread + } + + /// Waits for the associated thread to finish. + /// + /// If the child thread panics, [`Err`] is returned with the parameter given + /// to [`panic`]. + /// + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// [`panic`]: ../../std/macro.panic.html + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// join_handle.join().expect("Couldn't join on the associated thread"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn join(mut self) -> Result { + self.0.join() + } +} + +impl AsInner for JoinHandle { + fn as_inner(&self) -> &imp::Thread { self.0.native.as_ref().unwrap() } +} + +impl IntoInner for JoinHandle { + fn into_inner(self) -> imp::Thread { self.0.native.unwrap() } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for JoinHandle { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("JoinHandle { .. }") + } +} + +fn _assert_sync_and_send() { + fn _assert_both() {} + _assert_both::>(); + _assert_both::(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////////////// + +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests { + use any::Any; + use sync::mpsc::{channel, Sender}; + use result; + use super::{Builder}; + use thread; + use time::Duration; + use u32; + + // !!! These tests are dangerous. If something is buggy, they will hang, !!! + // !!! instead of exiting cleanly. This might wedge the buildbots. !!! + + #[test] + fn test_unnamed_thread() { + thread::spawn(move|| { + assert!(thread::current().name().is_none()); + }).join().ok().unwrap(); + } + + #[test] + fn test_named_thread() { + Builder::new().name("ada lovelace".to_string()).spawn(move|| { + assert!(thread::current().name().unwrap() == "ada lovelace".to_string()); + }).unwrap().join().unwrap(); + } + + #[test] + #[should_panic] + fn test_invalid_named_thread() { + let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {}); + } + + #[test] + fn test_run_basic() { + let (tx, rx) = channel(); + thread::spawn(move|| { + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); + } + + #[test] + fn test_join_panic() { + match thread::spawn(move|| { + panic!() + }).join() { + result::Result::Err(_) => (), + result::Result::Ok(()) => panic!() + } + } + + #[test] + fn test_spawn_sched() { + let (tx, rx) = channel(); + + fn f(i: i32, tx: Sender<()>) { + let tx = tx.clone(); + thread::spawn(move|| { + if i == 0 { + tx.send(()).unwrap(); + } else { + f(i - 1, tx); + } + }); + + } + f(10, tx); + rx.recv().unwrap(); + } + + #[test] + fn test_spawn_sched_childs_on_default_sched() { + let (tx, rx) = channel(); + + thread::spawn(move|| { + thread::spawn(move|| { + tx.send(()).unwrap(); + }); + }); + + rx.recv().unwrap(); + } + + fn avoid_copying_the_body(spawnfn: F) where F: FnOnce(Box) { + let (tx, rx) = channel(); + + let x: Box<_> = box 1; + let x_in_parent = (&*x) as *const i32 as usize; + + spawnfn(Box::new(move|| { + let x_in_child = (&*x) as *const i32 as usize; + tx.send(x_in_child).unwrap(); + })); + + let x_in_child = rx.recv().unwrap(); + assert_eq!(x_in_parent, x_in_child); + } + + #[test] + fn test_avoid_copying_the_body_spawn() { + avoid_copying_the_body(|v| { + thread::spawn(move || v()); + }); + } + + #[test] + fn test_avoid_copying_the_body_thread_spawn() { + avoid_copying_the_body(|f| { + thread::spawn(move|| { + f(); + }); + }) + } + + #[test] + fn test_avoid_copying_the_body_join() { + avoid_copying_the_body(|f| { + let _ = thread::spawn(move|| { + f() + }).join(); + }) + } + + #[test] + fn test_child_doesnt_ref_parent() { + // If the child refcounts the parent thread, this will stack overflow when + // climbing the thread tree to dereference each ancestor. (See #1789) + // (well, it would if the constant were 8000+ - I lowered it to be more + // valgrind-friendly. try this at home, instead..!) + const GENERATIONS: u32 = 16; + fn child_no(x: u32) -> Box { + return Box::new(move|| { + if x < GENERATIONS { + thread::spawn(move|| child_no(x+1)()); + } + }); + } + thread::spawn(|| child_no(0)()); + } + + #[test] + fn test_simple_newsched_spawn() { + thread::spawn(move || {}); + } + + #[test] + fn test_try_panic_message_static_str() { + match thread::spawn(move|| { + panic!("static string"); + }).join() { + Err(e) => { + type T = &'static str; + assert!(e.is::()); + assert_eq!(*e.downcast::().unwrap(), "static string"); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_owned_str() { + match thread::spawn(move|| { + panic!("owned string".to_string()); + }).join() { + Err(e) => { + type T = String; + assert!(e.is::()); + assert_eq!(*e.downcast::().unwrap(), "owned string".to_string()); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_any() { + match thread::spawn(move|| { + panic!(box 413u16 as Box); + }).join() { + Err(e) => { + type T = Box; + assert!(e.is::()); + let any = e.downcast::().unwrap(); + assert!(any.is::()); + assert_eq!(*any.downcast::().unwrap(), 413); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_unit_struct() { + struct Juju; + + match thread::spawn(move|| { + panic!(Juju) + }).join() { + Err(ref e) if e.is::() => {} + Err(_) | Ok(()) => panic!() + } + } + + #[test] + fn test_park_timeout_unpark_before() { + for _ in 0..10 { + thread::current().unpark(); + thread::park_timeout(Duration::from_millis(u32::MAX as u64)); + } + } + + #[test] + fn test_park_timeout_unpark_not_called() { + for _ in 0..10 { + thread::park_timeout(Duration::from_millis(10)); + } + } + + #[test] + fn test_park_timeout_unpark_called_other_thread() { + for _ in 0..10 { + let th = thread::current(); + + let _guard = thread::spawn(move || { + super::sleep(Duration::from_millis(50)); + th.unpark(); + }); + + thread::park_timeout(Duration::from_millis(u32::MAX as u64)); + } + } + + #[test] + fn sleep_ms_smoke() { + thread::sleep(Duration::from_millis(2)); + } + + #[test] + fn test_thread_id_equal() { + assert!(thread::current().id() == thread::current().id()); + } + + #[test] + fn test_thread_id_not_equal() { + let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap(); + assert!(thread::current().id() != spawned_id); + } + + // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due + // to the test harness apparently interfering with stderr configuration. }