diff options
| author | Fenrir <[email protected]> | 2017-03-05 00:25:16 -0700 |
|---|---|---|
| committer | Fenrir <[email protected]> | 2017-03-05 02:19:40 -0700 |
| commit | 5299b505b79c48e788067d66a727636ff933de92 (patch) | |
| tree | e882dbf4591a81bdefd7c721038eac3562eaacaf /ctr-std/src/thread | |
| parent | Merge pull request #24 from FenrirWolf/unit_type (diff) | |
| download | ctru-rs-5299b505b79c48e788067d66a727636ff933de92.tar.xz ctru-rs-5299b505b79c48e788067d66a727636ff933de92.zip | |
Initial thread support
Diffstat (limited to 'ctr-std/src/thread')
| -rw-r--r-- | ctr-std/src/thread/mod.rs | 1077 |
1 files changed, 1075 insertions, 2 deletions
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<String>, + // The size of the stack for the spawned thread in bytes + stack_size: Option<usize>, +} + +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<F, T>(self, f: F) -> io::Result<JoinHandle<T>> 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<UnsafeCell<Option<Result<T>>>> + = 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, T>(f: F) -> JoinHandle<T> 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<CString>, // Guaranteed to be UTF-8 + id: ThreadId, + lock: Mutex<bool>, // 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<Inner>, +} + +impl Thread { + // Used only internally to construct a thread object without spawning + fn new(name: Option<String>) -> 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<String>) -> 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<T> = ::result::Result<T, Box<Any + Send + 'static>>; + +// 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<T>(Arc<UnsafeCell<Option<Result<T>>>>); + +unsafe impl<T: Send> Send for Packet<T> {} +unsafe impl<T: Sync> Sync for Packet<T> {} + +/// Inner representation for JoinHandle +struct JoinInner<T> { + native: Option<imp::Thread>, + thread: Thread, + packet: Packet<T>, +} + +impl<T> JoinInner<T> { + fn join(&mut self) -> Result<T> { + 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<T>(JoinInner<T>); + +impl<T> JoinHandle<T> { + /// 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<T> { + self.0.join() + } +} + +impl<T> AsInner<imp::Thread> for JoinHandle<T> { + fn as_inner(&self) -> &imp::Thread { self.0.native.as_ref().unwrap() } +} + +impl<T> IntoInner<imp::Thread> for JoinHandle<T> { + fn into_inner(self) -> imp::Thread { self.0.native.unwrap() } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl<T> fmt::Debug for JoinHandle<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("JoinHandle { .. }") + } +} + +fn _assert_sync_and_send() { + fn _assert_both<T: Send + Sync>() {} + _assert_both::<JoinHandle<()>>(); + _assert_both::<Thread>(); +} + +//////////////////////////////////////////////////////////////////////////////// +// 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<F>(spawnfn: F) where F: FnOnce(Box<Fn() + Send>) { + 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<Fn() + Send> { + 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::<T>()); + assert_eq!(*e.downcast::<T>().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::<T>()); + assert_eq!(*e.downcast::<T>().unwrap(), "owned string".to_string()); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_any() { + match thread::spawn(move|| { + panic!(box 413u16 as Box<Any + Send>); + }).join() { + Err(e) => { + type T = Box<Any + Send>; + assert!(e.is::<T>()); + let any = e.downcast::<T>().unwrap(); + assert!(any.is::<u16>()); + assert_eq!(*any.downcast::<u16>().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::<Juju>() => {} + 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. } |