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
@ -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
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
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.
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
particular, the type system guarantees that tasks cannot induce a data race
Threads use Rust's type system to provide strong memory safety guarantees. In
particular, the type system guarantees that threads cannot induce a data race
from shared mutable state.
# Basics
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.
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 thread.
```{rust,ignore}
# use std::task::spawn;
# use std::thread::spawn;
// Print something profound in a different task using a named function
fn print_message() { println!("I am running in a different task!"); }
// Print something profound in a different thread using a named function
fn print_message() { println!("I am running in a different thread!"); }
spawn(print_message);
// Alternatively, use a `move ||` expression instead of a named function.
// `||` expressions evaluate to an unnamed closure. The `move` keyword
// indicates that the closure should take ownership of any variables it
// 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
concurrency: particularly, ownership. The language leaves the implementation
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
(that is, data which is deeply owned). Limiting the closure to `Send`
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}
# use std::task::spawn;
# fn generate_task_number() -> int { 0 }
# use std::thread::spawn;
# fn generate_thread_number() -> int { 0 }
// Generate some state locally
let child_task_number = generate_task_number();
let child_thread_number = generate_thread_number();
spawn(move || {
// Capture it in the remote task. The `move` keyword indicates
// that this closure should move `child_task_number` into its
// Capture it in the remote thread. The `move` keyword indicates
// that this closure should move `child_thread_number` into its
// environment, rather than capturing a reference into the
// enclosing stack frame.
println!("I am child number {}", child_task_number);
println!("I am child number {}", child_thread_number);
});
```
## 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:
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:
```{rust,ignore}
# use std::task::spawn;
# use std::thread::spawn;
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
@ -102,12 +102,12 @@ into its component parts).
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
task.
thread.
```{rust,ignore}
# use std::task::spawn;
# use std::thread::spawn;
# fn some_expensive_computation() -> int { 42 }
# let (tx, rx) = channel();
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
`Receiver` are sendable types and may be captured into tasks or otherwise
transferred between them. In the example, the child task runs an expensive
`Receiver` are sendable types and may be captured into threads or otherwise
transferred between them. In the example, the child thread runs an expensive
computation, then sends the result over the captured channel.
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
senders cannot use a single `Sender` value, and multiple receivers cannot use a
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}
# 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();
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();
spawn(move || {
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
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
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.
@ -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.
```{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 (tx, rx) = channel();
spawn(move || {
@ -256,18 +256,18 @@ fn main() {
## 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
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.
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
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.
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.
```{rust,ignore}
@ -284,10 +284,10 @@ fn main() {
let numbers_arc = Arc::new(numbers);
for num in range(1u, 10) {
let task_numbers = numbers_arc.clone();
let thread_numbers = numbers_arc.clone();
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
the wrapper and not its contents. Within the task's procedure, the captured
and a clone is captured for each thread via a procedure. This only copies
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
if it were local.
@ -319,29 +319,29 @@ if it were local.
# let numbers=Vec::from_fn(1000000, |_| rand::random::<f64>());
# let numbers_arc = Arc::new(numbers);
# let num = 4;
let task_numbers = numbers_arc.clone();
let thread_numbers = numbers_arc.clone();
spawn(move || {
// Capture task_numbers and use it as if it was the underlying vector
println!("{}-norm = {}", num, pnorm(task_numbers.as_slice(), num));
// Capture thread_numbers and use it as if it was the underlying vector
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
(which can also be written with an error string as an argument: `panic!(
~reason)`) and the `assert!` construct (which effectively calls `panic!()` if a
boolean expression is false) are both ways to raise exceptions. When a task
raises an exception, the task unwinds its stack—running destructors and
boolean expression is false) are both ways to raise exceptions. When a thread
raises an exception, the thread unwinds its stack—running destructors and
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.
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
`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:
`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`
@ -364,14 +364,14 @@ assert!(result.is_err());
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
child task terminates successfully, `try` will return an `Ok` result; if the
child task panics, `try` will return an `Error` result.
child thread terminates successfully, `try` will return an `Ok` result; if the
child thread panics, `try` will return an `Error` result.
[`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
> 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!()`.
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
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
such that the processing task cannot proceed).
such that the processing thread cannot proceed).

View File

@ -3036,7 +3036,7 @@ test foo ... FAILED
failures:
---- 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
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:
@ -3088,7 +3088,7 @@ failed, especially as we accumulate more tests.
failures:
---- 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
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.
@ -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
the enclosing stack frame. Moving closures are most useful with Rust's
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
@ -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
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.
# Threads
Concurrency and parallelism are topics that are of increasing interest to a
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
runtime errors in other languages are compile-time errors in Rust.
Rust's concurrency primitive is called a **task**. Tasks are similar to
threads, and do not share memory in an unsafe manner, preferring message
passing to communicate. It's worth noting that tasks are implemented as a
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's concurrency primitive is called a **thread**. It's worth noting that
threads are implemented as a 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 thread:
```{rust,ignore}
spawn(move || {
println!("Hello from a task!");
println!("Hello from a thread!");
});
```
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
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}
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
so. Rust's borrow checker catches the error.
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
If threads were only able to capture these values, they wouldn't be very useful.
Luckily, threads can communicate with each other through **channel**s. Channels
work like this:
```{rust,ignore}
let (tx, rx) = channel();
spawn(move || {
tx.send("Hello from a task!".to_string());
tx.send("Hello from a thread!".to_string());
});
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()`,
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}
let (tx1, rx1) = channel();
let (tx2, rx2) = channel();
spawn(move || {
tx1.send("Hello from a task!".to_string());
tx1.send("Hello from a thread!".to_string());
let message = rx2.recv();
println!("{}", message);
});
@ -5296,9 +5292,9 @@ println!("{}", message);
tx2.send("Goodbye from main!".to_string());
```
The closure has one sending end and one receiving end, and the main
task has one of each as well. Now they can talk back and forth in
whatever way they wish.
The closure has one sending end and one receiving end, and the main thread has
one of each as well. Now they can talk back and forth in whatever way they
wish.
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.
@ -5337,7 +5333,7 @@ we'll just get the value immediately.
## 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:
```{rust,ignore}
@ -5346,14 +5342,14 @@ 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`:
If a thread panics, it is not possible for it to recover. However, it can
notify other thread that it has panicked. We can do this with `thread::try`:
```{rust,ignore}
use std::task;
use std::thread;
use std::rand;
let result = task::try(move || {
let result = thread::try(move || {
if rand::random() {
println!("OK");
} 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
fail.

View File

@ -58,7 +58,7 @@ a guide that can help you out:
* [Strings](guide-strings.html)
* [Pointers](guide-pointers.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)
* [Foreign Function Interface](guide-ffi.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`
mod vec;
mod task {
// Load the `local_data` module from `task/local_data.rs`
mod thread {
// Load the `local_data` module from `thread/local_data.rs`
mod local_data;
}
```
@ -909,9 +909,9 @@ The directories and files used for loading external file modules can be
influenced with the `path` attribute.
```{.ignore}
#[path = "task_files"]
mod task {
// Load the `local_data` module from `task_files/tls.rs`
#[path = "thread_files"]
mod thread {
// Load the `local_data` module from `thread_files/tls.rs`
#[path = "tls.rs"]
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
or implement features not directly present in the language. For example, Rust
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.
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
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
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.
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`
: Have destructors.
* `send`
: Able to be sent across task boundaries.
: Able to be sent across thread boundaries.
* `sized`
: Has a size known at compile time.
* `sync`
: Able to be safely shared between tasks when aliased.
: Able to be safely shared between threads when aliased.
#### Operators
@ -2621,7 +2621,7 @@ The currently implemented features of the reference compiler are:
LLVM's implementation which works in concert with the kernel
loader and dynamic linker. This is not necessarily available
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).
* `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.
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_.
```{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:
* `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
structural types containing only other owned types.
All `Send` types are `'static`.
@ -3998,21 +3998,21 @@ to sendable.
# 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,
as parts of each only make sense when considered from the perspective of the
other.
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
and lifetime semantics of the memory model.
## Memory model
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
the heap may be shared between tasks, mutable portions may not.
[threads](#threads) each with its own *stack*, and a *heap*. Immutable portions of
the heap may be shared between threads, mutable portions may not.
Allocations in the stack consist of *slots*, and allocations in the heap
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
rust process. Items are neither dynamically allocated nor freed.
A task's _stack_ consists of activation frames automatically allocated on entry
to each function as the task executes. A stack allocation is reclaimed when
A thread's _stack_ consists of activation frames automatically allocated on entry
to each function as the thread executes. A stack allocation is reclaimed when
control leaves the frame containing it.
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
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.
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
guaranteed by the combined use of "move semantics", and the compiler-checked
_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
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.
### 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
[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
```
## Tasks
## Threads
An executing Rust program consists of a tree of tasks. A Rust _task_ consists
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.
Rust's primary concurrency mechanism is called a **thread**.
### 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
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.
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
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
locking or copying is required to avoid data races within the substructure of
such a value.
### Task lifecycle
### Thread
The _lifecycle_ of a task consists of a finite set of states and events that
cause transitions between the states. The lifecycle states of a task are:
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 thread are:
* running
* blocked
* panicked
* 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
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;
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.
Once *panicking*, a task 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
Once *panicking*, a thread unwinds its stack and transitions to the *dead* state.
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
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
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
(recursive) unwinding of the stack of a failed destructor. Nonetheless, 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
panics. Once a task has temporarily suspended its unwinding in the *panicking*
thread transitions to the *dead* state. There is no way to "recover" from thread
panics. Once a thread has temporarily suspended its unwinding in the *panicking*
state, a panic occurring from within this destructor results in *hard* panic.
A hard panic currently results in the process aborting.
A task 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
A thread in the *dead* state cannot transition to other states; it exists only to
have its termination status inspected by other threads, and/or to await
reclamation when the last reference to it drops.
# Runtime services, linkage and debugging
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
integrated into the language's execution model of memory, tasks, communication
integrated into the language's execution model of memory, threads, communication
and logging.
### 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
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
heap data.
@ -4189,15 +4186,15 @@ heap data.
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,
channels, tasks).
channels, threads).
Support for other built-in types such as simple types, tuples and enums is
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 system of task-lifecycle state transitions depending on the contents of
The runtime provides code to manage inter-thread communication. This includes
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
to serialize values for transmission over operating-system inter-process
communication facilities.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// 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,
//! 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() {
Ok(()) => { /* fallthrough */ }
Err(value) => {
// Task panicked without emitting a fatal diagnostic
// Thread panicked without emitting a fatal diagnostic
if !value.is::<diagnostic::FatalError>() {
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);

View File

@ -18,7 +18,7 @@
//! See the `distributions` submodule for sampling random numbers from
//! distributions like normal and exponential.
//!
//! # Task-local RNG
//! # Thread-local RNG
//!
//! 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

View File

@ -53,7 +53,7 @@ pub mod args;
mod at_exit_imp;
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.
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
/// 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.
/// 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.

View File

@ -36,7 +36,7 @@ use sys_common::stack;
use rt::unwind;
use rt::unwind::Unwinder;
/// State associated with Rust tasks.
/// State associated with Rust threads
///
/// This structure is currently undergoing major changes, and is
/// likely to be move/be merged with a `Thread` structure.
@ -50,14 +50,14 @@ pub struct Task {
awoken: bool, // used to prevent spurious wakeups
// 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.
stack_bounds: (uint, 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.
#[deriving(PartialEq)]
enum TaskState {
@ -67,31 +67,31 @@ enum TaskState {
}
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>>,
/// 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>,
/// The size of the stack for the spawned task
/// The size of the stack for the spawned thread
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
/// 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>>;
/// A handle to a blocked task. Usually this means having the Box<Task>
/// pointer by ownership, but if the task is killable, a killer can steal it
/// A handle to a blocked thread. Usually this means having the Box<Task>
/// pointer by ownership, but if the thread is killable, a killer can steal it
/// at any time.
pub enum BlockedTask {
Owned(Box<Task>),
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 on_exit: Option<Thunk<Result>>,
}
@ -101,7 +101,7 @@ pub struct BlockedTasks {
}
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 {
Task {
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
/// 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
/// 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`
/// 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
/// `is_destroyed()`.
///
@ -172,30 +172,30 @@ impl Task {
/// guaranteed to return if it panicks. Care should be taken to ensure that
/// 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`.
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
// recursive invocations of run(). If there's no one else, then
// relinquish ownership of ourselves back into TLS.
if Local::exists(None::<Task>) {
panic!("cannot run a task recursively inside another");
panic!("cannot run a thread recursively inside another");
}
self.state = Armed;
Local::put(self);
// 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
// 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.
// 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.
let result = unsafe { unwind::try(f) };
// After running the closure given return the task back out if it ran
// successfully, or clean up the task if it panicked.
// After running the closure given return the thread back out if it ran
// successfully, or clean up the thread if it panicked.
let task: Box<Task> = Local::take();
match result {
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`
/// 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.
pub fn destroy(self: Box<Task>) -> Box<Task> {
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
/// already been destroyed and/or annihilated.
fn cleanup(mut self: Box<Task>, result: Result) -> Box<Task> {
// 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();
Local::put(self);
@ -235,15 +235,15 @@ impl Task {
// if this panics, this will also likely abort the runtime.
//
// 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
// task to do or not.
// thread to do or not.
match what_to_do {
Some(f) => { f.invoke(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.
let mut task: Box<Task> = Local::take();
task.state = Destroyed;
@ -253,7 +253,7 @@ impl Task {
/// Queries whether this can be destroyed or not.
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
/// primitives in `std::comm` should be used.
//
@ -262,31 +262,31 @@ impl Task {
// shared state. Additionally, all of the violations are protected with a
// 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
// mutex. This address will not be changing (because the task is allocated
// on the heap). We must have this handle separately because the task will
// 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 thread is allocated
// on the heap). We must have this handle separately because the thread will
// have its ownership transferred to the given closure. We're guaranteed,
// 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
// function needs to replace the task back in TLS. There is no communication
// from the wakeup thread back to this thread about the task pointer, and
// there's really no need to. In order to get around this, we cast the task
// function needs to replace the thread back in TLS. There is no communication
// 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 thread
// 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
// ownership semantics in that there may be two `Box<Task>` objects.
//
// 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
// way that the compiler can understand. The task's memory is always valid
// for both tasks because these operations are all done inside of a mutex.
// way that the compiler can understand. The thread's memory is always valid
// 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
// 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
// semantics.
//
@ -319,11 +319,11 @@ impl Task {
let guard = (*me).lock.lock();
(*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
// 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
// task to become available. In other words, if task.wake()
// may not own the thread, so we may still have to wait for the
// thread to become available. In other words, if thread.wake()
// returns `None`, then someone else has ownership and we must
// wait for their signal.
match iter.map(f).filter_map(|a| a.err()).next() {
@ -342,15 +342,15 @@ impl Task {
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));
}
}
/// Wakes up a previously blocked task. This function can only be
/// called on tasks that were previously blocked in `deschedule`.
/// Wakes up a previously blocked thread. This function can only be
/// 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.
pub fn reawaken(mut self: Box<Task>) {
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
/// opportunity to allow other tasks a chance to run.
/// opportunity to allow other threads a chance to run.
pub fn yield_now() {
Thread::yield_now();
}
/// Returns the stack bounds for this task in (lo, hi) format. The stack
/// bounds may not be known for all tasks, so the return value may be
/// Returns the stack bounds for this thread in (lo, hi) format. The stack
/// bounds may not be known for all threads, so the return value may be
/// `None`.
pub fn stack_bounds(&self) -> (uint, uint) {
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> {
if self.stack_guard != 0 {
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.
pub fn drop(mut self) {
self.state = Destroyed;
@ -396,7 +396,7 @@ impl Task {
impl Drop for Task {
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);
}
}
@ -414,7 +414,7 @@ impl Iterator<BlockedTask> for BlockedTasks {
}
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>> {
match self {
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.
pub fn reawaken(self) {
self.wake().map(|t| t.reawaken());
@ -438,12 +438,12 @@ impl BlockedTask {
#[cfg(not(test))] pub fn trash(self) { }
#[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 {
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> {
let arc = match self {
Owned(task) => {
@ -543,7 +543,7 @@ mod test {
drop(Task::new(None, None));
}
// Task blocking tests
// Thread blocking tests
#[test]
fn block_and_wake() {

View File

@ -79,7 +79,7 @@ struct Exception {
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.
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
/// 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
/// 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
/// 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
/// run.
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.
//
// 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:
// - rust_eh_personality, used by all cleanup landing pads, which never catches,
// 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,
// 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
// 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.
// 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;
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
// 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
// have limited options. Currently our preference is to
// 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.");
unsafe { intrinsics::abort() }
}
@ -582,10 +582,10 @@ fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) ->
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
/// 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 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.

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Abstraction of a task pool for basic parallelism.
//! Abstraction of a thread pool for basic parallelism.
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.
///
/// # Example
@ -69,34 +69,34 @@ impl<'a> Drop for Sentinel<'a> {
/// assert_eq!(rx.iter().take(8u).sum(), 8u);
/// ```
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.
jobs: Sender<Thunk>
}
impl TaskPool {
/// Spawns a new task pool with `tasks` tasks.
/// Spawns a new thread pool with `threads` threads.
///
/// # Panics
///
/// This function will panic if `tasks` is 0.
pub fn new(tasks: uint) -> TaskPool {
assert!(tasks >= 1);
/// This function will panic if `threads` is 0.
pub fn new(threads: uint) -> TaskPool {
assert!(threads >= 1);
let (tx, rx) = channel::<Thunk>();
let rx = Arc::new(Mutex::new(rx));
// Taskpool tasks.
for _ in range(0, tasks) {
// Threadpool threads
for _ in range(0, threads) {
spawn_in_pool(rx.clone());
}
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)
where F : FnOnce(), F : Send
{
@ -106,7 +106,7 @@ impl TaskPool {
fn spawn_in_pool(jobs: Arc<Mutex<Receiver<Thunk>>>) {
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);
loop {
@ -165,12 +165,12 @@ mod test {
let pool = TaskPool::new(TEST_TASKS);
// Panic all the existing tasks.
// Panic all the existing threads.
for _ in range(0, TEST_TASKS) {
pool.execute(move|| -> () { panic!() });
}
// Ensure new tasks were spawned to compensate.
// Ensure new threads were spawned to compensate.
let (tx, rx) = channel();
for _ in range(0, TEST_TASKS) {
let tx = tx.clone();
@ -189,7 +189,7 @@ mod test {
let pool = TaskPool::new(TEST_TASKS);
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) {
let waiter = waiter.clone();
pool.execute(move|| {