s/task/thread/g

A part of #20038
This commit is contained in:
Steve Klabnik 2014-12-26 16:04:27 -05:00
parent d10642ef0f
commit b8ffad5964
11 changed files with 233 additions and 240 deletions

View File

@ -1,6 +1,6 @@
% The Rust Tasks and Communication Guide % The Rust Threads and Communication Guide
**NOTE** This guide is badly out of date an needs to be rewritten. **NOTE** This guide is badly out of date and needs to be rewritten.
# Introduction # Introduction
@ -9,36 +9,36 @@ primitives. This guide will describe the concurrency model in Rust, how it
relates to the Rust type system, and introduce the fundamental library relates to the Rust type system, and introduce the fundamental library
abstractions for constructing concurrent programs. abstractions for constructing concurrent programs.
Tasks provide failure isolation and recovery. When a fatal error occurs in Rust Threads provide failure isolation and recovery. When a fatal error occurs in Rust
code as a result of an explicit call to `panic!()`, an assertion failure, or code as a result of an explicit call to `panic!()`, an assertion failure, or
another invalid operation, the runtime system destroys the entire task. Unlike another invalid operation, the runtime system destroys the entire thread. Unlike
in languages such as Java and C++, there is no way to `catch` an exception. in languages such as Java and C++, there is no way to `catch` an exception.
Instead, tasks may monitor each other to see if they panic. Instead, threads may monitor each other to see if they panic.
Tasks use Rust's type system to provide strong memory safety guarantees. In Threads use Rust's type system to provide strong memory safety guarantees. In
particular, the type system guarantees that tasks cannot induce a data race particular, the type system guarantees that threads cannot induce a data race
from shared mutable state. from shared mutable state.
# Basics # Basics
At its simplest, creating a task is a matter of calling the `spawn` function At its simplest, creating a thread is a matter of calling the `spawn` function
with a closure argument. `spawn` executes the closure in the new task. with a closure argument. `spawn` executes the closure in the new thread.
```{rust,ignore} ```{rust,ignore}
# use std::task::spawn; # use std::thread::spawn;
// Print something profound in a different task using a named function // Print something profound in a different thread using a named function
fn print_message() { println!("I am running in a different task!"); } fn print_message() { println!("I am running in a different thread!"); }
spawn(print_message); spawn(print_message);
// Alternatively, use a `move ||` expression instead of a named function. // Alternatively, use a `move ||` expression instead of a named function.
// `||` expressions evaluate to an unnamed closure. The `move` keyword // `||` expressions evaluate to an unnamed closure. The `move` keyword
// indicates that the closure should take ownership of any variables it // indicates that the closure should take ownership of any variables it
// touches. // touches.
spawn(move || println!("I am also running in a different task!")); spawn(move || println!("I am also running in a different thread!"));
``` ```
In Rust, a task is not a concept that appears in the language semantics. In Rust, a thread is not a concept that appears in the language semantics.
Instead, Rust's type system provides all the tools necessary to implement safe Instead, Rust's type system provides all the tools necessary to implement safe
concurrency: particularly, ownership. The language leaves the implementation concurrency: particularly, ownership. The language leaves the implementation
details to the standard library. details to the standard library.
@ -49,26 +49,26 @@ argument a closure (of type `F`) that it will run exactly once. This
closure is limited to capturing `Send`-able data from its environment closure is limited to capturing `Send`-able data from its environment
(that is, data which is deeply owned). Limiting the closure to `Send` (that is, data which is deeply owned). Limiting the closure to `Send`
ensures that `spawn` can safely move the entire closure and all its ensures that `spawn` can safely move the entire closure and all its
associated state into an entirely different task for execution. associated state into an entirely different thread for execution.
```{rust,ignore} ```{rust,ignore}
# use std::task::spawn; # use std::thread::spawn;
# fn generate_task_number() -> int { 0 } # fn generate_thread_number() -> int { 0 }
// Generate some state locally // Generate some state locally
let child_task_number = generate_task_number(); let child_thread_number = generate_thread_number();
spawn(move || { spawn(move || {
// Capture it in the remote task. The `move` keyword indicates // Capture it in the remote thread. The `move` keyword indicates
// that this closure should move `child_task_number` into its // that this closure should move `child_thread_number` into its
// environment, rather than capturing a reference into the // environment, rather than capturing a reference into the
// enclosing stack frame. // enclosing stack frame.
println!("I am child number {}", child_task_number); println!("I am child number {}", child_thread_number);
}); });
``` ```
## Communication ## Communication
Now that we have spawned a new task, it would be nice if we could communicate Now that we have spawned a new thread, it would be nice if we could communicate
with it. For this, we use *channels*. A channel is simply a pair of endpoints: with it. For this, we use *channels*. A channel is simply a pair of endpoints:
one for sending messages and another for receiving messages. one for sending messages and another for receiving messages.
@ -78,7 +78,7 @@ of a channel, and a **receiver** is the receiving endpoint. Consider the followi
example of calculating two results concurrently: example of calculating two results concurrently:
```{rust,ignore} ```{rust,ignore}
# use std::task::spawn; # use std::thread::spawn;
let (tx, rx): (Sender<int>, Receiver<int>) = channel(); let (tx, rx): (Sender<int>, Receiver<int>) = channel();
@ -102,12 +102,12 @@ into its component parts).
let (tx, rx): (Sender<int>, Receiver<int>) = channel(); let (tx, rx): (Sender<int>, Receiver<int>) = channel();
``` ```
The child task will use the sender to send data to the parent task, which will The child thread will use the sender to send data to the parent thread, which will
wait to receive the data on the receiver. The next statement spawns the child wait to receive the data on the receiver. The next statement spawns the child
task. thread.
```{rust,ignore} ```{rust,ignore}
# use std::task::spawn; # use std::thread::spawn;
# fn some_expensive_computation() -> int { 42 } # fn some_expensive_computation() -> int { 42 }
# let (tx, rx) = channel(); # let (tx, rx) = channel();
spawn(move || { spawn(move || {
@ -116,10 +116,10 @@ spawn(move || {
}); });
``` ```
Notice that the creation of the task closure transfers `tx` to the child task Notice that the creation of the thread closure transfers `tx` to the child thread
implicitly: the closure captures `tx` in its environment. Both `Sender` and implicitly: the closure captures `tx` in its environment. Both `Sender` and
`Receiver` are sendable types and may be captured into tasks or otherwise `Receiver` are sendable types and may be captured into threads or otherwise
transferred between them. In the example, the child task runs an expensive transferred between them. In the example, the child thread runs an expensive
computation, then sends the result over the captured channel. computation, then sends the result over the captured channel.
Finally, the parent continues with some other expensive computation, then waits Finally, the parent continues with some other expensive computation, then waits
@ -137,7 +137,7 @@ The `Sender` and `Receiver` pair created by `channel` enables efficient
communication between a single sender and a single receiver, but multiple communication between a single sender and a single receiver, but multiple
senders cannot use a single `Sender` value, and multiple receivers cannot use a senders cannot use a single `Sender` value, and multiple receivers cannot use a
single `Receiver` value. What if our example needed to compute multiple single `Receiver` value. What if our example needed to compute multiple
results across a number of tasks? The following program is ill-typed: results across a number of threads? The following program is ill-typed:
```{rust,ignore} ```{rust,ignore}
# fn some_expensive_computation() -> int { 42 } # fn some_expensive_computation() -> int { 42 }
@ -160,7 +160,7 @@ Instead we can clone the `tx`, which allows for multiple senders.
let (tx, rx) = channel(); let (tx, rx) = channel();
for init_val in range(0u, 3) { for init_val in range(0u, 3) {
// Create a new channel handle to distribute to the child task // Create a new channel handle to distribute to the child thread
let child_tx = tx.clone(); let child_tx = tx.clone();
spawn(move || { spawn(move || {
child_tx.send(some_expensive_computation(init_val)); child_tx.send(some_expensive_computation(init_val));
@ -172,7 +172,7 @@ let result = rx.recv() + rx.recv() + rx.recv();
``` ```
Cloning a `Sender` produces a new handle to the same channel, allowing multiple Cloning a `Sender` produces a new handle to the same channel, allowing multiple
tasks to send data to a single receiver. It upgrades the channel internally in threads to send data to a single receiver. It upgrades the channel internally in
order to allow this functionality, which means that channels that are not order to allow this functionality, which means that channels that are not
cloned can avoid the overhead required to handle multiple senders. But this cloned can avoid the overhead required to handle multiple senders. But this
fact has no bearing on the channel's usage: the upgrade is transparent. fact has no bearing on the channel's usage: the upgrade is transparent.
@ -182,9 +182,9 @@ 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. reference, written with multiple streams, it might look like the example below.
```{rust,ignore} ```{rust,ignore}
# use std::task::spawn; # use std::thread::spawn;
// Create a vector of ports, one for each child task // Create a vector of ports, one for each child thread
let rxs = Vec::from_fn(3, |init_val| { let rxs = Vec::from_fn(3, |init_val| {
let (tx, rx) = channel(); let (tx, rx) = channel();
spawn(move || { spawn(move || {
@ -256,18 +256,18 @@ fn main() {
## Sharing without copying: Arc ## Sharing without copying: Arc
To share data between tasks, a first approach would be to only use channel as To share data between threads, a first approach would be to only use channel as
we have seen previously. A copy of the data to share would then be made for we have seen previously. A copy of the data to share would then be made for
each task. In some cases, this would add up to a significant amount of wasted each thread. In some cases, this would add up to a significant amount of wasted
memory and would require copying the same data more than necessary. memory and would require copying the same data more than necessary.
To tackle this issue, one can use an Atomically Reference Counted wrapper To tackle this issue, one can use an Atomically Reference Counted wrapper
(`Arc`) as implemented in the `sync` library of Rust. With an Arc, the data (`Arc`) as implemented in the `sync` library of Rust. With an Arc, the data
will no longer be copied for each task. The Arc acts as a reference to the will no longer be copied for each thread. The Arc acts as a reference to the
shared data and only this reference is shared and cloned. shared data and only this reference is shared and cloned.
Here is a small example showing how to use Arcs. We wish to run concurrently 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 several computations on a single large vector of floats. Each thread needs the
full vector to perform its duty. full vector to perform its duty.
```{rust,ignore} ```{rust,ignore}
@ -284,10 +284,10 @@ fn main() {
let numbers_arc = Arc::new(numbers); let numbers_arc = Arc::new(numbers);
for num in range(1u, 10) { for num in range(1u, 10) {
let task_numbers = numbers_arc.clone(); let thread_numbers = numbers_arc.clone();
spawn(move || { spawn(move || {
println!("{}-norm = {}", num, pnorm(task_numbers.as_slice(), num)); println!("{}-norm = {}", num, pnorm(thread_numbers.as_slice(), num));
}); });
} }
} }
@ -306,8 +306,8 @@ let numbers_arc = Arc::new(numbers);
# } # }
``` ```
and a clone is captured for each task via a procedure. This only copies and a clone is captured for each thread via a procedure. This only copies
the wrapper and not its contents. Within the task's procedure, the captured the wrapper and not its contents. Within the thread's procedure, the captured
Arc reference can be used as a shared reference to the underlying vector as Arc reference can be used as a shared reference to the underlying vector as
if it were local. if it were local.
@ -319,29 +319,29 @@ if it were local.
# let numbers=Vec::from_fn(1000000, |_| rand::random::<f64>()); # let numbers=Vec::from_fn(1000000, |_| rand::random::<f64>());
# let numbers_arc = Arc::new(numbers); # let numbers_arc = Arc::new(numbers);
# let num = 4; # let num = 4;
let task_numbers = numbers_arc.clone(); let thread_numbers = numbers_arc.clone();
spawn(move || { spawn(move || {
// Capture task_numbers and use it as if it was the underlying vector // Capture thread_numbers and use it as if it was the underlying vector
println!("{}-norm = {}", num, pnorm(task_numbers.as_slice(), num)); println!("{}-norm = {}", num, pnorm(thread_numbers.as_slice(), num));
}); });
# } # }
``` ```
# Handling task panics # Handling thread panics
Rust has a built-in mechanism for raising exceptions. The `panic!()` macro Rust has a built-in mechanism for raising exceptions. The `panic!()` macro
(which can also be written with an error string as an argument: `panic!( (which can also be written with an error string as an argument: `panic!(
~reason)`) and the `assert!` construct (which effectively calls `panic!()` if a ~reason)`) and the `assert!` construct (which effectively calls `panic!()` if a
boolean expression is false) are both ways to raise exceptions. When a task boolean expression is false) are both ways to raise exceptions. When a thread
raises an exception, the task unwinds its stack—running destructors and raises an exception, the thread unwinds its stack—running destructors and
freeing memory along the way—and then exits. Unlike exceptions in C++, freeing memory along the way—and then exits. Unlike exceptions in C++,
exceptions in Rust are unrecoverable within a single task: once a task panics, exceptions in Rust are unrecoverable within a single thread: once a thread panics,
there is no way to "catch" the exception. there is no way to "catch" the exception.
While it isn't possible for a task to recover from panicking, tasks may notify While it isn't possible for a thread to recover from panicking, threads may notify
each other if they panic. The simplest way of handling a panic is with the each other if they panic. The simplest way of handling a panic is with the
`try` function, which is similar to `spawn`, but immediately blocks and waits `try` function, which is similar to `spawn`, but immediately blocks and waits
for the child task to finish. `try` returns a value of type for the child thread to finish. `try` returns a value of type
`Result<T, Box<Any + Send>>`. `Result` is an `enum` type with two variants: `Result<T, Box<Any + Send>>`. `Result` is an `enum` type with two variants:
`Ok` and `Err`. In this case, because the type arguments to `Result` are `int` `Ok` and `Err`. In this case, because the type arguments to `Result` are `int`
and `()`, callers can pattern-match on a result to check whether it's an `Ok` and `()`, callers can pattern-match on a result to check whether it's an `Ok`
@ -364,14 +364,14 @@ assert!(result.is_err());
Unlike `spawn`, the function spawned using `try` may return a value, which Unlike `spawn`, the function spawned using `try` may return a value, which
`try` will dutifully propagate back to the caller in a [`Result`] enum. If the `try` will dutifully propagate back to the caller in a [`Result`] enum. If the
child task terminates successfully, `try` will return an `Ok` result; if the child thread terminates successfully, `try` will return an `Ok` result; if the
child task panics, `try` will return an `Error` result. child thread panics, `try` will return an `Error` result.
[`Result`]: std/result/index.html [`Result`]: std/result/index.html
> *Note:* A panicked task does not currently produce a useful error > *Note:* A panicked thread does not currently produce a useful error
> value (`try` always returns `Err(())`). In the > value (`try` always returns `Err(())`). In the
> future, it may be possible for tasks to intercept the value passed to > future, it may be possible for threads to intercept the value passed to
> `panic!()`. > `panic!()`.
But not all panics are created equal. In some cases you might need to abort But not all panics are created equal. In some cases you might need to abort
@ -379,4 +379,4 @@ the entire program (perhaps you're writing an assert which, if it trips,
indicates an unrecoverable logic error); in other cases you might want to indicates an unrecoverable logic error); in other cases you might want to
contain the panic at a certain boundary (perhaps a small piece of input from contain the panic at a certain boundary (perhaps a small piece of input from
the outside world, which you happen to be processing in parallel, is malformed the outside world, which you happen to be processing in parallel, is malformed
such that the processing task cannot proceed). such that the processing thread cannot proceed).

View File

@ -3036,7 +3036,7 @@ test foo ... FAILED
failures: failures:
---- foo stdout ---- ---- foo stdout ----
task 'foo' failed at 'assertion failed: false', /home/you/projects/testing/tests/lib.rs:3 thread 'foo' failed at 'assertion failed: false', /home/you/projects/testing/tests/lib.rs:3
@ -3045,7 +3045,7 @@ failures:
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
task '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:243 thread '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:243
``` ```
Lots of output! Let's break this down: Lots of output! Let's break this down:
@ -3088,7 +3088,7 @@ failed, especially as we accumulate more tests.
failures: failures:
---- foo stdout ---- ---- foo stdout ----
task 'foo' failed at 'assertion failed: false', /home/you/projects/testing/tests/lib.rs:3 thread 'foo' failed at 'assertion failed: false', /home/you/projects/testing/tests/lib.rs:3
@ -3097,7 +3097,7 @@ failures:
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
task '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:243 thread '<main>' failed at 'Some tests failed', /home/you/src/rust/src/libtest/lib.rs:243
``` ```
After all the tests run, Rust will show us any output from our failed tests. After all the tests run, Rust will show us any output from our failed tests.
@ -4263,7 +4263,7 @@ is that a moving closure always takes ownership of all variables that
it uses. Ordinary closures, in contrast, just create a reference into it uses. Ordinary closures, in contrast, just create a reference into
the enclosing stack frame. Moving closures are most useful with Rust's the enclosing stack frame. Moving closures are most useful with Rust's
concurrency features, and so we'll just leave it at this for concurrency features, and so we'll just leave it at this for
now. We'll talk about them more in the "Tasks" section of the guide. now. We'll talk about them more in the "Threads" section of the guide.
## Accepting closures as arguments ## Accepting closures as arguments
@ -5213,9 +5213,7 @@ as you can see, there's no overhead of deciding which version to call here,
hence 'statically dispatched'. The downside is that we have two copies of hence 'statically dispatched'. The downside is that we have two copies of
the same function, so our binary is a little bit larger. the same function, so our binary is a little bit larger.
# Tasks # Threads
**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 Concurrency and parallelism are topics that are of increasing interest to a
broad subsection of software developers. Modern computers are often multi-core, broad subsection of software developers. Modern computers are often multi-core,
@ -5224,24 +5222,22 @@ processor. Rust's semantics lend themselves very nicely to solving a number of
issues that programmers have with concurrency. Many concurrency errors that are issues that programmers have with concurrency. Many concurrency errors that are
runtime errors in other languages are compile-time errors in Rust. runtime errors in other languages are compile-time errors in Rust.
Rust's concurrency primitive is called a **task**. Tasks are similar to Rust's concurrency primitive is called a **thread**. It's worth noting that
threads, and do not share memory in an unsafe manner, preferring message threads are implemented as a library, and not part of the language. This means
passing to communicate. It's worth noting that tasks are implemented as a that in the future, other concurrency libraries can be written for Rust to help
library, and not part of the language. This means that in the future, other in specific scenarios. Here's an example of creating a thread:
concurrency libraries can be written for Rust to help in specific scenarios.
Here's an example of creating a task:
```{rust,ignore} ```{rust,ignore}
spawn(move || { spawn(move || {
println!("Hello from a task!"); println!("Hello from a thread!");
}); });
``` ```
The `spawn` function takes a closure as an argument, and runs that The `spawn` function takes a closure as an argument, and runs that
closure in a new task. Typically, you will want to use a moving closure in a new thread. Typically, you will want to use a moving
closure, so that the closure takes ownership of any variables that it closure, so that the closure takes ownership of any variables that it
touches. This implies that those variables are not usable from the touches. This implies that those variables are not usable from the
parent task after the child task is spawned: parent thread after the child thread is spawned:
```{rust,ignore} ```{rust,ignore}
let mut x = vec![1i, 2i, 3i]; let mut x = vec![1i, 2i, 3i];
@ -5257,15 +5253,15 @@ println!("The value of x[0] is: {}", x[0]); // error: use of moved value: `x`
other languages would let us do this, but it's not safe to do other languages would let us do this, but it's not safe to do
so. Rust's borrow checker catches the error. so. Rust's borrow checker catches the error.
If tasks were only able to capture these values, they wouldn't be very useful. If threads were only able to capture these values, they wouldn't be very useful.
Luckily, tasks can communicate with each other through **channel**s. Channels Luckily, threads can communicate with each other through **channel**s. Channels
work like this: work like this:
```{rust,ignore} ```{rust,ignore}
let (tx, rx) = channel(); let (tx, rx) = channel();
spawn(move || { spawn(move || {
tx.send("Hello from a task!".to_string()); tx.send("Hello from a thread!".to_string());
}); });
let message = rx.recv(); let message = rx.recv();
@ -5278,14 +5274,14 @@ receive the message on the `Receiver<T>` side with the `recv()` method. This
method blocks until it gets a message. There's a similar method, `.try_recv()`, method blocks until it gets a message. There's a similar method, `.try_recv()`,
which returns an `Result<T, TryRecvError>` and does not block. which returns an `Result<T, TryRecvError>` and does not block.
If you want to send messages to the task as well, create two channels! If you want to send messages to the thread as well, create two channels!
```{rust,ignore} ```{rust,ignore}
let (tx1, rx1) = channel(); let (tx1, rx1) = channel();
let (tx2, rx2) = channel(); let (tx2, rx2) = channel();
spawn(move || { spawn(move || {
tx1.send("Hello from a task!".to_string()); tx1.send("Hello from a thread!".to_string());
let message = rx2.recv(); let message = rx2.recv();
println!("{}", message); println!("{}", message);
}); });
@ -5296,9 +5292,9 @@ println!("{}", message);
tx2.send("Goodbye from main!".to_string()); tx2.send("Goodbye from main!".to_string());
``` ```
The closure has one sending end and one receiving end, and the main The closure has one sending end and one receiving end, and the main thread has
task has one of each as well. Now they can talk back and forth in one of each as well. Now they can talk back and forth in whatever way they
whatever way they wish. wish.
Notice as well that because `Sender` and `Receiver` are generic, while you can Notice as well that because `Sender` and `Receiver` are generic, while you can
pass any kind of information through the channel, the ends are strongly typed. pass any kind of information through the channel, the ends are strongly typed.
@ -5337,7 +5333,7 @@ we'll just get the value immediately.
## Success and failure ## Success and failure
Tasks don't always succeed, they can also panic. A task that wishes to panic Threads don't always succeed, they can also panic. A thread that wishes to panic
can call the `panic!` macro, passing a message: can call the `panic!` macro, passing a message:
```{rust,ignore} ```{rust,ignore}
@ -5346,14 +5342,14 @@ spawn(move || {
}); });
``` ```
If a task panics, it is not possible for it to recover. However, it can If a thread 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`: notify other thread that it has panicked. We can do this with `thread::try`:
```{rust,ignore} ```{rust,ignore}
use std::task; use std::thread;
use std::rand; use std::rand;
let result = task::try(move || { let result = thread::try(move || {
if rand::random() { if rand::random() {
println!("OK"); println!("OK");
} else { } else {
@ -5362,7 +5358,7 @@ let result = task::try(move || {
}); });
``` ```
This task will randomly panic or succeed. `task::try` returns a `Result` This thread will randomly panic or succeed. `thread::try` returns a `Result`
type, so we can handle the response like any other computation that may type, so we can handle the response like any other computation that may
fail. fail.

View File

@ -58,7 +58,7 @@ a guide that can help you out:
* [Strings](guide-strings.html) * [Strings](guide-strings.html)
* [Pointers](guide-pointers.html) * [Pointers](guide-pointers.html)
* [Crates and modules](guide-crates.html) * [Crates and modules](guide-crates.html)
* [Tasks and Communication](guide-tasks.html) * [Threads and Communication](guide-threads.html)
* [Error Handling](guide-error-handling.html) * [Error Handling](guide-error-handling.html)
* [Foreign Function Interface](guide-ffi.html) * [Foreign Function Interface](guide-ffi.html)
* [Writing Unsafe and Low-Level Code](guide-unsafe.html) * [Writing Unsafe and Low-Level Code](guide-unsafe.html)

View File

@ -899,8 +899,8 @@ mirrors the module hierarchy.
// Load the `vec` module from `vec.rs` // Load the `vec` module from `vec.rs`
mod vec; mod vec;
mod task { mod thread {
// Load the `local_data` module from `task/local_data.rs` // Load the `local_data` module from `thread/local_data.rs`
mod local_data; mod local_data;
} }
``` ```
@ -909,9 +909,9 @@ The directories and files used for loading external file modules can be
influenced with the `path` attribute. influenced with the `path` attribute.
```{.ignore} ```{.ignore}
#[path = "task_files"] #[path = "thread_files"]
mod task { mod thread {
// Load the `local_data` module from `task_files/tls.rs` // Load the `local_data` module from `thread_files/tls.rs`
#[path = "tls.rs"] #[path = "tls.rs"]
mod local_data; mod local_data;
} }
@ -1188,7 +1188,7 @@ code safe, in the surrounding context.
Unsafe blocks are used to wrap foreign libraries, make direct use of hardware Unsafe blocks are used to wrap foreign libraries, make direct use of hardware
or implement features not directly present in the language. For example, Rust or implement features not directly present in the language. For example, Rust
provides the language features necessary to implement memory-safe concurrency provides the language features necessary to implement memory-safe concurrency
in the language but the implementation of tasks and message passing is in the in the language but the implementation of threads and message passing is in the
standard library. standard library.
Rust's type system is a conservative approximation of the dynamic safety Rust's type system is a conservative approximation of the dynamic safety
@ -1500,7 +1500,7 @@ be modified by the program. One of Rust's goals is to make concurrency bugs
hard to run into, and this is obviously a very large source of race conditions hard to run into, and this is obviously a very large source of race conditions
or other bugs. For this reason, an `unsafe` block is required when either or other bugs. For this reason, an `unsafe` block is required when either
reading or writing a mutable static variable. Care should be taken to ensure reading or writing a mutable static variable. Care should be taken to ensure
that modifications to a mutable static are safe with respect to other tasks that modifications to a mutable static are safe with respect to other threads
running in the same process. running in the same process.
Mutable statics are still very useful, however. They can be used with C Mutable statics are still very useful, however. They can be used with C
@ -2253,11 +2253,11 @@ A complete list of the built-in language items follows:
* `drop` * `drop`
: Have destructors. : Have destructors.
* `send` * `send`
: Able to be sent across task boundaries. : Able to be sent across thread boundaries.
* `sized` * `sized`
: Has a size known at compile time. : Has a size known at compile time.
* `sync` * `sync`
: Able to be safely shared between tasks when aliased. : Able to be safely shared between threads when aliased.
#### Operators #### Operators
@ -2621,7 +2621,7 @@ The currently implemented features of the reference compiler are:
LLVM's implementation which works in concert with the kernel LLVM's implementation which works in concert with the kernel
loader and dynamic linker. This is not necessarily available loader and dynamic linker. This is not necessarily available
on all platforms, and usage of it is discouraged (rust on all platforms, and usage of it is discouraged (rust
focuses more on task-local data instead of thread-local focuses more on thread-local data instead of thread-local
data). data).
* `trace_macros` - Allows use of the `trace_macros` macro, which is a nasty * `trace_macros` - Allows use of the `trace_macros` macro, which is a nasty
@ -2939,7 +2939,7 @@ array is mutable, the resulting [lvalue](#lvalues,-rvalues-and-temporaries) can
be assigned to. be assigned to.
Indices are zero-based, and may be of any integral type. Vector access is Indices are zero-based, and may be of any integral type. Vector access is
bounds-checked at run-time. When the check fails, it will put the task in a bounds-checked at run-time. When the check fails, it will put the thread in a
_panicked state_. _panicked state_.
```{should-fail} ```{should-fail}
@ -3950,7 +3950,7 @@ Types in Rust are categorized into kinds, based on various properties of the
components of the type. The kinds are: components of the type. The kinds are:
* `Send` * `Send`
: Types of this kind can be safely sent between tasks. : Types of this kind can be safely sent between threads.
This kind includes scalars, boxes, procs, and This kind includes scalars, boxes, procs, and
structural types containing only other owned types. structural types containing only other owned types.
All `Send` types are `'static`. All `Send` types are `'static`.
@ -3998,21 +3998,21 @@ to sendable.
# Memory and concurrency models # Memory and concurrency models
Rust has a memory model centered around concurrently-executing _tasks_. Thus Rust has a memory model centered around concurrently-executing _threads_. Thus
its memory model and its concurrency model are best discussed simultaneously, its memory model and its concurrency model are best discussed simultaneously,
as parts of each only make sense when considered from the perspective of the as parts of each only make sense when considered from the perspective of the
other. other.
When reading about the memory model, keep in mind that it is partitioned in When reading about the memory model, keep in mind that it is partitioned in
order to support tasks; and when reading about tasks, keep in mind that their order to support threads; and when reading about threads, keep in mind that their
isolation and communication mechanisms are only possible due to the ownership isolation and communication mechanisms are only possible due to the ownership
and lifetime semantics of the memory model. and lifetime semantics of the memory model.
## Memory model ## Memory model
A Rust program's memory consists of a static set of *items*, a set of A Rust program's memory consists of a static set of *items*, a set of
[tasks](#tasks) each with its own *stack*, and a *heap*. Immutable portions of [threads](#threads) each with its own *stack*, and a *heap*. Immutable portions of
the heap may be shared between tasks, mutable portions may not. the heap may be shared between threads, mutable portions may not.
Allocations in the stack consist of *slots*, and allocations in the heap Allocations in the stack consist of *slots*, and allocations in the heap
consist of *boxes*. consist of *boxes*.
@ -4023,8 +4023,8 @@ The _items_ of a program are those functions, modules and types that have their
value calculated at compile-time and stored uniquely in the memory image of the value calculated at compile-time and stored uniquely in the memory image of the
rust process. Items are neither dynamically allocated nor freed. rust process. Items are neither dynamically allocated nor freed.
A task's _stack_ consists of activation frames automatically allocated on entry A thread's _stack_ consists of activation frames automatically allocated on entry
to each function as the task executes. A stack allocation is reclaimed when to each function as the thread executes. A stack allocation is reclaimed when
control leaves the frame containing it. control leaves the frame containing it.
The _heap_ is a general term that describes boxes. The lifetime of an The _heap_ is a general term that describes boxes. The lifetime of an
@ -4034,10 +4034,10 @@ in the heap, heap allocations may outlive the frame they are allocated within.
### Memory ownership ### Memory ownership
A task owns all memory it can *safely* reach through local variables, as well A thread owns all memory it can *safely* reach through local variables, as well
as boxes and references. as boxes and references.
When a task sends a value that has the `Send` trait to another task, it loses When a thread sends a value that has the `Send` trait to another thread, it loses
ownership of the value sent and can no longer refer to it. This is statically ownership of the value sent and can no longer refer to it. This is statically
guaranteed by the combined use of "move semantics", and the compiler-checked guaranteed by the combined use of "move semantics", and the compiler-checked
_meaning_ of the `Send` trait: it is only instantiated for (transitively) _meaning_ of the `Send` trait: it is only instantiated for (transitively)
@ -4046,12 +4046,12 @@ sendable kinds of data constructor and pointers, never including references.
When a stack frame is exited, its local allocations are all released, and its When a stack frame is exited, its local allocations are all released, and its
references to boxes are dropped. references to boxes are dropped.
When a task finishes, its stack is necessarily empty and it therefore has no When a thread finishes, its stack is necessarily empty and it therefore has no
references to any boxes; the remainder of its heap is immediately freed. references to any boxes; the remainder of its heap is immediately freed.
### Memory slots ### Memory slots
A task's stack contains slots. A thread's stack contains slots.
A _slot_ is a component of a stack frame, either a function parameter, a A _slot_ is a component of a stack frame, either a function parameter, a
[temporary](#lvalues,-rvalues-and-temporaries), or a local variable. [temporary](#lvalues,-rvalues-and-temporaries), or a local variable.
@ -4105,72 +4105,69 @@ let y = x;
// attempting to use `x` will result in an error here // attempting to use `x` will result in an error here
``` ```
## Tasks ## Threads
An executing Rust program consists of a tree of tasks. A Rust _task_ consists Rust's primary concurrency mechanism is called a **thread**.
of an entry function, a stack, a set of outgoing communication channels and
incoming communication ports, and ownership of some portion of the heap of a
single operating-system process.
### Communication between tasks ### Communication between threads
Rust tasks are isolated and generally unable to interfere with one another's Rust threads are isolated and generally unable to interfere with one another's
memory directly, except through [`unsafe` code](#unsafe-functions). All memory directly, except through [`unsafe` code](#unsafe-functions). All
contact between tasks is mediated by safe forms of ownership transfer, and data contact between threads is mediated by safe forms of ownership transfer, and data
races on memory are prohibited by the type system. races on memory are prohibited by the type system.
When you wish to send data between tasks, the values are restricted to the When you wish to send data between threads, the values are restricted to the
[`Send` type-kind](#type-kinds). Restricting communication interfaces to this [`Send` type-kind](#type-kinds). Restricting communication interfaces to this
kind ensures that no references move between tasks. Thus access to an entire kind ensures that no references move between threads. Thus access to an entire
data structure can be mediated through its owning "root" value; no further data structure can be mediated through its owning "root" value; no further
locking or copying is required to avoid data races within the substructure of locking or copying is required to avoid data races within the substructure of
such a value. such a value.
### Task lifecycle ### Thread
The _lifecycle_ of a task consists of a finite set of states and events that The _lifecycle_ of a threads consists of a finite set of states and events that
cause transitions between the states. The lifecycle states of a task are: cause transitions between the states. The lifecycle states of a thread are:
* running * running
* blocked * blocked
* panicked * panicked
* dead * dead
A task begins its lifecycle &mdash; once it has been spawned &mdash; in the A thread begins its lifecycle &mdash; once it has been spawned &mdash; in the
*running* state. In this state it executes the statements of its entry *running* state. In this state it executes the statements of its entry
function, and any functions called by the entry function. function, and any functions called by the entry function.
A task may transition from the *running* state to the *blocked* state any time A thread may transition from the *running* state to the *blocked* state any time
it makes a blocking communication call. When the call can be completed &mdash; it makes a blocking communication call. When the call can be completed &mdash;
when a message arrives at a sender, or a buffer opens to receive a message when a message arrives at a sender, or a buffer opens to receive a message
&mdash; then the blocked task will unblock and transition back to *running*. &mdash; then the blocked thread will unblock and transition back to *running*.
A task may transition to the *panicked* state at any time, due being killed by A thread may transition to the *panicked* state at any time, due being killed by
some external event or internally, from the evaluation of a `panic!()` macro. some external event or internally, from the evaluation of a `panic!()` macro.
Once *panicking*, a task unwinds its stack and transitions to the *dead* state. Once *panicking*, a thread unwinds its stack and transitions to the *dead* state.
Unwinding the stack of a task is done by the task itself, on its own control Unwinding the stack of a thread is done by the thread itself, on its own control
stack. If a value with a destructor is freed during unwinding, the code for the stack. If a value with a destructor is freed during unwinding, the code for the
destructor is run, also on the task's control stack. Running the destructor destructor is run, also on the thread's control stack. Running the destructor
code causes a temporary transition to a *running* state, and allows the code causes a temporary transition to a *running* state, and allows the
destructor code to cause any subsequent state transitions. The original task destructor code to cause any subsequent state transitions. The original thread
of unwinding and panicking thereby may suspend temporarily, and may involve of unwinding and panicking thereby may suspend temporarily, and may involve
(recursive) unwinding of the stack of a failed destructor. Nonetheless, the (recursive) unwinding of the stack of a failed destructor. Nonetheless, the
outermost unwinding activity will continue until the stack is unwound and the outermost unwinding activity will continue until the stack is unwound and the
task transitions to the *dead* state. There is no way to "recover" from task thread transitions to the *dead* state. There is no way to "recover" from thread
panics. Once a task has temporarily suspended its unwinding in the *panicking* panics. Once a thread has temporarily suspended its unwinding in the *panicking*
state, a panic occurring from within this destructor results in *hard* panic. state, a panic occurring from within this destructor results in *hard* panic.
A hard panic currently results in the process aborting. A hard panic currently results in the process aborting.
A task in the *dead* state cannot transition to other states; it exists only to A thread in the *dead* state cannot transition to other states; it exists only to
have its termination status inspected by other tasks, and/or to await have its termination status inspected by other threads, and/or to await
reclamation when the last reference to it drops. reclamation when the last reference to it drops.
# Runtime services, linkage and debugging # Runtime services, linkage and debugging
The Rust _runtime_ is a relatively compact collection of Rust code that The Rust _runtime_ is a relatively compact collection of Rust code that
provides fundamental services and datatypes to all Rust tasks at run-time. It provides fundamental services and datatypes to all Rust threads at run-time. It
is smaller and simpler than many modern language runtimes. It is tightly is smaller and simpler than many modern language runtimes. It is tightly
integrated into the language's execution model of memory, tasks, communication integrated into the language's execution model of memory, threads, communication
and logging. and logging.
### Memory allocation ### Memory allocation
@ -4181,7 +4178,7 @@ environment and releases them back to its environment when they are no longer
needed. The default implementation of the service-provider interface consists needed. The default implementation of the service-provider interface consists
of the C runtime functions `malloc` and `free`. of the C runtime functions `malloc` and `free`.
The runtime memory-management system, in turn, supplies Rust tasks with The runtime memory-management system, in turn, supplies Rust threads with
facilities for allocating releasing stacks, as well as allocating and freeing facilities for allocating releasing stacks, as well as allocating and freeing
heap data. heap data.
@ -4189,15 +4186,15 @@ heap data.
The runtime provides C and Rust code to assist with various built-in types, The runtime provides C and Rust code to assist with various built-in types,
such as arrays, strings, and the low level communication system (ports, such as arrays, strings, and the low level communication system (ports,
channels, tasks). channels, threads).
Support for other built-in types such as simple types, tuples and enums is Support for other built-in types such as simple types, tuples and enums is
open-coded by the Rust compiler. open-coded by the Rust compiler.
### Task scheduling and communication ### Thread scheduling and communication
The runtime provides code to manage inter-task communication. This includes The runtime provides code to manage inter-thread communication. This includes
the system of task-lifecycle state transitions depending on the contents of the system of thread-lifecycle state transitions depending on the contents of
queues, as well as code to copy values between queues and their recipients and queues, as well as code to copy values between queues and their recipients and
to serialize values for transmission over operating-system inter-process to serialize values for transmission over operating-system inter-process
communication facilities. communication facilities.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
//! Task-local reference-counted boxes (the `Rc<T>` type). //! Thread-local reference-counted boxes (the `Rc<T>` type).
//! //!
//! The `Rc<T>` type provides shared ownership of an immutable value. Destruction is deterministic, //! The `Rc<T>` type provides shared ownership of an immutable value. Destruction is deterministic,
//! and will occur as soon as the last owner is gone. It is marked as non-sendable because it //! and will occur as soon as the last owner is gone. It is marked as non-sendable because it

View File

@ -540,7 +540,7 @@ pub fn monitor<F:FnOnce()+Send>(f: F) {
match cfg.spawn(move || { std::io::stdio::set_stderr(box w); f() }).join() { match cfg.spawn(move || { std::io::stdio::set_stderr(box w); f() }).join() {
Ok(()) => { /* fallthrough */ } Ok(()) => { /* fallthrough */ }
Err(value) => { Err(value) => {
// Task panicked without emitting a fatal diagnostic // Thread panicked without emitting a fatal diagnostic
if !value.is::<diagnostic::FatalError>() { if !value.is::<diagnostic::FatalError>() {
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);

View File

@ -18,7 +18,7 @@
//! See the `distributions` submodule for sampling random numbers from //! See the `distributions` submodule for sampling random numbers from
//! distributions like normal and exponential. //! distributions like normal and exponential.
//! //!
//! # Task-local RNG //! # Thread-local RNG
//! //!
//! There is built-in support for a RNG associated with each task stored //! There is built-in support for a RNG associated with each task stored
//! in task-local storage. This RNG can be accessed via `task_rng`, or //! in task-local storage. This RNG can be accessed via `task_rng`, or

View File

@ -53,7 +53,7 @@ pub mod args;
mod at_exit_imp; mod at_exit_imp;
mod libunwind; mod libunwind;
/// The default error code of the rust runtime if the main task panics instead /// The default error code of the rust runtime if the main thread panics instead
/// of exiting cleanly. /// of exiting cleanly.
pub const DEFAULT_ERROR_CODE: int = 101; pub const DEFAULT_ERROR_CODE: int = 101;
@ -137,9 +137,9 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int {
/// ///
/// The procedure passed to this function will be executed as part of the /// The procedure passed to this function will be executed as part of the
/// runtime cleanup phase. For normal rust programs, this means that it will run /// runtime cleanup phase. For normal rust programs, this means that it will run
/// after all other tasks have exited. /// after all other threads have exited.
/// ///
/// The procedure is *not* executed with a local `Task` available to it, so /// The procedure is *not* executed with a local `Thread` available to it, so
/// primitives like logging, I/O, channels, spawning, etc, are *not* available. /// primitives like logging, I/O, channels, spawning, etc, are *not* available.
/// This is meant for "bare bones" usage to clean up runtime details, this is /// This is meant for "bare bones" usage to clean up runtime details, this is
/// not meant as a general-purpose "let's clean everything up" function. /// not meant as a general-purpose "let's clean everything up" function.

View File

@ -36,7 +36,7 @@ use sys_common::stack;
use rt::unwind; use rt::unwind;
use rt::unwind::Unwinder; use rt::unwind::Unwinder;
/// State associated with Rust tasks. /// State associated with Rust threads
/// ///
/// This structure is currently undergoing major changes, and is /// This structure is currently undergoing major changes, and is
/// likely to be move/be merged with a `Thread` structure. /// likely to be move/be merged with a `Thread` structure.
@ -50,14 +50,14 @@ pub struct Task {
awoken: bool, // used to prevent spurious wakeups awoken: bool, // used to prevent spurious wakeups
// This field holds the known bounds of the stack in (lo, hi) form. Not all // This field holds the known bounds of the stack in (lo, hi) form. Not all
// native tasks necessarily know their precise bounds, hence this is // native threads necessarily know their precise bounds, hence this is
// optional. // optional.
stack_bounds: (uint, uint), stack_bounds: (uint, uint),
stack_guard: uint stack_guard: uint
} }
// Once a task has entered the `Armed` state it must be destroyed via `drop`, // Once a thread has entered the `Armed` state it must be destroyed via `drop`,
// and no other method. This state is used to track this transition. // and no other method. This state is used to track this transition.
#[deriving(PartialEq)] #[deriving(PartialEq)]
enum TaskState { enum TaskState {
@ -67,31 +67,31 @@ enum TaskState {
} }
pub struct TaskOpts { pub struct TaskOpts {
/// Invoke this procedure with the result of the task when it finishes. /// Invoke this procedure with the result of the thread when it finishes.
pub on_exit: Option<Thunk<Result>>, pub on_exit: Option<Thunk<Result>>,
/// A name for the task-to-be, for identification in panic messages /// A name for the thread-to-be, for identification in panic messages
pub name: Option<SendStr>, pub name: Option<SendStr>,
/// The size of the stack for the spawned task /// The size of the stack for the spawned thread
pub stack_size: Option<uint>, pub stack_size: Option<uint>,
} }
/// Indicates the manner in which a task exited. /// Indicates the manner in which a thread exited.
/// ///
/// A task that completes without panicking is considered to exit successfully. /// A thread that completes without panicking is considered to exit successfully.
/// ///
/// If you wish for this result's delivery to block until all /// If you wish for this result's delivery to block until all
/// children tasks complete, recommend using a result future. /// children threads complete, recommend using a result future.
pub type Result = ::core::result::Result<(), Box<Any + Send>>; pub type Result = ::core::result::Result<(), Box<Any + Send>>;
/// A handle to a blocked task. Usually this means having the Box<Task> /// A handle to a blocked thread. Usually this means having the Box<Task>
/// pointer by ownership, but if the task is killable, a killer can steal it /// pointer by ownership, but if the thread is killable, a killer can steal it
/// at any time. /// at any time.
pub enum BlockedTask { pub enum BlockedTask {
Owned(Box<Task>), Owned(Box<Task>),
Shared(Arc<AtomicUint>), Shared(Arc<AtomicUint>),
} }
/// Per-task state related to task death, killing, panic, etc. /// Per-thread state related to thread death, killing, panic, etc.
pub struct Death { pub struct Death {
pub on_exit: Option<Thunk<Result>>, pub on_exit: Option<Thunk<Result>>,
} }
@ -101,7 +101,7 @@ pub struct BlockedTasks {
} }
impl Task { impl Task {
/// Creates a new uninitialized task. /// Creates a new uninitialized thread.
pub fn new(stack_bounds: Option<(uint, uint)>, stack_guard: Option<uint>) -> Task { pub fn new(stack_bounds: Option<(uint, uint)>, stack_guard: Option<uint>) -> Task {
Task { Task {
unwinder: Unwinder::new(), unwinder: Unwinder::new(),
@ -153,17 +153,17 @@ impl Task {
}) })
} }
/// Consumes ownership of a task, runs some code, and returns the task back. /// Consumes ownership of a thread, runs some code, and returns the thread back.
/// ///
/// This function can be used as an emulated "try/catch" to interoperate /// This function can be used as an emulated "try/catch" to interoperate
/// with the rust runtime at the outermost boundary. It is not possible to /// with the rust runtime at the outermost boundary. It is not possible to
/// use this function in a nested fashion (a try/catch inside of another /// use this function in a nested fashion (a try/catch inside of another
/// try/catch). Invoking this function is quite cheap. /// try/catch). Invoking this function is quite cheap.
/// ///
/// If the closure `f` succeeds, then the returned task can be used again /// If the closure `f` succeeds, then the returned thread can be used again
/// for another invocation of `run`. If the closure `f` panics then `self` /// for another invocation of `run`. If the closure `f` panics then `self`
/// will be internally destroyed along with all of the other associated /// will be internally destroyed along with all of the other associated
/// resources of this task. The `on_exit` callback is invoked with the /// resources of this thread. The `on_exit` callback is invoked with the
/// cause of panic (not returned here). This can be discovered by querying /// cause of panic (not returned here). This can be discovered by querying
/// `is_destroyed()`. /// `is_destroyed()`.
/// ///
@ -172,30 +172,30 @@ impl Task {
/// guaranteed to return if it panicks. Care should be taken to ensure that /// guaranteed to return if it panicks. Care should be taken to ensure that
/// stack references made by `f` are handled appropriately. /// stack references made by `f` are handled appropriately.
/// ///
/// It is invalid to call this function with a task that has been previously /// It is invalid to call this function with a thread that has been previously
/// destroyed via a failed call to `run`. /// destroyed via a failed call to `run`.
pub fn run(mut self: Box<Task>, f: ||) -> Box<Task> { pub fn run(mut self: Box<Task>, f: ||) -> Box<Task> {
assert!(!self.is_destroyed(), "cannot re-use a destroyed task"); assert!(!self.is_destroyed(), "cannot re-use a destroyed thread");
// First, make sure that no one else is in TLS. This does not allow // First, make sure that no one else is in TLS. This does not allow
// recursive invocations of run(). If there's no one else, then // recursive invocations of run(). If there's no one else, then
// relinquish ownership of ourselves back into TLS. // relinquish ownership of ourselves back into TLS.
if Local::exists(None::<Task>) { if Local::exists(None::<Task>) {
panic!("cannot run a task recursively inside another"); panic!("cannot run a thread recursively inside another");
} }
self.state = Armed; self.state = Armed;
Local::put(self); Local::put(self);
// There are two primary reasons that general try/catch is unsafe. The // There are two primary reasons that general try/catch is unsafe. The
// first is that we do not support nested try/catch. The above check for // first is that we do not support nested try/catch. The above check for
// an existing task in TLS is sufficient for this invariant to be // an existing thread in TLS is sufficient for this invariant to be
// upheld. The second is that unwinding while unwinding is not defined. // upheld. The second is that unwinding while unwinding is not defined.
// We take care of that by having an 'unwinding' flag in the task // We take care of that by having an 'unwinding' flag in the thread
// itself. For these reasons, this unsafety should be ok. // itself. For these reasons, this unsafety should be ok.
let result = unsafe { unwind::try(f) }; let result = unsafe { unwind::try(f) };
// After running the closure given return the task back out if it ran // After running the closure given return the thread back out if it ran
// successfully, or clean up the task if it panicked. // successfully, or clean up the thread if it panicked.
let task: Box<Task> = Local::take(); let task: Box<Task> = Local::take();
match result { match result {
Ok(()) => task, Ok(()) => task,
@ -203,13 +203,13 @@ impl Task {
} }
} }
/// Destroy all associated resources of this task. /// Destroy all associated resources of this thread.
/// ///
/// This function will perform any necessary clean up to prepare the task /// This function will perform any necessary clean up to prepare the thread
/// for destruction. It is required that this is called before a `Task` /// for destruction. It is required that this is called before a `Task`
/// falls out of scope. /// falls out of scope.
/// ///
/// The returned task cannot be used for running any more code, but it may /// The returned thread cannot be used for running any more code, but it may
/// be used to extract the runtime as necessary. /// be used to extract the runtime as necessary.
pub fn destroy(self: Box<Task>) -> Box<Task> { pub fn destroy(self: Box<Task>) -> Box<Task> {
if self.is_destroyed() { if self.is_destroyed() {
@ -219,14 +219,14 @@ impl Task {
} }
} }
/// Cleans up a task, processing the result of the task as appropriate. /// Cleans up a thread, processing the result of the thread as appropriate.
/// ///
/// This function consumes ownership of the task, deallocating it once it's /// This function consumes ownership of the thread, deallocating it once it's
/// done being processed. It is assumed that TLD and the local heap have /// done being processed. It is assumed that TLD and the local heap have
/// already been destroyed and/or annihilated. /// already been destroyed and/or annihilated.
fn cleanup(mut self: Box<Task>, result: Result) -> Box<Task> { fn cleanup(mut self: Box<Task>, result: Result) -> Box<Task> {
// After taking care of the data above, we need to transmit the result // After taking care of the data above, we need to transmit the result
// of this task. // of this thread.
let what_to_do = self.death.on_exit.take(); let what_to_do = self.death.on_exit.take();
Local::put(self); Local::put(self);
@ -235,15 +235,15 @@ impl Task {
// if this panics, this will also likely abort the runtime. // if this panics, this will also likely abort the runtime.
// //
// This closure is currently limited to a channel send via the // This closure is currently limited to a channel send via the
// standard library's task interface, but this needs // standard library's thread interface, but this needs
// reconsideration to whether it's a reasonable thing to let a // reconsideration to whether it's a reasonable thing to let a
// task to do or not. // thread to do or not.
match what_to_do { match what_to_do {
Some(f) => { f.invoke(result) } Some(f) => { f.invoke(result) }
None => { drop(result) } None => { drop(result) }
} }
// Now that we're done, we remove the task from TLS and flag it for // Now that we're done, we remove the thread from TLS and flag it for
// destruction. // destruction.
let mut task: Box<Task> = Local::take(); let mut task: Box<Task> = Local::take();
task.state = Destroyed; task.state = Destroyed;
@ -253,7 +253,7 @@ impl Task {
/// Queries whether this can be destroyed or not. /// Queries whether this can be destroyed or not.
pub fn is_destroyed(&self) -> bool { self.state == Destroyed } pub fn is_destroyed(&self) -> bool { self.state == Destroyed }
/// Deschedules the current task, invoking `f` `amt` times. It is not /// Deschedules the current thread, invoking `f` `amt` times. It is not
/// recommended to use this function directly, but rather communication /// recommended to use this function directly, but rather communication
/// primitives in `std::comm` should be used. /// primitives in `std::comm` should be used.
// //
@ -262,31 +262,31 @@ impl Task {
// shared state. Additionally, all of the violations are protected with a // shared state. Additionally, all of the violations are protected with a
// mutex, so in theory there are no races. // mutex, so in theory there are no races.
// //
// The first thing we need to do is to get a pointer to the task's internal // The first thing we need to do is to get a pointer to the thread's internal
// mutex. This address will not be changing (because the task is allocated // mutex. This address will not be changing (because the thread is allocated
// on the heap). We must have this handle separately because the task will // on the heap). We must have this handle separately because the thread will
// have its ownership transferred to the given closure. We're guaranteed, // have its ownership transferred to the given closure. We're guaranteed,
// however, that this memory will remain valid because *this* is the current // however, that this memory will remain valid because *this* is the current
// task's execution thread. // thread's execution thread.
// //
// The next weird part is where ownership of the task actually goes. We // The next weird part is where ownership of the thread actually goes. We
// relinquish it to the `f` blocking function, but upon returning this // relinquish it to the `f` blocking function, but upon returning this
// function needs to replace the task back in TLS. There is no communication // function needs to replace the thread back in TLS. There is no communication
// from the wakeup thread back to this thread about the task pointer, and // from the wakeup thread back to this thread about the thread pointer, and
// there's really no need to. In order to get around this, we cast the task // there's really no need to. In order to get around this, we cast the thread
// to a `uint` which is then used at the end of this function to cast back // to a `uint` which is then used at the end of this function to cast back
// to a `Box<Task>` object. Naturally, this looks like it violates // to a `Box<Task>` object. Naturally, this looks like it violates
// ownership semantics in that there may be two `Box<Task>` objects. // ownership semantics in that there may be two `Box<Task>` objects.
// //
// The fun part is that the wakeup half of this implementation knows to // The fun part is that the wakeup half of this implementation knows to
// "forget" the task on the other end. This means that the awakening half of // "forget" the thread on the other end. This means that the awakening half of
// things silently relinquishes ownership back to this thread, but not in a // things silently relinquishes ownership back to this thread, but not in a
// way that the compiler can understand. The task's memory is always valid // way that the compiler can understand. The thread's memory is always valid
// for both tasks because these operations are all done inside of a mutex. // for both threads because these operations are all done inside of a mutex.
// //
// You'll also find that if blocking fails (the `f` function hands the // You'll also find that if blocking fails (the `f` function hands the
// BlockedTask back to us), we will `mem::forget` the handles. The // BlockedTask back to us), we will `mem::forget` the handles. The
// reasoning for this is the same logic as above in that the task silently // reasoning for this is the same logic as above in that the thread silently
// transfers ownership via the `uint`, not through normal compiler // transfers ownership via the `uint`, not through normal compiler
// semantics. // semantics.
// //
@ -319,11 +319,11 @@ impl Task {
let guard = (*me).lock.lock(); let guard = (*me).lock.lock();
(*me).awoken = false; (*me).awoken = false;
// Apply the given closure to all of the "selectable tasks", // Apply the given closure to all of the "selectable threads",
// bailing on the first one that produces an error. Note that // bailing on the first one that produces an error. Note that
// care must be taken such that when an error is occurred, we // care must be taken such that when an error is occurred, we
// may not own the task, so we may still have to wait for the // may not own the thread, so we may still have to wait for the
// task to become available. In other words, if task.wake() // thread to become available. In other words, if thread.wake()
// returns `None`, then someone else has ownership and we must // returns `None`, then someone else has ownership and we must
// wait for their signal. // wait for their signal.
match iter.map(f).filter_map(|a| a.err()).next() { match iter.map(f).filter_map(|a| a.err()).next() {
@ -342,15 +342,15 @@ impl Task {
guard.wait(); guard.wait();
} }
} }
// put the task back in TLS, and everything is as it once was. // put the thread back in TLS, and everything is as it once was.
Local::put(mem::transmute(me)); Local::put(mem::transmute(me));
} }
} }
/// Wakes up a previously blocked task. This function can only be /// Wakes up a previously blocked thread. This function can only be
/// called on tasks that were previously blocked in `deschedule`. /// called on threads that were previously blocked in `deschedule`.
// //
// See the comments on `deschedule` for why the task is forgotten here, and // See the comments on `deschedule` for why the thread is forgotten here, and
// why it's valid to do so. // why it's valid to do so.
pub fn reawaken(mut self: Box<Task>) { pub fn reawaken(mut self: Box<Task>) {
unsafe { unsafe {
@ -362,21 +362,21 @@ impl Task {
} }
} }
/// Yields control of this task to another task. This function will /// Yields control of this thread to another thread. This function will
/// eventually return, but possibly not immediately. This is used as an /// eventually return, but possibly not immediately. This is used as an
/// opportunity to allow other tasks a chance to run. /// opportunity to allow other threads a chance to run.
pub fn yield_now() { pub fn yield_now() {
Thread::yield_now(); Thread::yield_now();
} }
/// Returns the stack bounds for this task in (lo, hi) format. The stack /// Returns the stack bounds for this thread in (lo, hi) format. The stack
/// bounds may not be known for all tasks, so the return value may be /// bounds may not be known for all threads, so the return value may be
/// `None`. /// `None`.
pub fn stack_bounds(&self) -> (uint, uint) { pub fn stack_bounds(&self) -> (uint, uint) {
self.stack_bounds self.stack_bounds
} }
/// Returns the stack guard for this task, if known. /// Returns the stack guard for this thread, if known.
pub fn stack_guard(&self) -> Option<uint> { pub fn stack_guard(&self) -> Option<uint> {
if self.stack_guard != 0 { if self.stack_guard != 0 {
Some(self.stack_guard) Some(self.stack_guard)
@ -385,9 +385,9 @@ impl Task {
} }
} }
/// Consume this task, flagging it as a candidate for destruction. /// Consume this thread, flagging it as a candidate for destruction.
/// ///
/// This function is required to be invoked to destroy a task. A task /// This function is required to be invoked to destroy a thread. A thread
/// destroyed through a normal drop will abort. /// destroyed through a normal drop will abort.
pub fn drop(mut self) { pub fn drop(mut self) {
self.state = Destroyed; self.state = Destroyed;
@ -396,7 +396,7 @@ impl Task {
impl Drop for Task { impl Drop for Task {
fn drop(&mut self) { fn drop(&mut self) {
rtdebug!("called drop for a task: {}", self as *mut Task as uint); rtdebug!("called drop for a thread: {}", self as *mut Task as uint);
rtassert!(self.state != Armed); rtassert!(self.state != Armed);
} }
} }
@ -414,7 +414,7 @@ impl Iterator<BlockedTask> for BlockedTasks {
} }
impl BlockedTask { impl BlockedTask {
/// Returns Some if the task was successfully woken; None if already killed. /// Returns Some if the thread was successfully woken; None if already killed.
pub fn wake(self) -> Option<Box<Task>> { pub fn wake(self) -> Option<Box<Task>> {
match self { match self {
Owned(task) => Some(task), Owned(task) => Some(task),
@ -427,7 +427,7 @@ impl BlockedTask {
} }
} }
/// Reawakens this task if ownership is acquired. If finer-grained control /// Reawakens this thread if ownership is acquired. If finer-grained control
/// is desired, use `wake` instead. /// is desired, use `wake` instead.
pub fn reawaken(self) { pub fn reawaken(self) {
self.wake().map(|t| t.reawaken()); self.wake().map(|t| t.reawaken());
@ -438,12 +438,12 @@ impl BlockedTask {
#[cfg(not(test))] pub fn trash(self) { } #[cfg(not(test))] pub fn trash(self) { }
#[cfg(test)] pub fn trash(self) { assert!(self.wake().is_none()); } #[cfg(test)] pub fn trash(self) { assert!(self.wake().is_none()); }
/// Create a blocked task, unless the task was already killed. /// Create a blocked thread, unless the thread was already killed.
pub fn block(task: Box<Task>) -> BlockedTask { pub fn block(task: Box<Task>) -> BlockedTask {
Owned(task) Owned(task)
} }
/// Converts one blocked task handle to a list of many handles to the same. /// Converts one blocked thread handle to a list of many handles to the same.
pub fn make_selectable(self, num_handles: uint) -> Take<BlockedTasks> { pub fn make_selectable(self, num_handles: uint) -> Take<BlockedTasks> {
let arc = match self { let arc = match self {
Owned(task) => { Owned(task) => {
@ -543,7 +543,7 @@ mod test {
drop(Task::new(None, None)); drop(Task::new(None, None));
} }
// Task blocking tests // Thread blocking tests
#[test] #[test]
fn block_and_wake() { fn block_and_wake() {

View File

@ -79,7 +79,7 @@ struct Exception {
pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: uint); pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: uint);
// Variables used for invoking callbacks when a task starts to unwind. // Variables used for invoking callbacks when a thread starts to unwind.
// //
// For more information, see below. // For more information, see below.
const MAX_CALLBACKS: uint = 16; const MAX_CALLBACKS: uint = 16;
@ -106,14 +106,14 @@ thread_local! { static PANICKING: Cell<bool> = Cell::new(false) }
/// ///
/// * This is not safe to call in a nested fashion. The unwinding /// * This is not safe to call in a nested fashion. The unwinding
/// interface for Rust is designed to have at most one try/catch block per /// interface for Rust is designed to have at most one try/catch block per
/// task, not multiple. No runtime checking is currently performed to uphold /// thread, not multiple. No runtime checking is currently performed to uphold
/// this invariant, so this function is not safe. A nested try/catch block /// this invariant, so this function is not safe. A nested try/catch block
/// may result in corruption of the outer try/catch block's state, especially /// may result in corruption of the outer try/catch block's state, especially
/// if this is used within a task itself. /// if this is used within a thread itself.
/// ///
/// * It is not sound to trigger unwinding while already unwinding. Rust tasks /// * It is not sound to trigger unwinding while already unwinding. Rust threads
/// have runtime checks in place to ensure this invariant, but it is not /// have runtime checks in place to ensure this invariant, but it is not
/// guaranteed that a rust task is in place when invoking this function. /// guaranteed that a rust thread is in place when invoking this function.
/// Unwinding twice can lead to resource leaks where some destructors are not /// Unwinding twice can lead to resource leaks where some destructors are not
/// run. /// run.
pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> { pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
@ -203,7 +203,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. // _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
// //
// This is pretty close to Rust's exception handling approach, except that Rust // This is pretty close to Rust's exception handling approach, except that Rust
// does have a single "catch-all" handler at the bottom of each task's stack. // does have a single "catch-all" handler at the bottom of each thread's stack.
// So we have two versions of the personality routine: // So we have two versions of the personality routine:
// - rust_eh_personality, used by all cleanup landing pads, which never catches, // - rust_eh_personality, used by all cleanup landing pads, which never catches,
// so the behavior of __gcc_personality_v0 is perfectly adequate there, and // so the behavior of __gcc_personality_v0 is perfectly adequate there, and
@ -523,7 +523,7 @@ pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, uint)) ->
// Currently this means that panic!() on OOM will invoke this code path, // Currently this means that panic!() on OOM will invoke this code path,
// but then again we're not really ready for panic on OOM anyway. If // but then again we're not really ready for panic on OOM anyway. If
// we do start doing this, then we should propagate this allocation to // we do start doing this, then we should propagate this allocation to
// be performed in the parent of this task instead of the task that's // be performed in the parent of this thread instead of the thread that's
// panicking. // panicking.
// see below for why we do the `Any` coercion here. // see below for why we do the `Any` coercion here.
@ -546,7 +546,7 @@ fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) ->
static INIT: Once = ONCE_INIT; static INIT: Once = ONCE_INIT;
INIT.doit(|| unsafe { register(failure::on_fail); }); INIT.doit(|| unsafe { register(failure::on_fail); });
// First, invoke call the user-defined callbacks triggered on task panic. // First, invoke call the user-defined callbacks triggered on thread panic.
// //
// By the time that we see a callback has been registered (by reading // By the time that we see a callback has been registered (by reading
// MAX_CALLBACKS), the actual callback itself may have not been stored yet, // MAX_CALLBACKS), the actual callback itself may have not been stored yet,
@ -574,7 +574,7 @@ fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) ->
// If a thread panics while it's already unwinding then we // If a thread panics while it's already unwinding then we
// have limited options. Currently our preference is to // have limited options. Currently our preference is to
// just abort. In the future we may consider resuming // just abort. In the future we may consider resuming
// unwinding or otherwise exiting the task cleanly. // unwinding or otherwise exiting the thread cleanly.
rterrln!("thread panicked while panicking. aborting."); rterrln!("thread panicked while panicking. aborting.");
unsafe { intrinsics::abort() } unsafe { intrinsics::abort() }
} }
@ -582,10 +582,10 @@ fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) ->
rust_panic(msg); rust_panic(msg);
} }
/// Register a callback to be invoked when a task unwinds. /// Register a callback to be invoked when a thread unwinds.
/// ///
/// This is an unsafe and experimental API which allows for an arbitrary /// This is an unsafe and experimental API which allows for an arbitrary
/// callback to be invoked when a task panics. This callback is invoked on both /// callback to be invoked when a thread panics. This callback is invoked on both
/// the initial unwinding and a double unwinding if one occurs. Additionally, /// the initial unwinding and a double unwinding if one occurs. Additionally,
/// the local `Task` will be in place for the duration of the callback, and /// the local `Task` will be in place for the duration of the callback, and
/// the callback must ensure that it remains in place once the callback returns. /// the callback must ensure that it remains in place once the callback returns.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
//! Abstraction of a task pool for basic parallelism. //! Abstraction of a thread pool for basic parallelism.
use core::prelude::*; use core::prelude::*;
@ -45,9 +45,9 @@ impl<'a> Drop for Sentinel<'a> {
} }
} }
/// A task pool used to execute functions in parallel. /// A thread pool used to execute functions in parallel.
/// ///
/// Spawns `n` worker tasks and replenishes the pool if any worker tasks /// Spawns `n` worker threads and replenishes the pool if any worker threads
/// panic. /// panic.
/// ///
/// # Example /// # Example
@ -69,34 +69,34 @@ impl<'a> Drop for Sentinel<'a> {
/// assert_eq!(rx.iter().take(8u).sum(), 8u); /// assert_eq!(rx.iter().take(8u).sum(), 8u);
/// ``` /// ```
pub struct TaskPool { pub struct TaskPool {
// How the taskpool communicates with subtasks. // How the threadpool communicates with subthreads.
// //
// This is the only such Sender, so when it is dropped all subtasks will // This is the only such Sender, so when it is dropped all subthreads will
// quit. // quit.
jobs: Sender<Thunk> jobs: Sender<Thunk>
} }
impl TaskPool { impl TaskPool {
/// Spawns a new task pool with `tasks` tasks. /// Spawns a new thread pool with `threads` threads.
/// ///
/// # Panics /// # Panics
/// ///
/// This function will panic if `tasks` is 0. /// This function will panic if `threads` is 0.
pub fn new(tasks: uint) -> TaskPool { pub fn new(threads: uint) -> TaskPool {
assert!(tasks >= 1); assert!(threads >= 1);
let (tx, rx) = channel::<Thunk>(); let (tx, rx) = channel::<Thunk>();
let rx = Arc::new(Mutex::new(rx)); let rx = Arc::new(Mutex::new(rx));
// Taskpool tasks. // Threadpool threads
for _ in range(0, tasks) { for _ in range(0, threads) {
spawn_in_pool(rx.clone()); spawn_in_pool(rx.clone());
} }
TaskPool { jobs: tx } TaskPool { jobs: tx }
} }
/// Executes the function `job` on a task in the pool. /// Executes the function `job` on a thread in the pool.
pub fn execute<F>(&self, job: F) pub fn execute<F>(&self, job: F)
where F : FnOnce(), F : Send where F : FnOnce(), F : Send
{ {
@ -106,7 +106,7 @@ impl TaskPool {
fn spawn_in_pool(jobs: Arc<Mutex<Receiver<Thunk>>>) { fn spawn_in_pool(jobs: Arc<Mutex<Receiver<Thunk>>>) {
Thread::spawn(move |:| { Thread::spawn(move |:| {
// Will spawn a new task on panic unless it is cancelled. // Will spawn a new thread on panic unless it is cancelled.
let sentinel = Sentinel::new(&jobs); let sentinel = Sentinel::new(&jobs);
loop { loop {
@ -165,12 +165,12 @@ mod test {
let pool = TaskPool::new(TEST_TASKS); let pool = TaskPool::new(TEST_TASKS);
// Panic all the existing tasks. // Panic all the existing threads.
for _ in range(0, TEST_TASKS) { for _ in range(0, TEST_TASKS) {
pool.execute(move|| -> () { panic!() }); pool.execute(move|| -> () { panic!() });
} }
// Ensure new tasks were spawned to compensate. // Ensure new threads were spawned to compensate.
let (tx, rx) = channel(); let (tx, rx) = channel();
for _ in range(0, TEST_TASKS) { for _ in range(0, TEST_TASKS) {
let tx = tx.clone(); let tx = tx.clone();
@ -189,7 +189,7 @@ mod test {
let pool = TaskPool::new(TEST_TASKS); let pool = TaskPool::new(TEST_TASKS);
let waiter = Arc::new(Barrier::new(TEST_TASKS + 1)); let waiter = Arc::new(Barrier::new(TEST_TASKS + 1));
// Panic all the existing tasks in a bit. // Panic all the existing threads in a bit.
for _ in range(0, TEST_TASKS) { for _ in range(0, TEST_TASKS) {
let waiter = waiter.clone(); let waiter = waiter.clone();
pool.execute(move|| { pool.execute(move|| {