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]
This commit is contained in:
Aaron Turon 2014-12-14 00:05:32 -08:00
parent 13f302d0c5
commit a27fbac868
86 changed files with 464 additions and 1786 deletions

View file

@ -445,7 +445,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
loop {
//waiting 1 second for gdbserver start
timer::sleep(Duration::milliseconds(1000));
let result = Thread::with_join(move || {
let result = Thread::spawn(move || {
tcp::TcpStream::connect("127.0.0.1:5039").unwrap();
}).join();
if result.is_err() {

View file

@ -1,5 +1,7 @@
% The Rust Tasks and Communication Guide
**NOTE** This guide is badly out of date an needs to be rewritten.
# Introduction
Rust provides safe concurrent abstractions through a number of core library
@ -22,7 +24,7 @@ from shared mutable state.
At its simplest, creating a task is a matter of calling the `spawn` function
with a closure argument. `spawn` executes the closure in the new task.
```{rust}
```{rust,ignore}
# use std::task::spawn;
// Print something profound in a different task using a named function
@ -49,7 +51,7 @@ closure is limited to capturing `Send`-able data from its environment
ensures that `spawn` can safely move the entire closure and all its
associated state into an entirely different task for execution.
```{rust}
```{rust,ignore}
# use std::task::spawn;
# fn generate_task_number() -> int { 0 }
// Generate some state locally
@ -75,7 +77,7 @@ The simplest way to create a channel is to use the `channel` function to create
of a channel, and a **receiver** is the receiving endpoint. Consider the following
example of calculating two results concurrently:
```{rust}
```{rust,ignore}
# use std::task::spawn;
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
@ -96,7 +98,7 @@ stream for sending and receiving integers (the left-hand side of the `let`,
`(tx, rx)`, is an example of a destructuring let: the pattern separates a tuple
into its component parts).
```{rust}
```{rust,ignore}
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
```
@ -104,7 +106,7 @@ The child task will use the sender to send data to the parent task, which will
wait to receive the data on the receiver. The next statement spawns the child
task.
```{rust}
```{rust,ignore}
# use std::task::spawn;
# fn some_expensive_computation() -> int { 42 }
# let (tx, rx) = channel();
@ -123,7 +125,7 @@ computation, then sends the result over the captured channel.
Finally, the parent continues with some other expensive computation, then waits
for the child's result to arrive on the receiver:
```{rust}
```{rust,ignore}
# fn some_other_expensive_computation() {}
# let (tx, rx) = channel::<int>();
# tx.send(0);
@ -154,7 +156,7 @@ spawn(move || {
Instead we can clone the `tx`, which allows for multiple senders.
```{rust}
```{rust,ignore}
let (tx, rx) = channel();
for init_val in range(0u, 3) {
@ -179,7 +181,7 @@ Note that the above cloning example is somewhat contrived since you could also
simply use three `Sender` pairs, but it serves to illustrate the point. For
reference, written with multiple streams, it might look like the example below.
```{rust}
```{rust,ignore}
# use std::task::spawn;
// Create a vector of ports, one for each child task
@ -203,7 +205,7 @@ getting the result later.
The basic example below illustrates this.
```{rust}
```{rust,ignore}
use std::sync::Future;
# fn main() {
@ -230,7 +232,7 @@ called.
Here is another example showing how futures allow you to background
computations. The workload will be distributed on the available cores.
```{rust}
```{rust,ignore}
# use std::num::Float;
# use std::sync::Future;
fn partial_sum(start: uint) -> f64 {
@ -268,7 +270,7 @@ Here is a small example showing how to use Arcs. We wish to run concurrently
several computations on a single large vector of floats. Each task needs the
full vector to perform its duty.
```{rust}
```{rust,ignore}
use std::num::Float;
use std::rand;
use std::sync::Arc;
@ -295,7 +297,7 @@ The function `pnorm` performs a simple computation on the vector (it computes
the sum of its items at the power given as argument and takes the inverse power
of this value). The Arc on the vector is created by the line:
```{rust}
```{rust,ignore}
# use std::rand;
# use std::sync::Arc;
# fn main() {
@ -309,7 +311,7 @@ the wrapper and not its contents. Within the task's procedure, the captured
Arc reference can be used as a shared reference to the underlying vector as
if it were local.
```{rust}
```{rust,ignore}
# use std::rand;
# use std::sync::Arc;
# fn pnorm(nums: &[f64], p: uint) -> f64 { 4.0 }
@ -346,11 +348,11 @@ and `()`, callers can pattern-match on a result to check whether it's an `Ok`
result with an `int` field (representing a successful result) or an `Err` result
(representing termination with an error).
```{rust}
```{rust,ignore}
# use std::thread::Thread;
# fn some_condition() -> bool { false }
# fn calculate_result() -> int { 0 }
let result: Result<int, Box<std::any::Any + Send>> = Thread::with_join(move || {
let result: Result<int, Box<std::any::Any + Send>> = Thread::spawn(move || {
if some_condition() {
calculate_result()
} else {

View file

@ -5217,6 +5217,8 @@ the same function, so our binary is a little bit larger.
# Tasks
**NOTE**: this section is currently out of date and will be rewritten soon.
Concurrency and parallelism are topics that are of increasing interest to a
broad subsection of software developers. Modern computers are often multi-core,
to the point that even embedded devices like cell phones have more than one
@ -5231,7 +5233,7 @@ library, and not part of the language. This means that in the future, other
concurrency libraries can be written for Rust to help in specific scenarios.
Here's an example of creating a task:
```{rust}
```{rust,ignore}
spawn(move || {
println!("Hello from a task!");
});
@ -5261,7 +5263,7 @@ If tasks were only able to capture these values, they wouldn't be very useful.
Luckily, tasks can communicate with each other through **channel**s. Channels
work like this:
```{rust}
```{rust,ignore}
let (tx, rx) = channel();
spawn(move || {
@ -5280,7 +5282,7 @@ which returns an `Result<T, TryRecvError>` and does not block.
If you want to send messages to the task as well, create two channels!
```{rust}
```{rust,ignore}
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
@ -5340,7 +5342,7 @@ we'll just get the value immediately.
Tasks don't always succeed, they can also panic. A task that wishes to panic
can call the `panic!` macro, passing a message:
```{rust}
```{rust,ignore}
spawn(move || {
panic!("Nope.");
});
@ -5349,7 +5351,7 @@ spawn(move || {
If a task panics, it is not possible for it to recover. However, it can
notify other tasks that it has panicked. We can do this with `task::try`:
```{rust}
```{rust,ignore}
use std::task;
use std::rand;

View file

@ -389,11 +389,13 @@ safe concurrent programs.
Here's an example of a concurrent Rust program:
```{rust}
use std::thread::Thread;
fn main() {
for _ in range(0u, 10u) {
spawn(move || {
Thread::spawn(move || {
println!("Hello, world!");
});
}).detach();
}
}
```
@ -403,7 +405,8 @@ This program creates ten threads, who all print `Hello, world!`. The
double bars `||`. (The `move` keyword indicates that the closure takes
ownership of any data it uses; we'll have more on the significance of
this shortly.) This closure is executed in a new thread created by
`spawn`.
`spawn`. The `detach` method means that the child thread is allowed to
outlive its parent.
One common form of problem in concurrent programs is a 'data race.'
This occurs when two different threads attempt to access the same
@ -418,13 +421,15 @@ problem.
Let's see an example. This Rust code will not compile:
```{rust,ignore}
use std::thread::Thread;
fn main() {
let mut numbers = vec![1i, 2i, 3i];
for i in range(0u, 3u) {
spawn(move || {
Thread::spawn(move || {
for j in range(0, 3) { numbers[j] += 1 }
});
}).detach();
}
}
```
@ -469,6 +474,7 @@ mutation doesn't cause a data race.
Here's what using an Arc with a Mutex looks like:
```{rust}
use std::thread::Thread;
use std::sync::{Arc,Mutex};
fn main() {
@ -476,13 +482,13 @@ fn main() {
for i in range(0u, 3u) {
let number = numbers.clone();
spawn(move || {
Thread::spawn(move || {
let mut array = number.lock();
(*array)[i] += 1;
println!("numbers[{}] is {}", i, (*array)[i]);
});
}).detach();
}
}
```
@ -532,13 +538,15 @@ As an example, Rust's ownership system is _entirely_ at compile time. The
safety check that makes this an error about moved values:
```{rust,ignore}
use std::thread::Thread;
fn main() {
let vec = vec![1i, 2, 3];
for i in range(1u, 3) {
spawn(move || {
Thread::spawn(move || {
println!("{}", vec[i]);
});
}).detach();
}
}
```

View file

@ -39,6 +39,7 @@ use heap::deallocate;
///
/// ```rust
/// use std::sync::Arc;
/// use std::thread::Thread;
///
/// fn main() {
/// let numbers = Vec::from_fn(100, |i| i as f32);
@ -47,11 +48,11 @@ use heap::deallocate;
/// for _ in range(0u, 10) {
/// let child_numbers = shared_numbers.clone();
///
/// spawn(move || {
/// Thread::spawn(move || {
/// let local_numbers = child_numbers.as_slice();
///
/// // Work with the local numbers
/// });
/// }).detach();
/// }
/// }
/// ```

View file

@ -1344,6 +1344,7 @@ pub mod raw {
#[cfg(test)]
mod tests {
use std::boxed::Box;
use std::cell::Cell;
use std::default::Default;
use std::mem;
@ -1627,7 +1628,10 @@ mod tests {
#[test]
fn test_swap_remove_noncopyable() {
// Tests that we don't accidentally run destructors twice.
let mut v = vec![Box::new(()), Box::new(()), Box::new(())];
let mut v = Vec::new();
v.push(box 0u8);
v.push(box 0u8);
v.push(box 0u8);
let mut _e = v.swap_remove(0);
assert_eq!(v.len(), 2);
_e = v.swap_remove(1);

View file

@ -92,7 +92,7 @@ impl<'a, T, Sized? B> BorrowFrom<Cow<'a, T, B>> for B where B: ToOwned<T> {
/// Trait for moving into a `Cow`
pub trait IntoCow<'a, T, Sized? B> {
/// Moves `serlf` into `Cow`
/// Moves `self` into `Cow`
fn into_cow(self) -> Cow<'a, T, B>;
}

View file

@ -9,7 +9,7 @@
// except according to those terms.
use core::finally::{try_finally, Finally};
use std::task::failing;
use std::thread::Thread;
#[test]
fn test_success() {
@ -20,7 +20,7 @@ fn test_success() {
*i = 10;
},
|i| {
assert!(!failing());
assert!(!Thread::panicking());
assert_eq!(*i, 10);
*i = 20;
});
@ -38,7 +38,7 @@ fn test_fail() {
panic!();
},
|i| {
assert!(failing());
assert!(Thread::panicking());
assert_eq!(*i, 10);
})
}

View file

@ -475,10 +475,10 @@ pub fn monitor<F:FnOnce()+Send>(f: F) {
static STACK_SIZE: uint = 32000000; // 32MB
let (tx, rx) = channel();
let mut w = Some(io::ChanWriter::new(tx)); // option dance
let w = io::ChanWriter::new(tx);
let mut r = io::ChanReader::new(rx);
let mut cfg = thread::cfg().name("rustc".to_string());
let mut cfg = thread::Builder::new().name("rustc".to_string());
// FIXME: Hacks on hacks. If the env is trying to override the stack size
// then *don't* set it explicitly.
@ -486,11 +486,7 @@ pub fn monitor<F:FnOnce()+Send>(f: F) {
cfg = cfg.stack_size(STACK_SIZE);
}
let f = proc() {
std::io::stdio::set_stderr(box w.take().unwrap());
f()
};
match cfg.with_join(f).join() {
match cfg.spawn(move || { std::io::stdio::set_stderr(box w); f() }).join() {
Ok(()) => { /* fallthrough */ }
Err(value) => {
// Task panicked without emitting a fatal diagnostic

View file

@ -900,7 +900,7 @@ fn run_work_multithreaded(sess: &Session,
let mut tx = Some(tx);
futures.push(rx);
thread::cfg().name(format!("codegen-{}", i)).spawn(move |:| {
thread::Builder::new().name(format!("codegen-{}", i)).spawn(move |:| {
let diag_handler = mk_handler(box diag_emitter);
// Must construct cgcx inside the proc because it has non-Send
@ -927,7 +927,7 @@ fn run_work_multithreaded(sess: &Session,
}
tx.take().unwrap().send(());
});
}).detach();
}
let mut panicked = false;

View file

@ -102,7 +102,11 @@ struct Output {
}
pub fn main() {
std::os::set_exit_status(main_args(std::os::args().as_slice()));
static STACK_SIZE: uint = 32000000; // 32MB
let res = std::thread::Builder::new().stack_size(STACK_SIZE).spawn(move || {
main_args(std::os::args().as_slice())
}).join();
std::os::set_exit_status(res.map_err(|_| ()).unwrap());
}
pub fn opts() -> Vec<getopts::OptGroup> {
@ -343,7 +347,7 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche
let cr = Path::new(cratefile);
info!("starting to run rustc");
let (mut krate, analysis) = std::thread::Thread::with_join(move |:| {
let (mut krate, analysis) = std::thread::Thread::spawn(move |:| {
let cr = cr;
core::run_core(libs, cfgs, externs, &cr, triple)
}).join().map_err(|_| "rustc failed").unwrap();

View file

@ -155,7 +155,7 @@ fn runtest(test: &str, cratename: &str, libs: Vec<Path>, externs: core::Externs,
None => box io::stderr() as Box<Writer>,
};
io::util::copy(&mut p, &mut err).unwrap();
});
}).detach();
let emitter = diagnostic::EmitterWriter::new(box w2, None);
// Compile the code

View file

@ -1,559 +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 alloc::boxed::Box;
use core::mem;
use core::uint;
use libc;
use thunk::{Thunk};
use stack;
use stack_overflow;
pub unsafe fn init() {
imp::guard::init();
stack_overflow::init();
}
pub unsafe fn cleanup() {
stack_overflow::cleanup();
}
#[cfg(target_os = "windows")]
type StartFn = extern "system" fn(*mut libc::c_void) -> imp::rust_thread_return;
#[cfg(not(target_os = "windows"))]
type StartFn = extern "C" fn(*mut libc::c_void) -> imp::rust_thread_return;
/// 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;
// This is the starting point of rust os threads. The first thing we do
// is make sure that we don't trigger __morestack (also why this has a
// no_stack_check annotation), and then we extract the main function
// and invoke it.
#[no_stack_check]
fn start_thread(main: *mut libc::c_void) -> imp::rust_thread_return {
unsafe {
stack::record_os_managed_stack_bounds(0, uint::MAX);
let handler = stack_overflow::Handler::new();
let f: Box<Thunk> = mem::transmute(main);
f.invoke(());
drop(handler);
mem::transmute(0 as imp::rust_thread_return)
}
}
#[no_stack_check]
#[cfg(target_os = "windows")]
extern "system" fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return {
return start_thread(main);
}
#[no_stack_check]
#[cfg(not(target_os = "windows"))]
extern fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return {
return start_thread(main);
}
/// 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,F>(main: F) -> Thread<T>
where T:Send, F:FnOnce() -> T, F:Send
{
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, F>(stack: uint, main: F) -> Thread<T>
where T:Send, F:FnOnce() -> T, F:Send
{
// 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 native = unsafe {
imp::create(stack, Thunk::new(move |:| {
*packet2 = Some(main.call_once(()));
}))
};
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<F>(main: F)
where F : FnOnce() + 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<F>(stack: uint, main: F)
where F : FnOnce() + Send
{
unsafe {
let handle = imp::create(stack, Thunk::new(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(windows)]
#[allow(non_snake_case)]
mod imp {
use alloc::boxed::Box;
use core::cmp;
use core::mem;
use core::ptr;
use libc;
use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
LPVOID, DWORD, LPDWORD, HANDLE};
use stack::RED_ZONE;
use thunk::Thunk;
pub type rust_thread = HANDLE;
pub type rust_thread_return = DWORD;
pub mod guard {
pub unsafe fn main() -> uint {
0
}
pub unsafe fn current() -> uint {
0
}
pub unsafe fn init() {
}
}
pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
let arg: *mut libc::c_void = mem::transmute(box p);
// FIXME On UNIX, we guard against stack sizes that are too small but
// that's because pthreads enforces that stacks are at least
// PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
// just that below a certain threshold you can't do anything useful.
// That threshold is application and architecture-specific, however.
// For now, the only requirement is that it's big enough to hold the
// red zone. Round up to the next 64 kB because that's what the NT
// kernel does, might as well make it explicit. With the current
// 20 kB red zone, that makes for a 64 kB minimum stack.
let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1);
let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t,
super::thread_start, arg, 0, ptr::null_mut());
if ret as uint == 0 {
// be sure to not leak the closure
let _p: Box<Thunk> = mem::transmute(arg);
panic!("failed to spawn native thread: {}", ret);
}
return ret;
}
pub unsafe fn join(native: rust_thread) {
use libc::consts::os::extra::INFINITE;
WaitForSingleObject(native, INFINITE);
}
pub unsafe fn detach(native: rust_thread) {
assert!(libc::CloseHandle(native) != 0);
}
pub unsafe fn yield_now() {
// This function will return 0 if there are no other threads to execute,
// but this also means that the yield was useless so this isn't really a
// case that needs to be worried about.
SwitchToThread();
}
#[allow(non_snake_case)]
extern "system" {
fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES,
dwStackSize: SIZE_T,
lpStartAddress: super::StartFn,
lpParameter: LPVOID,
dwCreationFlags: DWORD,
lpThreadId: LPDWORD) -> HANDLE;
fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
fn SwitchToThread() -> BOOL;
}
}
#[cfg(unix)]
mod imp {
use core::prelude::*;
use alloc::boxed::Box;
use core::cmp;
use core::mem;
use core::ptr;
use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
use libc;
use thunk::Thunk;
use stack::RED_ZONE;
pub type rust_thread = libc::pthread_t;
pub type rust_thread_return = *mut u8;
#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
pub mod guard {
pub unsafe fn current() -> uint {
0
}
pub unsafe fn main() -> uint {
0
}
pub unsafe fn init() {
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub mod guard {
use super::*;
#[cfg(any(target_os = "linux", target_os = "android"))]
use core::mem;
#[cfg(any(target_os = "linux", target_os = "android"))]
use core::ptr;
use libc;
use libc::funcs::posix88::mman::{mmap};
use libc::consts::os::posix88::{PROT_NONE,
MAP_PRIVATE,
MAP_ANON,
MAP_FAILED,
MAP_FIXED};
// These are initialized in init() and only read from after
static mut PAGE_SIZE: uint = 0;
static mut GUARD_PAGE: uint = 0;
#[cfg(target_os = "macos")]
unsafe fn get_stack_start() -> *mut libc::c_void {
current() as *mut libc::c_void
}
#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe fn get_stack_start() -> *mut libc::c_void {
let mut attr: libc::pthread_attr_t = mem::zeroed();
if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
panic!("failed to get thread attributes");
}
let mut stackaddr = ptr::null_mut();
let mut stacksize = 0;
if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
panic!("failed to get stack information");
}
if pthread_attr_destroy(&mut attr) != 0 {
panic!("failed to destroy thread attributes");
}
stackaddr
}
pub unsafe fn init() {
let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
if psize == -1 {
panic!("failed to get page size");
}
PAGE_SIZE = psize as uint;
let stackaddr = get_stack_start();
// Rellocate the last page of the stack.
// This ensures SIGBUS will be raised on
// stack overflow.
let result = mmap(stackaddr,
PAGE_SIZE as libc::size_t,
PROT_NONE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
-1,
0);
if result != stackaddr || result == MAP_FAILED {
panic!("failed to allocate a guard page");
}
let offset = if cfg!(target_os = "linux") {
2
} else {
1
};
GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
}
pub unsafe fn main() -> uint {
GUARD_PAGE
}
#[cfg(target_os = "macos")]
pub unsafe fn current() -> uint {
(pthread_get_stackaddr_np(pthread_self()) as libc::size_t -
pthread_get_stacksize_np(pthread_self())) as uint
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub unsafe fn current() -> uint {
let mut attr: libc::pthread_attr_t = mem::zeroed();
if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
panic!("failed to get thread attributes");
}
let mut guardsize = 0;
if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
panic!("failed to get stack guard page");
}
if guardsize == 0 {
panic!("there is no guard page");
}
let mut stackaddr = ptr::null_mut();
let mut stacksize = 0;
if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
panic!("failed to get stack information");
}
if pthread_attr_destroy(&mut attr) != 0 {
panic!("failed to destroy thread attributes");
}
stackaddr as uint + guardsize as uint
}
}
pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(pthread_attr_init(&mut attr), 0);
assert_eq!(pthread_attr_setdetachstate(&mut attr,
PTHREAD_CREATE_JOINABLE), 0);
// Reserve room for the red zone, the runtime's stack of last resort.
let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
0 => {
},
libc::EINVAL => {
// EINVAL means |stack_size| is either too small or not a
// multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again.
let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint;
let stack_size = (stack_size + page_size - 1) &
(-(page_size as int - 1) as uint - 1);
assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
},
errno => {
// This cannot really happen.
panic!("pthread_attr_setstacksize() error: {}", errno);
},
};
let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint
let ret = pthread_create(&mut native, &attr, super::thread_start, arg);
assert_eq!(pthread_attr_destroy(&mut attr), 0);
if ret != 0 {
// be sure to not leak the closure
let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg);
panic!("failed to spawn native thread: {}", ret);
}
native
}
pub unsafe fn join(native: rust_thread) {
assert_eq!(pthread_join(native, ptr::null_mut()), 0);
}
pub unsafe fn detach(native: rust_thread) {
assert_eq!(pthread_detach(native), 0);
}
pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
// glibc >= 2.15 has a __pthread_get_minstack() function that returns
// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
// storage. We need that information to avoid blowing up when a small stack
// is created in an application with big thread-local storage requirements.
// See #6233 for rationale and details.
//
// Link weakly to the symbol for compatibility with older versions of glibc.
// Assumes that we've been dynamically linked to libpthread but that is
// currently always the case. Note that you need to check that the symbol
// is non-null before calling it!
#[cfg(target_os = "linux")]
fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t {
type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t;
extern {
#[linkage = "extern_weak"]
static __pthread_get_minstack: *const ();
}
if __pthread_get_minstack.is_null() {
PTHREAD_STACK_MIN
} else {
unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) }
}
}
// __pthread_get_minstack() is marked as weak but extern_weak linkage is
// not supported on OS X, hence this kludge...
#[cfg(not(target_os = "linux"))]
fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t {
PTHREAD_STACK_MIN
}
#[cfg(any(target_os = "linux"))]
extern {
pub fn pthread_self() -> libc::pthread_t;
pub fn pthread_getattr_np(native: libc::pthread_t,
attr: *mut libc::pthread_attr_t) -> libc::c_int;
pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t,
guardsize: *mut libc::size_t) -> libc::c_int;
pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t,
stackaddr: *mut *mut libc::c_void,
stacksize: *mut libc::size_t) -> libc::c_int;
}
#[cfg(target_os = "macos")]
extern {
pub fn pthread_self() -> libc::pthread_t;
pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void;
pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t;
}
extern {
fn pthread_create(native: *mut libc::pthread_t,
attr: *const libc::pthread_attr_t,
f: super::StartFn,
value: *mut libc::c_void) -> libc::c_int;
fn pthread_join(native: libc::pthread_t,
value: *mut *mut libc::c_void) -> libc::c_int;
fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
stack_size: libc::size_t) -> libc::c_int;
fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
state: libc::c_int) -> libc::c_int;
fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
fn sched_yield() -> libc::c_int;
}
}
#[cfg(test)]
mod tests {
use super::Thread;
#[test]
fn smoke() { Thread::start(move|| {}).join(); }
#[test]
fn data() { assert_eq!(Thread::start(move|| { 1i }).join(), 1); }
#[test]
fn detached() { Thread::spawn(move|| {}) }
#[test]
fn small_stacks() {
assert_eq!(42i, Thread::start_stack(0, move|| 42i).join());
assert_eq!(42i, Thread::start_stack(1, move|| 42i).join());
}
}

