diff options
| author | Aaron Turon <aturon@mozilla.com> | 2014-12-14 00:05:32 -0800 |
|---|---|---|
| committer | Aaron Turon <aturon@mozilla.com> | 2014-12-18 23:31:52 -0800 |
| commit | a27fbac86849e07a0a6c746869d8f78319bd3a16 (patch) | |
| tree | f17d75fcdd4d353f5ff919e491a5fc71252c0ef1 /src/libstd/rt | |
| parent | 13f302d0c5dd3a88426da53ba07cdbe16459635b (diff) | |
| download | rust-a27fbac86849e07a0a6c746869d8f78319bd3a16.tar.gz rust-a27fbac86849e07a0a6c746869d8f78319bd3a16.zip | |
Revise std::thread API to join by default
This commit is part of a series that introduces a `std::thread` API to replace `std::task`. In the new API, `spawn` returns a `JoinGuard`, which by default will join the spawned thread when dropped. It can also be used to join explicitly at any time, returning the thread's result. Alternatively, the spawned thread can be explicitly detached (so no join takes place). As part of this change, Rust processes now terminate when the main thread exits, even if other detached threads are still running, moving Rust closer to standard threading models. This new behavior may break code that was relying on the previously implicit join-all. In addition to the above, the new thread API also offers some built-in support for building blocking abstractions in user space; see the module doc for details. Closes #18000 [breaking-change]
Diffstat (limited to 'src/libstd/rt')
| -rw-r--r-- | src/libstd/rt/at_exit_imp.rs | 2 | ||||
| -rw-r--r-- | src/libstd/rt/local.rs | 131 | ||||
| -rw-r--r-- | src/libstd/rt/mod.rs | 5 | ||||
| -rw-r--r-- | src/libstd/rt/mutex.rs | 406 | ||||
| -rw-r--r-- | src/libstd/rt/thread.rs | 170 | ||||
| -rw-r--r-- | src/libstd/rt/unwind.rs | 1 | ||||
| -rw-r--r-- | src/libstd/rt/util.rs | 1 |
7 files changed, 6 insertions, 710 deletions
diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs index 1b97a01146c..b63b4ced005 100644 --- a/src/libstd/rt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -45,7 +45,7 @@ pub fn cleanup() { let queue: Box<Queue> = mem::transmute(queue); let v = mem::replace(&mut *queue.lock(), Vec::new()); for to_run in v.into_iter() { - to_run.invoke(); + to_run.invoke(()); } } } diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs deleted file mode 100644 index 089960a6bc8..00000000000 --- a/src/libstd/rt/local.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::prelude::*; - -use boxed::Box; -use rt::local_ptr; -use rt::task::Task; - -/// Encapsulates some task-local data. -pub trait Local<Borrowed> { - fn put(value: Box<Self>); - fn take() -> Box<Self>; - fn try_take() -> Option<Box<Self>>; - fn exists(unused_value: Option<Self>) -> bool; - fn borrow(unused_value: Option<Self>) -> Borrowed; - unsafe fn unsafe_take() -> Box<Self>; - unsafe fn unsafe_borrow() -> *mut Self; - unsafe fn try_unsafe_borrow() -> Option<*mut Self>; -} - -impl Local<local_ptr::Borrowed<Task>> for Task { - #[inline] - fn put(value: Box<Task>) { unsafe { local_ptr::put(value) } } - #[inline] - fn take() -> Box<Task> { unsafe { local_ptr::take() } } - #[inline] - fn try_take() -> Option<Box<Task>> { unsafe { local_ptr::try_take() } } - fn exists(_: Option<Task>) -> bool { local_ptr::exists() } - #[inline] - fn borrow(_: Option<Task>) -> local_ptr::Borrowed<Task> { - unsafe { - local_ptr::borrow::<Task>() - } - } - #[inline] - unsafe fn unsafe_take() -> Box<Task> { local_ptr::unsafe_take() } - #[inline] - unsafe fn unsafe_borrow() -> *mut Task { local_ptr::unsafe_borrow() } - #[inline] - unsafe fn try_unsafe_borrow() -> Option<*mut Task> { - local_ptr::try_unsafe_borrow() - } -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - use super::super::thread::Thread; - use super::super::task::Task; - - #[test] - fn thread_local_task_smoke_test() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - let task: Box<Task> = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn thread_local_task_two_instances() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - let task: Box<Task> = Local::take(); - cleanup_task(task); - let task = box Task::new(None, None); - Local::put(task); - let task: Box<Task> = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn borrow_smoke_test() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - - unsafe { - let _task: *mut Task = Local::unsafe_borrow(); - } - let task: Box<Task> = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn borrow_with_return() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - - { - let _ = Local::borrow(None::<Task>); - } - - let task: Box<Task> = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn try_take() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - - let t: Box<Task> = Local::try_take().unwrap(); - let u: Option<Box<Task>> = Local::try_take(); - assert!(u.is_none()); - - cleanup_task(t); - }).join(); - } - - fn cleanup_task(t: Box<Task>) { - t.drop(); - } - -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index acc05cbf140..fd50d845716 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -27,6 +27,7 @@ use os; use thunk::Thunk; use kinds::Send; use thread::Thread; +use ops::FnOnce; use sys; use sys_common; use sys_common::thread_info::{mod, NewThread}; @@ -145,8 +146,8 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { /// /// It is forbidden for procedures to register more `at_exit` handlers when they /// are running, and doing so will lead to a process abort. -pub fn at_exit(f: proc():Send) { - at_exit_imp::push(f); +pub fn at_exit<F:FnOnce()+Send>(f: F) { + at_exit_imp::push(Thunk::new(f)); } /// One-time runtime cleanup. diff --git a/src/libstd/rt/mutex.rs b/src/libstd/rt/mutex.rs deleted file mode 100644 index 381f14570df..00000000000 --- a/src/libstd/rt/mutex.rs +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A native mutex and condition variable type. -//! -//! This module contains bindings to the platform's native mutex/condition -//! variable primitives. It provides two types: `StaticNativeMutex`, which can -//! be statically initialized via the `NATIVE_MUTEX_INIT` value, and a simple -//! wrapper `NativeMutex` that has a destructor to clean up after itself. These -//! objects serve as both mutexes and condition variables simultaneously. -//! -//! The static lock is lazily initialized, but it can only be unsafely -//! destroyed. A statically initialized lock doesn't necessarily have a time at -//! which it can get deallocated. For this reason, there is no `Drop` -//! implementation of the static mutex, but rather the `destroy()` method must -//! be invoked manually if destruction of the mutex is desired. -//! -//! The non-static `NativeMutex` type does have a destructor, but cannot be -//! statically initialized. -//! -//! It is not recommended to use this type for idiomatic rust use. These types -//! are appropriate where no other options are available, but other rust -//! concurrency primitives should be used before them: the `sync` crate defines -//! `StaticMutex` and `Mutex` types. -//! -//! # Example -//! -//! ```rust -//! use rt::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT}; -//! -//! // Use a statically initialized mutex -//! static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; -//! -//! unsafe { -//! let _guard = LOCK.lock(); -//! } // automatically unlocked here -//! -//! // Use a normally initialized mutex -//! unsafe { -//! let mut lock = NativeMutex::new(); -//! -//! { -//! let _guard = lock.lock(); -//! } // unlocked here -//! -//! // sometimes the RAII guard isn't appropriate -//! lock.lock_noguard(); -//! lock.unlock_noguard(); -//! } // `lock` is deallocated here -//! ``` - -#![allow(non_camel_case_types)] - -use core::prelude::*; - -use sys::mutex as imp; - -/// A native mutex suitable for storing in statics (that is, it has -/// the `destroy` method rather than a destructor). -/// -/// Prefer the `NativeMutex` type where possible, since that does not -/// require manual deallocation. -pub struct StaticNativeMutex { - inner: imp::Mutex, -} - -/// A native mutex with a destructor for clean-up. -/// -/// See `StaticNativeMutex` for a version that is suitable for storing in -/// statics. -pub struct NativeMutex { - inner: StaticNativeMutex -} - -/// Automatically unlocks the mutex that it was created from on -/// destruction. -/// -/// Using this makes lock-based code resilient to unwinding/task -/// panic, because the lock will be automatically unlocked even -/// then. -#[must_use] -pub struct LockGuard<'a> { - lock: &'a StaticNativeMutex -} - -pub const NATIVE_MUTEX_INIT: StaticNativeMutex = StaticNativeMutex { - inner: imp::MUTEX_INIT, -}; - -impl StaticNativeMutex { - /// Creates a new mutex. - /// - /// Note that a mutex created in this way needs to be explicit - /// freed with a call to `destroy` or it will leak. - /// Also it is important to avoid locking until mutex has stopped moving - pub unsafe fn new() -> StaticNativeMutex { - StaticNativeMutex { inner: imp::Mutex::new() } - } - - /// Acquires this lock. This assumes that the current thread does not - /// already hold the lock. - /// - /// # Example - /// - /// ```rust - /// use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - /// static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - /// unsafe { - /// let _guard = LOCK.lock(); - /// // critical section... - /// } // automatically unlocked in `_guard`'s destructor - /// ``` - /// - /// # Unsafety - /// - /// This method is unsafe because it will not function correctly if this - /// mutex has been *moved* since it was last used. The mutex can move an - /// arbitrary number of times before its first usage, but once a mutex has - /// been used once it is no longer allowed to move (or otherwise it invokes - /// undefined behavior). - /// - /// Additionally, this type does not take into account any form of - /// scheduling model. This will unconditionally block the *os thread* which - /// is not always desired. - pub unsafe fn lock<'a>(&'a self) -> LockGuard<'a> { - self.inner.lock(); - - LockGuard { lock: self } - } - - /// Attempts to acquire the lock. The value returned is `Some` if - /// the attempt succeeded. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. - pub unsafe fn trylock<'a>(&'a self) -> Option<LockGuard<'a>> { - if self.inner.trylock() { - Some(LockGuard { lock: self }) - } else { - None - } - } - - /// Acquire the lock without creating a `LockGuard`. - /// - /// These needs to be paired with a call to `.unlock_noguard`. Prefer using - /// `.lock`. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, this - /// does not guarantee that the mutex will ever be unlocked, and it is - /// undefined to drop an already-locked mutex. - pub unsafe fn lock_noguard(&self) { self.inner.lock() } - - /// Attempts to acquire the lock without creating a - /// `LockGuard`. The value returned is whether the lock was - /// acquired or not. - /// - /// If `true` is returned, this needs to be paired with a call to - /// `.unlock_noguard`. Prefer using `.trylock`. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock_noguard`. - pub unsafe fn trylock_noguard(&self) -> bool { - self.inner.trylock() - } - - /// Unlocks the lock. This assumes that the current thread already holds the - /// lock. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, it - /// is not guaranteed that this is unlocking a previously locked mutex. It - /// is undefined to unlock an unlocked mutex. - pub unsafe fn unlock_noguard(&self) { self.inner.unlock() } - - /// Block on the internal condition variable. - /// - /// This function assumes that the lock is already held. Prefer - /// using `LockGuard.wait` since that guarantees that the lock is - /// held. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, this - /// is unsafe because the mutex may not be currently locked. - pub unsafe fn wait_noguard(&self) { self.inner.wait() } - - /// Signals a thread in `wait` to wake up - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, this - /// is unsafe because the mutex may not be currently locked. - pub unsafe fn signal_noguard(&self) { self.inner.signal() } - - /// This function is especially unsafe because there are no guarantees made - /// that no other thread is currently holding the lock or waiting on the - /// condition variable contained inside. - pub unsafe fn destroy(&self) { self.inner.destroy() } -} - -impl NativeMutex { - /// Creates a new mutex. - /// - /// The user must be careful to ensure the mutex is not locked when its is - /// being destroyed. - /// Also it is important to avoid locking until mutex has stopped moving - pub unsafe fn new() -> NativeMutex { - NativeMutex { inner: StaticNativeMutex::new() } - } - - /// Acquires this lock. This assumes that the current thread does not - /// already hold the lock. - /// - /// # Example - /// - /// ```rust - /// use rt::mutex::NativeMutex; - /// unsafe { - /// let mut lock = NativeMutex::new(); - /// - /// { - /// let _guard = lock.lock(); - /// // critical section... - /// } // automatically unlocked in `_guard`'s destructor - /// } - /// ``` - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::lock`. - pub unsafe fn lock<'a>(&'a self) -> LockGuard<'a> { - self.inner.lock() - } - - /// Attempts to acquire the lock. The value returned is `Some` if - /// the attempt succeeded. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::trylock`. - pub unsafe fn trylock<'a>(&'a self) -> Option<LockGuard<'a>> { - self.inner.trylock() - } - - /// Acquire the lock without creating a `LockGuard`. - /// - /// These needs to be paired with a call to `.unlock_noguard`. Prefer using - /// `.lock`. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::lock_noguard`. - pub unsafe fn lock_noguard(&self) { self.inner.lock_noguard() } - - /// Attempts to acquire the lock without creating a - /// `LockGuard`. The value returned is whether the lock was - /// acquired or not. - /// - /// If `true` is returned, this needs to be paired with a call to - /// `.unlock_noguard`. Prefer using `.trylock`. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::trylock_noguard`. - pub unsafe fn trylock_noguard(&self) -> bool { - self.inner.trylock_noguard() - } - - /// Unlocks the lock. This assumes that the current thread already holds the - /// lock. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::unlock_noguard`. - pub unsafe fn unlock_noguard(&self) { self.inner.unlock_noguard() } - - /// Block on the internal condition variable. - /// - /// This function assumes that the lock is already held. Prefer - /// using `LockGuard.wait` since that guarantees that the lock is - /// held. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::wait_noguard`. - pub unsafe fn wait_noguard(&self) { self.inner.wait_noguard() } - - /// Signals a thread in `wait` to wake up - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::signal_noguard`. - pub unsafe fn signal_noguard(&self) { self.inner.signal_noguard() } -} - -impl Drop for NativeMutex { - fn drop(&mut self) { - unsafe {self.inner.destroy()} - } -} - -impl<'a> LockGuard<'a> { - /// Block on the internal condition variable. - pub unsafe fn wait(&self) { - self.lock.wait_noguard() - } - - /// Signals a thread in `wait` to wake up. - pub unsafe fn signal(&self) { - self.lock.signal_noguard() - } -} - -#[unsafe_destructor] -impl<'a> Drop for LockGuard<'a> { - fn drop(&mut self) { - unsafe {self.lock.unlock_noguard()} - } -} - -#[cfg(test)] -mod test { - use prelude::*; - - use mem::drop; - use super::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - use rt::thread::Thread; - - #[test] - fn smoke_lock() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - let _guard = LK.lock(); - } - } - - #[test] - fn smoke_cond() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - let guard = LK.lock(); - let t = Thread::start(move|| { - let guard = LK.lock(); - guard.signal(); - }); - guard.wait(); - drop(guard); - - t.join(); - } - } - - #[test] - fn smoke_lock_noguard() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - LK.lock_noguard(); - LK.unlock_noguard(); - } - } - - #[test] - fn smoke_cond_noguard() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - LK.lock_noguard(); - let t = Thread::start(move|| { - LK.lock_noguard(); - LK.signal_noguard(); - LK.unlock_noguard(); - }); - LK.wait_noguard(); - LK.unlock_noguard(); - - t.join(); - } - } - - #[test] - fn destroy_immediately() { - unsafe { - let m = StaticNativeMutex::new(); - m.destroy(); - } - } -} diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs deleted file mode 100644 index 9eb7048f1e6..00000000000 --- a/src/libstd/rt/thread.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Native os-thread management -//! -//! This modules contains bindings necessary for managing OS-level threads. -//! These functions operate outside of the rust runtime, creating threads -//! which are not used for scheduling in any way. - -#![allow(non_camel_case_types)] - -use core::prelude::*; - -use boxed::Box; -use mem; -use sys::stack_overflow; -use sys::thread as imp; - -pub unsafe fn init() { - imp::guard::init(); - stack_overflow::init(); -} - -pub unsafe fn cleanup() { - stack_overflow::cleanup(); -} - -/// This struct represents a native thread's state. This is used to join on an -/// existing thread created in the join-able state. -pub struct Thread<T> { - native: imp::rust_thread, - joined: bool, - packet: Box<Option<T>>, -} - -static DEFAULT_STACK_SIZE: uint = 1024 * 1024; - -/// Returns the last writable byte of the main thread's stack next to the guard -/// page. Must be called from the main thread. -pub fn main_guard_page() -> uint { - unsafe { - imp::guard::main() - } -} - -/// Returns the last writable byte of the current thread's stack next to the -/// guard page. Must not be called from the main thread. -pub fn current_guard_page() -> uint { - unsafe { - imp::guard::current() - } -} - -// There are two impl blocks b/c if T were specified at the top then it's just a -// pain to specify a type parameter on Thread::spawn (which doesn't need the -// type parameter). -impl Thread<()> { - /// Starts execution of a new OS thread. - /// - /// This function will not wait for the thread to join, but a handle to the - /// thread will be returned. - /// - /// Note that the handle returned is used to acquire the return value of the - /// procedure `main`. The `join` function will wait for the thread to finish - /// and return the value that `main` generated. - /// - /// Also note that the `Thread` returned will *always* wait for the thread - /// to finish executing. This means that even if `join` is not explicitly - /// called, when the `Thread` falls out of scope its destructor will block - /// waiting for the OS thread. - pub fn start<T: Send>(main: proc():Send -> T) -> Thread<T> { - Thread::start_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `start`, but specifies an explicit - /// stack size for the new thread. - pub fn start_stack<T: Send>(stack: uint, main: proc():Send -> T) -> Thread<T> { - - // We need the address of the packet to fill in to be stable so when - // `main` fills it in it's still valid, so allocate an extra box to do - // so. - let packet = box None; - let packet2: *mut Option<T> = unsafe { - *mem::transmute::<&Box<Option<T>>, *const *mut Option<T>>(&packet) - }; - let main = proc() unsafe { *packet2 = Some(main()); }; - let native = unsafe { imp::create(stack, box main) }; - - Thread { - native: native, - joined: false, - packet: packet, - } - } - - /// This will spawn a new thread, but it will not wait for the thread to - /// finish, nor is it possible to wait for the thread to finish. - /// - /// This corresponds to creating threads in the 'detached' state on unix - /// systems. Note that platforms may not keep the main program alive even if - /// there are detached thread still running around. - pub fn spawn(main: proc():Send) { - Thread::spawn_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `spawn`, but explicitly specifies a - /// stack size for the new thread. - pub fn spawn_stack(stack: uint, main: proc():Send) { - unsafe { - let handle = imp::create(stack, box main); - imp::detach(handle); - } - } - - /// Relinquishes the CPU slot that this OS-thread is currently using, - /// allowing another thread to run for awhile. - pub fn yield_now() { - unsafe { imp::yield_now(); } - } -} - -impl<T: Send> Thread<T> { - /// Wait for this thread to finish, returning the result of the thread's - /// calculation. - pub fn join(mut self) -> T { - assert!(!self.joined); - unsafe { imp::join(self.native) }; - self.joined = true; - assert!(self.packet.is_some()); - self.packet.take().unwrap() - } -} - -#[unsafe_destructor] -impl<T: Send> Drop for Thread<T> { - fn drop(&mut self) { - // This is required for correctness. If this is not done then the thread - // would fill in a return box which no longer exists. - if !self.joined { - unsafe { imp::join(self.native) }; - } - } -} - -#[cfg(test)] -mod tests { - use super::Thread; - - #[test] - fn smoke() { Thread::start(proc (){}).join(); } - - #[test] - fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); } - - #[test] - fn detached() { Thread::spawn(proc () {}) } - - #[test] - fn small_stacks() { - assert_eq!(42i, Thread::start_stack(0, proc () 42i).join()); - assert_eq!(42i, Thread::start_stack(1, proc () 42i).join()); - } -} diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index c896f4e39da..8ef10cbbd77 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -373,6 +373,7 @@ pub mod eabi { pub use self::EXCEPTION_DISPOSITION::*; use rt::libunwind as uw; use libc::{c_void, c_int}; + use kinds::Copy; #[repr(C)] #[allow(missing_copy_implementations)] diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 86dbb6066f3..fa527a70f83 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -89,6 +89,7 @@ pub fn default_sched_threads() -> uint { pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || cfg!(rtassert); +#[allow(missing_copy_implementations)] pub struct Stdio(libc::c_int); #[allow(non_upper_case_globals)] |