View file

@ -637,7 +637,7 @@ mod tests {
#[test]
fn test_to_c_str_fail() {
assert!(Thread::with_join(move|| { "he\x00llo".to_c_str() }).join().is_err());
assert!(Thread::spawn(move|| { "he\x00llo".to_c_str() }).join().is_err());
}
#[test]

View file

@ -13,7 +13,7 @@
use thread::Thread;
use sync::atomic::{AtomicBool, INIT_ATOMIC_BOOL, Ordering};
use sync::Arc;
use kinds::marker::NoSend;
use kinds::marker::{NoSend, NoSync};
use mem;
use clone::Clone;
@ -30,6 +30,7 @@ pub struct SignalToken {
pub struct WaitToken {
inner: Arc<Inner>,
no_send: NoSend,
no_sync: NoSync,
}
pub fn tokens() -> (WaitToken, SignalToken) {
@ -40,6 +41,7 @@ pub fn tokens() -> (WaitToken, SignalToken) {
let wait_token = WaitToken {
inner: inner.clone(),
no_send: NoSend,
no_sync: NoSync,
};
let signal_token = SignalToken {
inner: inner

View file

@ -59,26 +59,30 @@
//! Simple usage:
//!
//! ```
//! use std::thread::Thread;
//!
//! // Create a simple streaming channel
//! let (tx, rx) = channel();
//! spawn(move|| {
//! Thread::spawn(move|| {
//! tx.send(10i);
//! });
//! }).detach();
//! assert_eq!(rx.recv(), 10i);
//! ```
//!
//! Shared usage:
//!
//! ```
//! // Create a shared channel that can be sent along from many tasks
//! use std::thread::Thread;
//!
//! // Create a shared channel that can be sent along from many threads
//! // where tx is the sending half (tx for transmission), and rx is the receiving
//! // half (rx for receiving).
//! let (tx, rx) = channel();
//! for i in range(0i, 10i) {
//! let tx = tx.clone();
//! spawn(move|| {
//! Thread::spawn(move|| {
//! tx.send(i);
//! })
//! }).detach()
//! }
//!
//! for _ in range(0i, 10i) {
@ -100,11 +104,13 @@
//! Synchronous channels:
//!
//! ```
//! use std::thread::Thread;
//!
//! let (tx, rx) = sync_channel::<int>(0);
//! spawn(move|| {
//! Thread::spawn(move|| {
//! // This will wait for the parent task to start receiving
//! tx.send(53);
//! });
//! }).detach();
//! rx.recv();
//! ```
//!
@ -451,15 +457,17 @@ impl<T> UnsafeFlavor<T> for Receiver<T> {
/// # Example
///
/// ```
/// use std::thread::Thread;
///
/// // tx is is the sending half (tx for transmission), and rx is the receiving
/// // half (rx for receiving).
/// let (tx, rx) = channel();
///
/// // Spawn off an expensive computation
/// spawn(move|| {
/// Thread::spawn(move|| {
/// # fn expensive_computation() {}
/// tx.send(expensive_computation());
/// });
/// }).detach();
///
/// // Do some useful work for awhile
///
@ -490,15 +498,17 @@ pub fn channel<T: Send>() -> (Sender<T>, Receiver<T>) {
/// # Example
///
/// ```
/// use std::thread::Thread;
///
/// let (tx, rx) = sync_channel(1);
///
/// // this returns immediately
/// tx.send(1i);
///
/// spawn(move|| {
/// Thread::spawn(move|| {
/// // this will block until the previous message has been received
/// tx.send(2i);
/// });
/// }).detach();
///
/// assert_eq!(rx.recv(), 1i);
/// assert_eq!(rx.recv(), 2i);
@ -1242,7 +1252,7 @@ mod test {
test! { fn oneshot_single_thread_recv_chan_close() {
// Receiving on a closed chan will panic
let res = Thread::with_join(move|| {
let res = Thread::spawn(move|| {
let (tx, rx) = channel::<int>();
drop(tx);
rx.recv();
@ -1314,7 +1324,7 @@ mod test {
spawn(move|| {
drop(tx);
});
let res = Thread::with_join(move|| {
let res = Thread::spawn(move|| {
assert!(rx.recv() == box 10);
}).join();
assert!(res.is_err());
@ -1336,7 +1346,7 @@ mod test {
spawn(move|| {
drop(rx);
});
let _ = Thread::with_join(move|| {
let _ = Thread::spawn(move|| {
tx.send(1);
}).join();
}
@ -1345,8 +1355,8 @@ mod test {
test! { fn oneshot_multi_thread_recv_close_stress() {
for _ in range(0, stress_factor()) {
let (tx, rx) = channel::<int>();
spawn(proc() {
let res = Thread::with_join(move|| {
spawn(move|| {
let res = Thread::spawn(move|| {
rx.recv();
}).join();
assert!(res.is_err());
@ -1664,7 +1674,7 @@ mod sync_tests {
test! { fn oneshot_single_thread_recv_chan_close() {
// Receiving on a closed chan will panic
let res = Thread::with_join(move|| {
let res = Thread::spawn(move|| {
let (tx, rx) = sync_channel::<int>(0);
drop(tx);
rx.recv();
@ -1741,7 +1751,7 @@ mod sync_tests {
spawn(move|| {
drop(tx);
});
let res = Thread::with_join(move|| {
let res = Thread::spawn(move|| {
assert!(rx.recv() == box 10);
}).join();
assert!(res.is_err());
@ -1763,7 +1773,7 @@ mod sync_tests {
spawn(move|| {
drop(rx);
});
let _ = Thread::with_join(move || {
let _ = Thread::spawn(move || {
tx.send(1);
}).join();
}
@ -1772,8 +1782,8 @@ mod sync_tests {
test! { fn oneshot_multi_thread_recv_close_stress() {
for _ in range(0, stress_factor()) {
let (tx, rx) = sync_channel::<int>(0);
spawn(proc() {
let res = Thread::with_join(move|| {
spawn(move|| {
let res = Thread::spawn(move|| {
rx.recv();
}).join();
assert!(res.is_err());

View file

@ -108,20 +108,18 @@ fn wait<'a, 'b, T: Send>(lock: &'a Mutex<State<T>>,
f: fn(SignalToken) -> Blocker)
-> MutexGuard<'a, State<T>>
{
let me: Box<Task> = Local::take();
me.deschedule(1, |task| {
match mem::replace(&mut guard.blocker, f(task)) {
NoneBlocked => {}
_ => unreachable!(),
}
mem::drop(guard);
Ok(())
});
lock.lock()
let (wait_token, signal_token) = blocking::tokens();
match mem::replace(&mut guard.blocker, f(signal_token)) {
NoneBlocked => {}
_ => unreachable!(),
}
drop(guard); // unlock
wait_token.wait(); // block
lock.lock() // relock
}
/// Wakes up a task, dropping the lock at the correct time
fn wakeup<T>(task: BlockedTask, guard: MutexGuard<State<T>>) {
/// Wakes up a thread, dropping the lock at the correct time
fn wakeup<T>(token: SignalToken, guard: MutexGuard<State<T>>) {
// We need to be careful to wake up the waiting task *outside* of the mutex
// in case it incurs a context switch.
drop(guard);

View file

@ -167,7 +167,7 @@ mod test {
tx.send(vec![3u8, 4u8]);
tx.send(vec![5u8, 6u8]);
tx.send(vec![7u8, 8u8]);
});
}).detach();
let mut reader = ChanReader::new(rx);
let mut buf = [0u8, ..3];
@ -210,7 +210,7 @@ mod test {
tx.send(b"rld\nhow ".to_vec());
tx.send(b"are you?".to_vec());
tx.send(b"".to_vec());
});
}).detach();
let mut reader = ChanReader::new(rx);
@ -229,11 +229,7 @@ mod test {
writer.write_be_u32(42).unwrap();
let wanted = vec![0u8, 0u8, 0u8, 42u8];
<<<<<<< HEAD
let got = match task::try(move|| { rx.recv() }) {
=======
let got = match Thread::with_join(proc() { rx.recv() }).join() {
>>>>>>> Fallout from new thread API
let got = match Thread::spawn(move|| { rx.recv() }).join() {
Ok(got) => got,
Err(_) => panic!(),
};

View file

@ -105,6 +105,7 @@
//! # #![allow(dead_code)]
//! use std::io::{TcpListener, TcpStream};
//! use std::io::{Acceptor, Listener};
//! use std::thread::Thread;
//!
//! let listener = TcpListener::bind("127.0.0.1:80");
//!
@ -119,10 +120,10 @@
//! for stream in acceptor.incoming() {
//! match stream {
//! Err(e) => { /* connection failed */ }
//! Ok(stream) => spawn(move|| {
//! Ok(stream) => Thread::spawn(move|| {
//! // connection succeeded
//! handle_client(stream)
//! })
//! }).detach()
//! }
//! }
//!

View file

@ -136,16 +136,17 @@ impl TcpStream {
/// use std::io::timer;
/// use std::io::TcpStream;
/// use std::time::Duration;
/// use std::thread::Thread;
///
/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
/// let stream2 = stream.clone();
///
/// spawn(move|| {
/// Thread::spawn(move|| {
/// // close this stream after one second
/// timer::sleep(Duration::seconds(1));
/// let mut stream = stream2;
/// stream.close_read();
/// });
/// }).detach();
///
/// // wait for some data, will get canceled after one second
/// let mut buf = [0];
@ -279,6 +280,7 @@ impl sys_common::AsInner<TcpStreamImp> for TcpStream {
/// # #![allow(dead_code)]
/// use std::io::{TcpListener, TcpStream};
/// use std::io::{Acceptor, Listener};
/// use std::thread::Thread;
///
/// let listener = TcpListener::bind("127.0.0.1:80");
///
@ -293,10 +295,10 @@ impl sys_common::AsInner<TcpStreamImp> for TcpStream {
/// for stream in acceptor.incoming() {
/// match stream {
/// Err(e) => { /* connection failed */ }
/// Ok(stream) => spawn(move|| {
/// Ok(stream) => Thread::spawn(move|| {
/// // connection succeeded
/// handle_client(stream)
/// })
/// }).detach()
/// }
/// }
///
@ -416,11 +418,12 @@ impl TcpAcceptor {
/// ```
/// # #![allow(experimental)]
/// use std::io::{TcpListener, Listener, Acceptor, EndOfFile};
/// use std::thread::Thread;
///
/// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap();
/// let a2 = a.clone();
///
/// spawn(move|| {
/// Thread::spawn(move|| {
/// let mut a2 = a2;
/// for socket in a2.incoming() {
/// match socket {
@ -429,7 +432,7 @@ impl TcpAcceptor {
/// Err(e) => panic!("unexpected error: {}", e),
/// }
/// }
/// });
/// }).detach();
///
/// # fn wait_for_sigint() {}
/// // Now that our accept loop is running, wait for the program to be

View file

@ -698,7 +698,7 @@ impl Process {
Thread::spawn(move |:| {
let mut stream = stream;
tx.send(stream.read_to_end())
});
}).detach();
}
None => tx.send(Ok(Vec::new()))
}

View file

@ -230,7 +230,6 @@ pub mod hash;
/* Threads and communication */
pub mod task;
#[allow(missing_docs)]
pub mod thread;
pub mod sync;
pub mod comm;

View file

@ -353,13 +353,15 @@ macro_rules! vec {
/// # Example
///
/// ```
/// use std::thread::Thread;
///
/// let (tx1, rx1) = channel();
/// let (tx2, rx2) = channel();
/// # fn long_running_task() {}
/// # fn calculate_the_answer() -> int { 42i }
///
/// spawn(move|| { long_running_task(); tx1.send(()) });
/// spawn(move|| { tx2.send(calculate_the_answer()) });
/// Thread::spawn(move|| { long_running_task(); tx1.send(()) }).detach();
/// Thread::spawn(move|| { tx2.send(calculate_the_answer()) }).detach();
///
/// select! (
/// () = rx1.recv() => println!("the long running task finished first"),

View file

@ -26,6 +26,7 @@
#![allow(missing_docs)]
#![allow(non_snake_case)]
#![allow(unused_imports)]
pub use self::MemoryMapKind::*;
pub use self::MapOption::*;
@ -37,7 +38,7 @@ use fmt;
use io::{IoResult, IoError};
use iter::{Iterator, IteratorExt};
use kinds::Copy;
use libc::{c_void, c_int};
use libc::{c_void, c_int, c_char};
use libc;
use boxed::Box;
use ops::{Drop, FnOnce};

View file

@ -515,17 +515,17 @@ mod tests {
#[test]
fn test_null_byte() {
use thread::Thread;
let result = Thread::with_join(move|| {
let result = Thread::spawn(move|| {
Path::new(b"foo/bar\0")
}).join();
assert!(result.is_err());
let result = Thread::with_join(move|| {
let result = Thread::spawn(move|| {
Path::new("test").set_filename(b"f\0o")
}).join();
assert!(result.is_err());
let result = Thread::with_join(move|| {
let result = Thread::spawn(move|| {
Path::new("test").push(b"f\0o");
}).join();
assert!(result.is_err());

View file

@ -1299,17 +1299,17 @@ mod tests {
#[test]
fn test_null_byte() {
use thread::Thread;
let result = Thread::with_join(move|| {
let result = Thread::spawn(move|| {
Path::new(b"foo/bar\0")
}).join();
assert!(result.is_err());
let result = Thread::with_join(move|| {
let result = Thread::spawn(move|| {
Path::new("test").set_filename(b"f\0o")
}).join();
assert!(result.is_err());
let result = Thread::with_join(move|| {
let result = Thread::spawn(move || {
Path::new("test").push(b"f\0o");
}).join();
assert!(result.is_err());

View file

@ -379,7 +379,7 @@ mod test {
r.fill_bytes(&mut v);
Thread::yield_now();
}
});
}).detach();
}
// start all the tasks

View file

@ -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(());
}
}
}

View file

@ -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();
}
}

View file

@ -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.

View file

@ -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();
}
}
}

View file

@ -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());
}
}

View file

@ -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)]

View file

@ -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)]

View file

@ -42,14 +42,15 @@
//! ```
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicUint, SeqCst};
//! use std::thread::Thread;
//!
//! fn main() {
//! let spinlock = Arc::new(AtomicUint::new(1));
//!
//! let spinlock_clone = spinlock.clone();
//! spawn(move|| {
//! Thread::spawn(move|| {
//! spinlock_clone.store(0, SeqCst);
//! });
//! }).detach();
//!
//! // Wait for the other task to release the lock
//! while spinlock.load(SeqCst) != 0 {}
@ -61,6 +62,7 @@
//! ```
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicOption, SeqCst};
//! use std::thread::Thread;
//!
//! fn main() {
//! struct BigObject;
@ -68,14 +70,14 @@
//! let shared_big_object = Arc::new(AtomicOption::empty());
//!
//! let shared_big_object_clone = shared_big_object.clone();
//! spawn(move|| {
//! Thread::spawn(move|| {
//! let unwrapped_big_object = shared_big_object_clone.take(SeqCst);
//! if unwrapped_big_object.is_some() {
//! println!("got a big object from another task");
//! } else {
//! println!("other task hasn't sent big object yet");
//! }
//! });
//! }).detach();
//!
//! shared_big_object.swap(box BigObject, SeqCst);
//! }

View file

@ -15,17 +15,18 @@ use sync::{Mutex, Condvar};
///
/// ```rust
/// use std::sync::{Arc, Barrier};
/// use std::thread::Thread;
///
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in range(0u, 10) {
/// let c = barrier.clone();
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// spawn(move|| {
/// Thread::spawn(move|| {
/// println!("before wait");
/// c.wait();
/// println!("after wait");
/// });
/// }).detach();
/// }
/// ```
pub struct Barrier {

View file

@ -36,17 +36,18 @@ use time::Duration;
///
/// ```
/// use std::sync::{Arc, Mutex, Condvar};
/// use std::thread::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
/// spawn(move|| {
/// Thread::spawn(move|| {
/// let &(ref lock, ref cvar) = &*pair2;
/// let mut started = lock.lock();
/// *started = true;
/// cvar.notify_one();
/// });
/// }).detach();
///
/// // wait for the thread to start up
/// let &(ref lock, ref cvar) = &*pair;
@ -362,4 +363,3 @@ mod tests {
}
}

View file

@ -142,7 +142,7 @@ impl<A:Send> Future<A> {
Thread::spawn(move |:| {
// Don't panic if the other end has hung up
let _ = tx.send_opt(blk());
});
}).detach();
Future::from_receiver(rx)
}

View file

@ -35,6 +35,7 @@ use sys_common::mutex as sys;
///
/// ```rust
/// use std::sync::{Arc, Mutex};
/// use std::thread::Thread;
/// const N: uint = 10;
///
/// // Spawn a few threads to increment a shared variable (non-atomically), and
@ -47,7 +48,7 @@ use sys_common::mutex as sys;
/// let (tx, rx) = channel();
/// for _ in range(0u, 10) {
/// let (data, tx) = (data.clone(), tx.clone());
/// spawn(move|| {
/// Thread::spawn(move|| {
/// // The shared static can only be accessed once the lock is held.
/// // Our non-atomic increment is safe because we're the only thread
/// // which can access the shared state when the lock is held.
@ -57,7 +58,7 @@ use sys_common::mutex as sys;
/// tx.send(());
/// }
/// // the lock is unlocked here when `data` goes out of scope.
/// });
/// }).detach();
/// }
///
/// rx.recv();
@ -386,7 +387,7 @@ mod test {
fn test_mutex_arc_poison() {
let arc = Arc::new(Mutex::new(1i));
let arc2 = arc.clone();
let _ = Thread::with_join(move|| {
let _ = Thread::spawn(move|| {
let lock = arc2.lock();
assert_eq!(*lock, 2);
}).join();
@ -414,7 +415,7 @@ mod test {
fn test_mutex_arc_access_in_unwind() {
let arc = Arc::new(Mutex::new(1i));
let arc2 = arc.clone();
let _ = Thread::with_join::<()>(move|| -> () {
let _ = Thread::spawn(move|| -> () {
struct Unwinder {
i: Arc<Mutex<int>>,
}

View file

@ -409,7 +409,7 @@ mod tests {
fn test_rw_arc_poison_wr() {
let arc = Arc::new(RWLock::new(1i));
let arc2 = arc.clone();
let _ = Thread::with_join(move|| {
let _ = Thread::spawn(move|| {
let lock = arc2.write();
assert_eq!(*lock, 2);
}).join();
@ -422,7 +422,7 @@ mod tests {
fn test_rw_arc_poison_ww() {
let arc = Arc::new(RWLock::new(1i));
let arc2 = arc.clone();
let _ = Thread::with_join(move|| {
let _ = Thread::spawn(move|| {
let lock = arc2.write();
assert_eq!(*lock, 2);
}).join();
@ -434,7 +434,7 @@ mod tests {
fn test_rw_arc_no_poison_rr() {
let arc = Arc::new(RWLock::new(1i));
let arc2 = arc.clone();
let _ = Thread::with_join(move|| {
let _ = Thread::spawn(move|| {
let lock = arc2.read();
assert_eq!(*lock, 2);
}).join();
@ -445,7 +445,7 @@ mod tests {
fn test_rw_arc_no_poison_rw() {
let arc = Arc::new(RWLock::new(1i));
let arc2 = arc.clone();
let _ = Thread::with_join(move|| {
let _ = Thread::spawn(move|| {
let lock = arc2.read();
assert_eq!(*lock, 2);
}).join();
@ -468,13 +468,13 @@ mod tests {
*lock = tmp + 1;
}
tx.send(());
});
}).detach();
// Readers try to catch the writer in the act
let mut children = Vec::new();
for _ in range(0u, 5) {
let arc3 = arc.clone();
children.push(Thread::with_join(move|| {
children.push(Thread::spawn(move|| {
let lock = arc3.read();
assert!(*lock >= 0);
}));
@ -495,11 +495,7 @@ mod tests {
fn test_rw_arc_access_in_unwind() {
let arc = Arc::new(RWLock::new(1i));
let arc2 = arc.clone();
<<<<<<< HEAD
let _ = task::try(move|| -> () {
=======
let _ = Thread::with_join::<()>(proc() {
>>>>>>> Fallout from new thread API
let _ = Thread::spawn(move|| -> () {
struct Unwinder {
i: Arc<RWLock<int>>,
}

View file

@ -126,7 +126,7 @@ fn spawn_in_pool(jobs: Arc<Mutex<Receiver<Thunk>>>) {
}
sentinel.cancel();
});
}).detach();
}
#[cfg(test)]

View file

@ -10,8 +10,8 @@
use io::{IoResult, Writer};
use iter::{Iterator, IteratorExt};
use option::{Some, None};
use result::{Ok, Err};
use option::Option::{Some, None};
use result::Result::{Ok, Err};
use str::{StrPrelude, from_str};
use unicode::char::UnicodeChar;

View file

@ -82,16 +82,12 @@ impl<M: Send> Helper<M> {
*self.signal.get() = send as uint;
let t = f();
<<<<<<< HEAD
task::spawn(move |:| {
=======
Thread::spawn(proc() {
>>>>>>> Fallout from new thread API
Thread::spawn(move |:| {
helper(receive, rx, t);
let _g = self.lock.lock();
*self.shutdown.get() = true;
self.cond.notify_one()
});
}).detach();
rt::at_exit(move|:| { self.shutdown() });
*self.initialized.get() = true;

View file

@ -14,6 +14,7 @@ use boxed::Box;
use mem;
use uint;
use libc;
use thunk::Thunk;
use sys_common::stack;
use sys::{thread, stack_overflow};
@ -26,8 +27,8 @@ pub fn start_thread(main: *mut libc::c_void) -> thread::rust_thread_return {
unsafe {
stack::record_os_managed_stack_bounds(0, uint::MAX);
let handler = stack_overflow::Handler::new();
let f: Box<proc()> = mem::transmute(main);
(*f)();
let f: Box<Thunk> = mem::transmute(main);
f.invoke(());
drop(handler);
mem::transmute(0 as thread::rust_thread_return)
}

View file

@ -28,6 +28,11 @@ thread_local!(static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(Non
impl ThreadInfo {
fn with<R>(f: |&mut ThreadInfo| -> R) -> R {
if THREAD_INFO.destroyed() {
panic!("Use of std::thread::Thread::current() is not possible after \
the thread's local data has been destroyed");
}
THREAD_INFO.with(|c| {
if c.borrow().is_none() {
*c.borrow_mut() = Some(ThreadInfo {

View file

@ -87,8 +87,8 @@ use c_str::CString;
use io::{IoResult, Writer};
use libc;
use mem;
use option::{Some, None, Option};
use result::{Ok, Err};
use option::Option::{mod, Some, None};
use result::Result::{Ok, Err};
use sync::{StaticMutex, MUTEX_INIT};
use sys_common::backtrace::*;

View file

@ -16,6 +16,7 @@ use mem;
use ptr;
use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
use libc;
use thunk::Thunk;
use sys_common::stack::RED_ZONE;
use sys_common::thread::*;
@ -153,7 +154,7 @@ pub mod guard {
}
}
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(pthread_attr_init(&mut attr), 0);
@ -181,13 +182,13 @@ pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
},
};
let arg: *mut libc::c_void = mem::transmute(p);
let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint
let ret = pthread_create(&mut native, &attr, thread_start, arg);
assert_eq!(pthread_attr_destroy(&mut attr), 0);
if ret != 0 {
// be sure to not leak the closure
let _p: Box<proc():Send> = mem::transmute(arg);
let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg);
panic!("failed to spawn native thread: {}", ret);
}
native

View file

@ -27,9 +27,9 @@ use io::{IoResult, Writer};
use libc;
use mem;
use ops::Drop;
use option::{Some, None};
use option::Option::{Some, None};
use path::Path;
use result::{Ok, Err};
use result::Result::{Ok, Err};
use sync::{StaticMutex, MUTEX_INIT};
use slice::SliceExt;
use str::StrPrelude;

View file

@ -15,7 +15,7 @@ use libc::{mod, c_int};
use c_str::CString;
use mem;
use os::windoze::fill_utf16_buf_and_decode;
use sys::os::fill_utf16_buf_and_decode;
use path;
use ptr;
use str;

View file

@ -24,6 +24,9 @@ use path::{Path, GenericPath, BytesContainer};
use ptr::{mod, RawPtr};
use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
use sys::fs::FileDesc;
use option::Option;
use option::Option::{Some, None};
use slice;
use os::TMPBUF_SZ;
use libc::types::os::arch::extra::DWORD;
@ -138,7 +141,7 @@ pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD) -> Option<String
// set `res` to None and continue.
let s = String::from_utf16(sub)
.expect("fill_utf16_buf_and_decode: closure created invalid UTF-16");
res = option::Some(s)
res = Some(s)
}
}
return res;

View file

@ -34,7 +34,7 @@ impl Drop for Handler {
// It returns the guard page of the current task or 0 if that
// guard page doesn't exist. None is returned if there's currently
// no local task.
unsafe fn get_task_guard_page() -> Option<uint> {
unsafe fn get_task_guard_page() -> uint {
thread_info::stack_guard()
}
@ -55,9 +55,7 @@ extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> L
// however stack checks by limit should be disabled on Windows
stack::record_sp_limit(0);
if get_task_guard_page().is_some() {
report_overflow();
}
report_overflow();
EXCEPTION_CONTINUE_SEARCH
}

View file

@ -17,6 +17,7 @@ use ptr;
use libc;
use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
LPVOID, DWORD, LPDWORD, HANDLE};
use thunk::Thunk;
use sys_common::stack::RED_ZONE;
use sys_common::thread::*;
@ -43,8 +44,8 @@ pub mod guard {
}
}
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
let arg: *mut libc::c_void = mem::transmute(p);
pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
let arg: *mut libc::c_void = mem::transmute(box p);
// FIXME On UNIX, we guard against stack sizes that are too small but
// that's because pthreads enforces that stacks are at least
// PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
@ -60,7 +61,7 @@ pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
if ret as uint == 0 {
// be sure to not leak the closure
let _p: Box<proc():Send> = mem::transmute(arg);
let _p: Box<Thunk> = mem::transmute(arg);
panic!("failed to spawn native thread: {}", ret);
}
return ret;

View file

@ -14,8 +14,7 @@ use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
use mem;
use rt;
use rt::exclusive::Exclusive;
use sync::{ONCE_INIT, Once};
use sync::{ONCE_INIT, Once, Mutex};
pub type Key = DWORD;
pub type Dtor = unsafe extern fn(*mut u8);
@ -55,7 +54,7 @@ pub type Dtor = unsafe extern fn(*mut u8);
// /threading/thread_local_storage_win.cc#L42
static INIT_DTORS: Once = ONCE_INIT;
static mut DTORS: *mut Exclusive<Vec<(Key, Dtor)>> = 0 as *mut _;
static mut DTORS: *mut Mutex<Vec<(Key, Dtor)>> = 0 as *mut _;
// -------------------------------------------------------------------------
// Native bindings
@ -126,13 +125,13 @@ extern "system" {
// FIXME: This could probably be at least a little faster with a BTree.
fn init_dtors() {
let dtors = box Exclusive::new(Vec::<(Key, Dtor)>::new());
let dtors = box Mutex::new(Vec::<(Key, Dtor)>::new());
unsafe {
DTORS = mem::transmute(dtors);
}
rt::at_exit(move|| unsafe {
mem::transmute::<_, Box<Exclusive<Vec<(Key, Dtor)>>>>(DTORS);
mem::transmute::<_, Box<Mutex<Vec<(Key, Dtor)>>>>(DTORS);
DTORS = 0 as *mut _;
});
}

View file

@ -17,21 +17,24 @@ use boxed::Box;
use thread;
use kinds::Send;
use result::Result;
use ops::FnOnce;
/// Deprecate: use `std::thread::Cfg` instead.
#[deprecated = "use std::thread::Cfg instead"]
pub type TaskBuilder = thread::Cfg;
/// Deprecate: use `std::thread::Builder` instead.
#[deprecated = "use std::thread::Builder instead"]
pub type TaskBuilder = thread::Builder;
/// Deprecated: use `std::thread::Thread::spawn` instead.
#[deprecated = "use std::thread::Thread::spawn instead"]
pub fn spawn(f: proc(): Send) {
thread::Thread::spawn(f);
/// Deprecated: use `std::thread::Thread::spawn` and `detach` instead.
#[deprecated = "use std::thread::Thread::spawn and detach instead"]
pub fn spawn<F>(f: F) where F: FnOnce(), F: Send {
thread::Thread::spawn(f).detach();
}
/// Deprecated: use `std::thread::Thread::with_join instead`.
#[deprecated = "use std::thread::Thread::with_join instead"]
pub fn try<T: Send>(f: proc(): Send -> T) -> Result<T, Box<Any + Send>> {
thread::Thread::with_join(f).join()
/// Deprecated: use `std::thread::Thread::spawn` and `join` instead.
#[deprecated = "use std::thread::Thread::spawn and join instead"]
pub fn try<T, F>(f: F) -> Result<T, Box<Any + Send>> where
T: Send, F: FnOnce() -> T, F: Send
{
thread::Thread::spawn(f).join()
}
/// Deprecated: use `std::thread::Thread::yield_now instead`.

View file

@ -15,13 +15,12 @@
//! An executing Rust program consists of a collection of native OS threads,
//! each with their own stack and local state.
//!
//! Threads generally have their memory *isolated* from each other by virtue of
//! Rust's owned types (which of course may only be owned by a single thread at
//! a time). Communication between threads can be done through
//! [channels](../../std/comm/index.html), Rust's message-passing types, along
//! with [other forms of thread synchronization](../../std/sync/index.html) and
//! shared-memory data structures. In particular, types that are guaranteed to
//! be threadsafe are easily shared between threads using the
//! Communication between threads can be done through
//! [channels](../../std/comm/index.html), Rust's message-passing
//! types, along with [other forms of thread
//! synchronization](../../std/sync/index.html) and shared-memory data
//! structures. In particular, types that are guaranteed to be
//! threadsafe are easily shared between threads using the
//! atomically-reference-counted container,
//! [`Arc`](../../std/sync/struct.Arc.html).
//!
@ -54,52 +53,43 @@
//!
//! ## Spawning a thread
//!
//! There are a few different ways to spawn a new thread, depending on how it
//! should relate to the parent thread.
//!
//! ### Simple detached threads
//!
//! The simplest case just spawns a completely independent (detached) thread,
//! returning a new `Thread` handle to it:
//! A new thread can be spawned using the `Thread::spawn` function:
//!
//! ```rust
//! use std::thread::Thread;
//!
//! Thread::spawn(proc() {
//! let guard = Thread::spawn(move || {
//! println!("Hello, World!");
//! })
//! // some computation here
//! });
//! let result = guard.join();
//! ```
//!
//! The spawned thread may outlive its parent.
//! The `spawn` function doesn't return a `Thread` directly; instead, it returns
//! a *join guard* from which a `Thread` can be extracted. The join guard is an
//! RAII-style guard that will automatically join the child thread (block until
//! it terminates) when it is dropped. You can join the child thread in advance
//! by calling the `join` method on the guard, which will also return the result
//! produced by the thread.
//!
//! ### Joining
//! If you instead wish to *detach* the child thread, allowing it to outlive its
//! parent, you can use the `detach` method on the guard,
//!
//! Alternatively, the `with_join` constructor spawns a new thread and returns a
//! `JoinGuard` which can be used to wait until the child thread completes,
//! returning its result (or `Err` if the child thread panicked):
//! A handle to the thread itself is available via the `thread` method on the
//! join guard.
//!
//! ```rust
//! use std::thread::Thread;
//! ## Configuring threads
//!
//! let guard = Thread::with_join(proc() { panic!() };
//! assert!(guard.join().is_err());
//! ```
//!
//! The guard works in RAII style, meaning that the child thread is
//! automatically joined when the guard is dropped. A handle to the thread
//! itself is available via the `thread` method on the guard.
//!
//! ### Configured threads
//!
//! Finally, a new thread can be configured independently of how it is
//! spawned. Configuration is available via the `Cfg` builder, which currently
//! allows you to set the name, stack size, and writers for `println!` and
//! `panic!` for the child thread:
//! A new thread can be configured before it is spawned via the `Builder` type,
//! which currently allows you to set the name, stack size, and writers for
//! `println!` and `panic!` for the child thread:
//!
//! ```rust
//! use std::thread;
//!
//! thread::cfg().name("child1").spawn(proc() { println!("Hello, world!") });
//! thread::Builder::new().name("child1".to_string()).spawn(move || {
//! println!("Hello, world!")
//! }).detach();
//! ```
//!
//! ## Blocking support: park and unpark
@ -139,18 +129,19 @@ use core::prelude::*;
use any::Any;
use borrow::IntoCow;
use boxed::Box;
use mem;
use cell::UnsafeCell;
use sync::{Mutex, Condvar, Arc};
use string::String;
use rt::{mod, unwind};
use io::{Writer, stdio};
use thunk::Thunk;
use sys::thread as imp;
use sys_common::{stack, thread_info};
/// Thread configuation. Provides detailed control over the properties
/// and behavior of new threads.
pub struct Cfg {
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
@ -161,11 +152,11 @@ pub struct Cfg {
stderr: Option<Box<Writer + Send>>,
}
impl Cfg {
impl Builder {
/// Generate the base configuration for spawning a thread, from which
/// configuration methods can be chained.
pub fn new() -> Cfg {
Cfg {
pub fn new() -> Builder {
Builder {
name: None,
stack_size: None,
stdout: None,
@ -175,41 +166,51 @@ impl Cfg {
/// Name the thread-to-be. Currently the name is used for identification
/// only in panic messages.
pub fn name(mut self, name: String) -> Cfg {
pub fn name(mut self, name: String) -> Builder {
self.name = Some(name);
self
}
/// Deprecated: use `name` instead
#[deprecated = "use name instead"]
pub fn named<T: IntoCow<'static, String, str>>(self, name: T) -> Cfg {
pub fn named<T: IntoCow<'static, String, str>>(self, name: T) -> Builder {
self.name(name.into_cow().into_owned())
}
/// Set the size of the stack for the new thread.
pub fn stack_size(mut self, size: uint) -> Cfg {
pub fn stack_size(mut self, size: uint) -> Builder {
self.stack_size = Some(size);
self
}
/// Redirect thread-local stdout.
#[experimental = "Will likely go away after proc removal"]
pub fn stdout(mut self, stdout: Box<Writer + Send>) -> Cfg {
pub fn stdout(mut self, stdout: Box<Writer + Send>) -> Builder {
self.stdout = Some(stdout);
self
}
/// Redirect thread-local stderr.
#[experimental = "Will likely go away after proc removal"]
pub fn stderr(mut self, stderr: Box<Writer + Send>) -> Cfg {
pub fn stderr(mut self, stderr: Box<Writer + Send>) -> Builder {
self.stderr = Some(stderr);
self
}
fn core_spawn<T: Send>(self, f: proc():Send -> T, after: proc(Result<T>):Send)
-> (imp::rust_thread, Thread)
/// Spawn a new joinable thread, and return a JoinGuard guard for it.
///
/// See `Thead::spawn` and the module doc for more details.
pub fn spawn<T, F>(self, f: F) -> JoinGuard<T> where
T: Send, F: FnOnce() -> T, F: Send
{
let Cfg { name, stack_size, stdout, stderr } = self;
self.spawn_inner(Thunk::new(f))
}
fn spawn_inner<T: Send>(self, f: Thunk<(), T>) -> JoinGuard<T> {
let my_packet = Arc::new(UnsafeCell::new(None));
let their_packet = my_packet.clone();
let Builder { name, stack_size, stdout, stderr } = self;
let stack_size = stack_size.unwrap_or(rt::min_stack());
let my_thread = Thread::new(name);
@ -221,7 +222,7 @@ impl Cfg {
// because by the time that this function is executing we've already
// consumed at least a little bit of stack (we don't know the exact byte
// address at which our stack started).
let main = proc() {
let main = move |:| {
let something_around_the_top_of_the_stack = 1;
let addr = &something_around_the_top_of_the_stack as *const int;
let my_stack_top = addr as uint;
@ -235,74 +236,49 @@ impl Cfg {
their_thread
);
// There are two primary reasons that general try/catch is
// unsafe. The first is that we do not support nested try/catch. The
// fact that this is happening in a newly-spawned thread
// suffices. The second is that unwinding while unwinding is not
// defined. We take care of that by having an 'unwinding' flag in
// the thread itself. For these reasons, this unsafety should be ok.
unsafe {
let mut output = None;
let f = if stdout.is_some() || stderr.is_some() {
proc() {
let _ = stdout.map(stdio::set_stdout);
let _ = stderr.map(stdio::set_stderr);
f()
}
} else {
f
};
let mut output = None;
let f: Thunk<(), T> = if stdout.is_some() || stderr.is_some() {
Thunk::new(move |:| {
let _ = stdout.map(stdio::set_stdout);
let _ = stderr.map(stdio::set_stderr);
f.invoke(())
})
} else {
f
};
let try_result = {
let ptr = &mut output;
unwind::try(move || *ptr = Some(f()))
};
match (output, try_result) {
(Some(data), Ok(_)) => after(Ok(data)),
(None, Err(cause)) => after(Err(cause)),
_ => unreachable!()
let try_result = {
let ptr = &mut output;
// There are two primary reasons that general try/catch is
// unsafe. The first is that we do not support nested
// try/catch. The fact that this is happening in a newly-spawned
// thread suffices. The second is that unwinding while unwinding
// is not defined. We take care of that by having an
// 'unwinding' flag in the thread itself. For these reasons,
// this unsafety should be ok.
unsafe {
unwind::try(move || *ptr = Some(f.invoke(())))
}
};
unsafe {
*their_packet.get() = Some(match (output, try_result) {
(Some(data), Ok(_)) => Ok(data),
(None, Err(cause)) => Err(cause),
_ => unreachable!()
});
}
};
(unsafe { imp::create(stack_size, box main) }, my_thread)
}
/// Spawn a detached thread, and return a handle to it.
///
/// The new child thread may outlive its parent.
pub fn spawn(self, f: proc():Send) -> Thread {
let (native, thread) = self.core_spawn(f, proc(_) {});
unsafe { imp::detach(native) };
thread
}
/// Spawn a joinable thread, and return an RAII guard for it.
pub fn with_join<T: Send>(self, f: proc():Send -> T) -> JoinGuard<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 any: Box<Any+Send> = box 0u8; // sentinel value
let my_packet = box Err(any);
let their_packet: *mut Result<T> = unsafe {
*mem::transmute::<&Box<Result<T>>, *const *mut Result<T>>(&my_packet)
};
let (native, thread) = self.core_spawn(f, proc(result) {
unsafe { *their_packet = result; }
});
JoinGuard {
native: native,
native: unsafe { imp::create(stack_size, Thunk::new(main)) },
joined: false,
packet: Some(my_packet),
thread: thread,
packet: my_packet,
thread: my_thread,
}
}
}
/// A convenience function for creating configurations.
pub fn cfg() -> Cfg { Cfg::new() }
struct Inner {
name: Option<String>,
lock: Mutex<bool>, // true when there is a buffered unpark
@ -316,6 +292,7 @@ pub struct Thread {
}
impl Thread {
// Used only internally to construct a thread object without spawning
fn new(name: Option<String>) -> Thread {
Thread {
inner: Arc::new(Inner {
@ -326,16 +303,16 @@ impl Thread {
}
}
/// Spawn a detached thread, and return a handle to it.
/// Spawn a new joinable thread, returning a `JoinGuard` for it.
///
/// The new child thread may outlive its parent.
pub fn spawn(f: proc():Send) -> Thread {
Cfg::new().spawn(f)
}
/// Spawn a joinable thread, and return an RAII guard for it.
pub fn with_join<T: Send>(f: proc():Send -> T) -> JoinGuard<T> {
Cfg::new().with_join(f)
/// The join guard can be used to explicitly join the child thead (via
/// `join`), returning `Result<T>`, or it will implicitly join the child
/// upon being dropped. To detach the child, allowing it to outlive the
/// current thread, use `detach`. See the module documentation for additional details.
pub fn spawn<T, F>(f: F) -> JoinGuard<T> where
T: Send, F: FnOnce() -> T, F: Send
{
Builder::new().spawn(f)
}
/// Gets a handle to the thread that invokes it.
@ -353,10 +330,15 @@ impl Thread {
thread_info::panicking()
}
// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp
/// Block unless or until the current thread's token is made available (may wake spuriously).
///
/// See the module doc for more detail.
//
// 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.
pub fn park() {
let thread = Thread::current();
let mut guard = thread.inner.lock.lock();
@ -394,43 +376,52 @@ impl thread_info::NewThread for Thread {
pub type Result<T> = ::result::Result<T, Box<Any + Send>>;
#[must_use]
/// An RAII guard that will block until thread termination when dropped.
/// An RAII-style guard that will block until thread termination when dropped.
///
/// The type `T` is the return type for the thread's main function.
pub struct JoinGuard<T> {
native: imp::rust_thread,
thread: Thread,
joined: bool,
packet: Option<Box<Result<T>>>,
packet: Arc<UnsafeCell<Option<Result<T>>>>,
}
impl<T: Send> JoinGuard<T> {
/// Extract a handle to the thread this guard will join on.
pub fn thread(&self) -> Thread {
self.thread.clone()
pub fn thread(&self) -> &Thread {
&self.thread
}
/// Wait for the associated thread to finish, returning the result of the thread's
/// calculation.
///
/// If the child thread panics, `Err` is returned with the parameter given
/// to `panic`.
pub fn join(mut self) -> Result<T> {
assert!(!self.joined);
unsafe { imp::join(self.native) };
self.joined = true;
let box res = self.packet.take().unwrap();
res
unsafe {
(*self.packet.get()).take().unwrap()
}
}
/// Detaches the child thread, allowing it to outlive its parent.
pub fn detach(mut self) {
unsafe { imp::detach(self.native) };
self.joined = true; // avoid joining in the destructor
}
}
#[unsafe_destructor]
impl<T: Send> Drop for JoinGuard<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) };
}
}
}
// TODO: fix tests
#[cfg(test)]
mod test {
use any::{Any, AnyRefExt};
@ -440,21 +431,22 @@ mod test {
use result;
use std::io::{ChanReader, ChanWriter};
use string::String;
use super::{Thread, cfg};
use thunk::Thunk;
use super::{Thread, Builder};
// !!! 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::with_join(proc() {
Thread::spawn(move|| {
assert!(Thread::current().name().is_none());
}).join().map_err(|_| ()).unwrap();
}
#[test]
fn test_named_thread() {
cfg().name("ada lovelace".to_string()).with_join(proc() {
Builder::new().name("ada lovelace".to_string()).spawn(move|| {
assert!(Thread::current().name().unwrap() == "ada lovelace".to_string());
}).join().map_err(|_| ()).unwrap();
}
@ -462,15 +454,15 @@ mod test {
#[test]
fn test_run_basic() {
let (tx, rx) = channel();
Thread::spawn(proc() {
Thread::spawn(move|| {
tx.send(());
});
}).detach();
rx.recv();
}
#[test]
fn test_join_success() {
match Thread::with_join::<String>(proc() {
match Thread::spawn(move|| -> String {
"Success!".to_string()
}).join().as_ref().map(|s| s.as_slice()) {
result::Result::Ok("Success!") => (),
@ -480,7 +472,7 @@ mod test {
#[test]
fn test_join_panic() {
match Thread::with_join(proc() {
match Thread::spawn(move|| {
panic!()
}).join() {
result::Result::Err(_) => (),
@ -496,13 +488,13 @@ mod test {
fn f(i: int, tx: Sender<()>) {
let tx = tx.clone();
Thread::spawn(proc() {
Thread::spawn(move|| {
if i == 0 {
tx.send(());
} else {
f(i - 1, tx);
}
});
}).detach();
}
f(10, tx);
@ -513,25 +505,25 @@ mod test {
fn test_spawn_sched_childs_on_default_sched() {
let (tx, rx) = channel();
Thread::spawn(proc() {
Thread::spawn(proc() {
Thread::spawn(move|| {
Thread::spawn(move|| {
tx.send(());
});
});
}).detach();
}).detach();
rx.recv();
}
fn avoid_copying_the_body(spawnfn: |v: proc():Send|) {
fn avoid_copying_the_body<F>(spawnfn: F) where F: FnOnce(Thunk) {
let (tx, rx) = channel::<uint>();
let x = box 1;
let x_in_parent = (&*x) as *const int as uint;
spawnfn(proc() {
spawnfn(Thunk::new(move|| {
let x_in_child = (&*x) as *const int as uint;
tx.send(x_in_child);
});
}));
let x_in_child = rx.recv();
assert_eq!(x_in_parent, x_in_child);
@ -539,24 +531,25 @@ mod test {
#[test]
fn test_avoid_copying_the_body_spawn() {
avoid_copying_the_body(|v| { Thread::spawn(v); });
avoid_copying_the_body(|v| {
Thread::spawn(move || v.invoke(())).detach();
});
}
#[test]
fn test_avoid_copying_the_body_thread_spawn() {
avoid_copying_the_body(|f| {
let builder = cfg();
builder.spawn(proc() {
f();
});
Thread::spawn(move|| {
f.invoke(());
}).detach();
})
}
#[test]
fn test_avoid_copying_the_body_join() {
avoid_copying_the_body(|f| {
let _ = Thread::with_join(proc() {
f()
let _ = Thread::spawn(move|| {
f.invoke(())
}).join();
})
}
@ -568,24 +561,24 @@ mod test {
// (well, it would if the constant were 8000+ - I lowered it to be more
// valgrind-friendly. try this at home, instead..!)
static GENERATIONS: uint = 16;
fn child_no(x: uint) -> proc(): Send {
return proc() {
fn child_no(x: uint) -> Thunk {
return Thunk::new(move|| {
if x < GENERATIONS {
Thread::spawn(child_no(x+1));
Thread::spawn(move|| child_no(x+1).invoke(())).detach();
}
}
});
}
Thread::spawn(child_no(0));
Thread::spawn(|| child_no(0).invoke(())).detach();
}
#[test]
fn test_simple_newsched_spawn() {
Thread::spawn(proc()());
Thread::spawn(move || {}).detach();
}
#[test]
fn test_try_panic_message_static_str() {
match Thread::with_join(proc() {
match Thread::spawn(move|| {
panic!("static string");
}).join() {
Err(e) => {
@ -599,7 +592,7 @@ mod test {
#[test]
fn test_try_panic_message_owned_str() {
match Thread::with_join(proc() {
match Thread::spawn(move|| {
panic!("owned string".to_string());
}).join() {
Err(e) => {
@ -613,7 +606,7 @@ mod test {
#[test]
fn test_try_panic_message_any() {
match Thread::with_join(proc() {
match Thread::spawn(move|| {
panic!(box 413u16 as Box<Any + Send>);
}).join() {
Err(e) => {
@ -631,7 +624,7 @@ mod test {
fn test_try_panic_message_unit_struct() {
struct Juju;
match Thread::with_join(proc() {
match Thread::spawn(move|| {
panic!(Juju)
}).join() {
Err(ref e) if e.is::<Juju>() => {}
@ -645,9 +638,9 @@ mod test {
let mut reader = ChanReader::new(rx);
let stdout = ChanWriter::new(tx);
let r = cfg().stdout(box stdout as Box<Writer + Send>).with_join(proc() {
print!("Hello, world!");
}).join();
let r = Builder::new().stdout(box stdout as Box<Writer + Send>).spawn(move|| {
print!("Hello, world!");
}).join();
assert!(r.is_ok());
let output = reader.read_to_string().unwrap();

View file

@ -68,6 +68,7 @@ pub mod scoped;
///
/// ```
/// use std::cell::RefCell;
/// use std::thread::Thread;
///
/// thread_local!(static FOO: RefCell<uint> = RefCell::new(1));
///
@ -77,12 +78,12 @@ pub mod scoped;
/// });
///
/// // each thread starts out with the initial value of 1
/// spawn(move|| {
/// Thread::spawn(move|| {
/// FOO.with(|f| {
/// assert_eq!(*f.borrow(), 1);
/// *f.borrow_mut() = 3;
/// });
/// });
/// }).detach();
///
/// // we retain our original value of 2 despite the child thread
/// FOO.with(|f| {
@ -533,7 +534,7 @@ mod tests {
}
}
Thread::with_join(move|| {
Thread::spawn(move|| {
drop(S1);
}).join();
}
@ -551,7 +552,7 @@ mod tests {
}
}
Thread::with_join(move|| unsafe {
Thread::spawn(move|| unsafe {
K1.with(|s| *s.get() = Some(S1));
}).join();
}

View file

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Because this module is temporary...
#![allow(missing_docs)]
use alloc::boxed::Box;
use core::kinds::Send;
use core::ops::FnOnce;

View file

@ -1126,7 +1126,7 @@ pub fn run_test(opts: &TestOpts,
let mut reader = ChanReader::new(rx);
let stdout = ChanWriter::new(tx.clone());
let stderr = ChanWriter::new(tx);
let mut cfg = thread::cfg().name(match desc.name {
let mut cfg = thread::Builder::new().name(match desc.name {
DynTestName(ref name) => name.clone().to_string(),
StaticTestName(name) => name.to_string(),
});
@ -1137,11 +1137,11 @@ pub fn run_test(opts: &TestOpts,
cfg = cfg.stderr(box stderr as Box<Writer + Send>);
}
let result_guard = cfg.with_join(testfn);
let result_guard = cfg.spawn(move || { testfn.invoke(()) });
let stdout = reader.read_to_end().unwrap().into_iter().collect();
let test_result = calc_result(&desc, result_guard.join());
monitor_ch.send((desc.clone(), test_result, stdout));
});
}).detach();
}
match testfn {

View file

@ -20,7 +20,7 @@
use std::comm;
use std::os;
use std::task;
use std::thread::Thread;
use std::time::Duration;
use std::uint;
@ -64,7 +64,7 @@ fn run(args: &[String]) {
let mut worker_results = Vec::new();
for _ in range(0u, workers) {
let to_child = to_child.clone();
worker_results.push(task::try_future(move|| {
worker_results.push(Thread::spawn(move|| {
for _ in range(0u, size / workers) {
//println!("worker {}: sending {} bytes", i, num_bytes);
to_child.send(request::bytes(num_bytes));
@ -72,12 +72,12 @@ fn run(args: &[String]) {
//println!("worker {} exiting", i);
}));
}
task::spawn(move|| {
Thread::spawn(move|| {
server(&from_parent, &to_parent);
});
}).detach();
for r in worker_results.into_iter() {
r.unwrap();
let _ = r.join();
}
//println!("sending stop message");

View file

@ -15,7 +15,7 @@
// I *think* it's the same, more or less.
use std::os;
use std::task;
use std::thread::Thread;
use std::time::Duration;
use std::uint;
@ -58,7 +58,7 @@ fn run(args: &[String]) {
let mut worker_results = Vec::new();
let from_parent = if workers == 1 {
let (to_child, from_parent) = channel();
worker_results.push(task::try_future(move|| {
worker_results.push(Thread::spawn(move|| {
for _ in range(0u, size / workers) {
//println!("worker {}: sending {} bytes", i, num_bytes);
to_child.send(request::bytes(num_bytes));
@ -70,7 +70,7 @@ fn run(args: &[String]) {
let (to_child, from_parent) = channel();
for _ in range(0u, workers) {
let to_child = to_child.clone();
worker_results.push(task::try_future(move|| {
worker_results.push(Thread::spawn(move|| {
for _ in range(0u, size / workers) {
//println!("worker {}: sending {} bytes", i, num_bytes);
to_child.send(request::bytes(num_bytes));
@ -80,12 +80,12 @@ fn run(args: &[String]) {
}
from_parent
};
task::spawn(move|| {
Thread::spawn(move|| {
server(&from_parent, &to_parent);
});
}).detach();
for r in worker_results.into_iter() {
r.unwrap();
let _ = r.join();
}
//println!("sending stop message");

View file

@ -22,7 +22,7 @@ extern crate getopts;
use std::os;
use std::result::Result::{Ok, Err};
use std::task;
use std::thread::Thread;
use std::time::Duration;
fn fib(n: int) -> int {
@ -34,15 +34,15 @@ fn fib(n: int) -> int {
} else {
let (tx1, rx) = channel();
let tx2 = tx1.clone();
task::spawn(move|| pfib(&tx2, n - 1));
Thread::spawn(move|| pfib(&tx2, n - 1)).detach();
let tx2 = tx1.clone();
task::spawn(move|| pfib(&tx2, n - 2));
Thread::spawn(move|| pfib(&tx2, n - 2)).detach();
tx.send(rx.recv() + rx.recv());
}
}
let (tx, rx) = channel();
spawn(move|| pfib(&tx, n) );
Thread::spawn(move|| pfib(&tx, n) ).detach();
rx.recv()
}
@ -77,12 +77,12 @@ fn stress_task(id: int) {
fn stress(num_tasks: int) {
let mut results = Vec::new();
for i in range(0, num_tasks) {
results.push(task::try_future(move|| {
results.push(Thread::spawn(move|| {
stress_task(i);
}));
}
for r in results.into_iter() {
r.unwrap();
let _ = r.join();
}
}

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:task '<main>' panicked at
// error-pattern:thread '<main>' panicked at
fn main() {
panic!()

View file

@ -8,14 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:task '<unnamed>' panicked at 'test'
// error-pattern:thread '<unnamed>' panicked at 'test'
use std::task;
use std::thread::Thread;
fn main() {
let r: Result<int,_> = task::try(move|| {
let r: Result<int,_> = Thread::spawn(move|| {
panic!("test");
1i
});
}).join();
assert!(r.is_ok());
}

View file

@ -8,15 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:task 'owned name' panicked at 'test'
// error-pattern:thread 'owned name' panicked at 'test'
use std::task::TaskBuilder;
use std::thread::Builder;
fn main() {
let r: Result<int,_> = TaskBuilder::new().named("owned name".to_string())
.try(move|| {
let r: Result<int,_> = Builder::new().name("owned name".to_string()).spawn(move|| {
panic!("test");
1i
});
}).join();
assert!(r.is_ok());
}

View file

@ -1,21 +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.
// error-pattern:task 'send name' panicked at 'test'
fn main() {
let r: Result<int,_> =
::std::task::TaskBuilder::new().named("send name".into_cow())
.try(move|| {
panic!("test");
3i
});
assert!(r.is_ok());
}

View file

@ -1,19 +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.
// error-pattern:task 'static name' panicked at 'test'
fn main() {
let r: Result<int,_> =
::std::task::TaskBuilder::new().named("static name").try(move|| {
panic!("test");
});
assert!(r.is_ok());
}

View file

@ -9,7 +9,7 @@
// except according to those terms.
// check-stdout
// error-pattern:task 'test_foo' panicked at
// error-pattern:thread 'test_foo' panicked at
// compile-flags: --test
// ignore-pretty: does not work well with `--test`
@ -17,4 +17,3 @@
fn test_foo() {
panic!()
}

View file

@ -9,7 +9,7 @@
// except according to those terms.
// check-stdout
// error-pattern:task 'test_foo' panicked at
// error-pattern:thread 'test_foo' panicked at
// compile-flags: --test
// ignore-pretty: does not work well with `--test`
@ -18,5 +18,3 @@
fn test_foo() {
panic!("blah")
}

View file

@ -1,12 +0,0 @@
-include ../tools.mk
HOST_LIB_DIR=$(TMPDIR)/../../../stage$(RUST_BUILD_STAGE)/lib
# This overrides the LD_LIBRARY_PATH for RUN
TARGET_RPATH_DIR:=$(TARGET_RPATH_DIR):$(TMPDIR)
all:
$(RUSTC) lib.rs
$(CC) main.c -o $(call RUN_BINFILE,main) $(call RPATH_LINK_SEARCH,$(HOST_LIB_DIR)) -lboot $(EXTRACFLAGS)
$(call RUN,main)
$(call REMOVE_DYLIBS,boot)
$(call FAIL,main)

View file

@ -1,24 +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.
#![crate_name="boot"]
#![crate_type="dylib"]
use std::rt;
use std::thunk::Thunk;
#[no_mangle] // this needs to get called from C
pub extern "C" fn foo(argc: int, argv: *const *const u8) -> int {
rt::start(argc, argv, Thunk::new(move|| {
spawn(move|| {
println!("hello");
});
}))
}

View file

@ -1,16 +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.
// this is the rust entry point that we're going to call.
int foo(int argc, char *argv[]);
int main(int argc, char *argv[]) {
return foo(argc, argv);
}

View file

@ -45,5 +45,5 @@ pub fn fails() {
}
pub fn main() {
Thread::with_join(fails).join();
Thread::spawn(fails).join();
}

View file

@ -11,7 +11,7 @@
extern crate libc;
use std::mem;
use std::rt::thread::Thread;
use std::thread::Thread;
#[link(name = "rust_test_helpers")]
extern {
@ -21,7 +21,7 @@ extern {
pub fn main() {
unsafe {
Thread::start(move|| {
Thread::spawn(move|| {
let i = &100i;
rust_dbg_call(callback, mem::transmute(i));
}).join();

View file

@ -18,9 +18,11 @@
// A var moved into a proc, that has a mutable loan path should
// not trigger a misleading unused_mut warning.
use std::thread::Thread;
pub fn main() {
let mut stdin = std::io::stdin();
spawn(move|| {
Thread::spawn(move|| {
let _ = stdin.read_to_end();
});
}).detach();
}

View file

@ -8,13 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::task::TaskBuilder;
use std::thread::Builder;
use std::thunk::Thunk;
static generations: uint = 1024+256+128+49;
fn spawn(f: Thunk) {
TaskBuilder::new().stack_size(32 * 1024).spawn(move|| f.invoke(()))
Builder::new().stack_size(32 * 1024).spawn(move|| f.invoke(())).detach()
}
fn child_no(x: uint) -> Thunk {

View file

@ -8,11 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::rt::exclusive;
use std::sync::Mutex;
pub fn main() {
unsafe {
let x = Some(exclusive::Exclusive::new(true));
let x = Some(Mutex::new(true));
match x {
Some(ref z) if *z.lock() => {
assert!(*z.lock());

View file

@ -33,11 +33,11 @@ fn start(argc: int, argv: *const *const u8) -> int {
return 0
}
rt::start(argc, argv, Thunk::new(main))
}
fn main() {
let args = os::args();
let args = unsafe {
Vec::from_fn(argc as uint, |i| {
String::from_raw_buf(*argv.offset(i as int)).into_bytes()
})
};
let me = args[0].as_slice();
let x: &[u8] = &[1u8];
@ -52,6 +52,8 @@ fn main() {
pass(Command::new(me).arg(x).output().unwrap());
let x: &[u8] = &[6u8];
pass(Command::new(me).arg(x).output().unwrap());
0
}
fn pass(output: ProcessOutput) {

View file

@ -13,9 +13,9 @@
// regression test for issue #10405, make sure we don't call println! too soon.
use std::task::TaskBuilder;
use std::thread::Builder;
pub fn main() {
let mut t = TaskBuilder::new();
t.spawn(move|| ());
let mut t = Builder::new();
t.spawn(move|| ()).detach();
}

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::task;
use std::thread::Thread;
pub fn main() { test00(); }
@ -16,19 +16,19 @@ fn start(_task_number: int) { println!("Started / Finished task."); }
fn test00() {
let i: int = 0;
let mut result = task::try_future(move|| {
let mut result = Thread::spawn(move|| {
start(i)
});
// Sleep long enough for the task to finish.
let mut i = 0u;
while i < 10000 {
task::deschedule();
Thread::yield_now();
i += 1;
}
// Try joining tasks that have already finished.
result.unwrap();
result.join();
println!("Joined task.");
}

View file

@ -10,7 +10,7 @@
// no-pretty-expanded FIXME #15189
use std::task;
use std::thread::Thread;
pub fn main() { println!("===== WITHOUT THREADS ====="); test00(); }
@ -39,7 +39,7 @@ fn test00() {
let mut results = Vec::new();
while i < number_of_tasks {
let tx = tx.clone();
results.push(task::try_future({
results.push(Thread::spawn({
let i = i;
move|| {
test00_start(&tx, i, number_of_messages)
@ -60,7 +60,7 @@ fn test00() {
}
// Join spawned tasks...
for r in results.iter_mut() { r.get_ref(); }
for r in results.into_iter() { r.join(); }
println!("Completed: Final number is: ");
println!("{}", sum);

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::task;
use std::thread::Thread;
pub fn main() { test00(); }
@ -23,7 +23,7 @@ fn test00() {
let (tx, rx) = channel();
let number_of_messages: int = 10;
let result = task::try_future(move|| {
let result = Thread::spawn(move|| {
test00_start(&tx, number_of_messages);
});
@ -34,7 +34,7 @@ fn test00() {
i += 1;
}
result.unwrap();
result.join();
assert_eq!(sum, number_of_messages * (number_of_messages - 1) / 2);
}

View file

@ -16,7 +16,7 @@ fn main() {
let mut reader = ChanReader::new(rx);
let stderr = ChanWriter::new(tx);
let res = thread::cfg().stderr(box stderr as Box<Writer + Send>).with_join(move|| -> () {
let res = thread::Builder::new().stderr(box stderr as Box<Writer + Send>).spawn(move|| -> () {
panic!("Hello, world!")
}).join();
assert!(res.is_err());

View file

@ -19,7 +19,7 @@ extern crate libc;
use std::io::net::tcp::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
use std::task::TaskBuilder;
use std::thread::Builder;
use std::time::Duration;
fn main() {
@ -53,7 +53,7 @@ fn main() {
let (tx, rx) = channel();
for _ in range(0u, 1000) {
let tx = tx.clone();
TaskBuilder::new().stack_size(64 * 1024).spawn(move|| {
Builder::new().stack_size(64 * 1024).spawn(move|| {
match TcpStream::connect(addr) {
Ok(stream) => {
let mut stream = stream;
@ -64,7 +64,7 @@ fn main() {
Err(e) => debug!("{}", e)
}
tx.send(());
});
}).detach();
}
// Wait for all clients to exit, but don't wait for the server to exit. The

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::rt::exclusive;
use std::sync::Mutex;
struct Point {x: int, y: int, z: int}
@ -16,7 +16,7 @@ fn f(p: &mut Point) { p.z = 13; }
pub fn main() {
unsafe {
let x = Some(exclusive::Exclusive::new(true));
let x = Some(Mutex::new(true));
match x {
Some(ref z) if *z.lock() => {
assert!(*z.lock());

View file

@ -8,18 +8,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::task;
use std::thread::Thread;
pub fn main() {
let mut result = task::try_future(child);
let mut result = Thread::spawn(child);
println!("1");
task::deschedule();
Thread::yield_now();
println!("2");
task::deschedule();
Thread::yield_now();
println!("3");
result.unwrap();
result.join();
}
fn child() {
println!("4"); task::deschedule(); println!("5"); task::deschedule(); println!("6");
println!("4"); Thread::yield_now(); println!("5"); Thread::yield_now(); println!("6");
}

View file

@ -8,13 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::task;
use std::thread::Thread;
pub fn main() {
let mut result = task::try_future(child);
let mut result = Thread::spawn(child);
println!("1");
task::deschedule();
result.unwrap();
Thread::yield_now();
result.join();
}
fn child() { println!("2"); }