Skip to content

Commit a27fbac

Browse files
committed
Revise std::thread API to join by default
This commit is part of a series that introduces a `std::thread` API to replace `std::task`. In the new API, `spawn` returns a `JoinGuard`, which by default will join the spawned thread when dropped. It can also be used to join explicitly at any time, returning the thread's result. Alternatively, the spawned thread can be explicitly detached (so no join takes place). As part of this change, Rust processes now terminate when the main thread exits, even if other detached threads are still running, moving Rust closer to standard threading models. This new behavior may break code that was relying on the previously implicit join-all. In addition to the above, the new thread API also offers some built-in support for building blocking abstractions in user space; see the module doc for details. Closes rust-lang#18000 [breaking-change]
1 parent 13f302d commit a27fbac

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+464
-1786
lines changed

src/compiletest/runtest.rs

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

src/doc/guide-tasks.md

+17-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
% The Rust Tasks and Communication Guide
22

3+
**NOTE** This guide is badly out of date an needs to be rewritten.
4+
35
# Introduction
46

57
Rust provides safe concurrent abstractions through a number of core library
@@ -22,7 +24,7 @@ from shared mutable state.
2224
At its simplest, creating a task is a matter of calling the `spawn` function
2325
with a closure argument. `spawn` executes the closure in the new task.
2426

25-
```{rust}
27+
```{rust,ignore}
2628
# use std::task::spawn;
2729
2830
// Print something profound in a different task using a named function
@@ -49,7 +51,7 @@ closure is limited to capturing `Send`-able data from its environment
4951
ensures that `spawn` can safely move the entire closure and all its
5052
associated state into an entirely different task for execution.
5153

52-
```{rust}
54+
```{rust,ignore}
5355
# use std::task::spawn;
5456
# fn generate_task_number() -> int { 0 }
5557
// Generate some state locally
@@ -75,7 +77,7 @@ The simplest way to create a channel is to use the `channel` function to create
7577
of a channel, and a **receiver** is the receiving endpoint. Consider the following
7678
example of calculating two results concurrently:
7779

78-
```{rust}
80+
```{rust,ignore}
7981
# use std::task::spawn;
8082
8183
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
@@ -96,15 +98,15 @@ stream for sending and receiving integers (the left-hand side of the `let`,
9698
`(tx, rx)`, is an example of a destructuring let: the pattern separates a tuple
9799
into its component parts).
98100

99-
```{rust}
101+
```{rust,ignore}
100102
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
101103
```
102104

103105
The child task will use the sender to send data to the parent task, which will
104106
wait to receive the data on the receiver. The next statement spawns the child
105107
task.
106108

107-
```{rust}
109+
```{rust,ignore}
108110
# use std::task::spawn;
109111
# fn some_expensive_computation() -> int { 42 }
110112
# let (tx, rx) = channel();
@@ -123,7 +125,7 @@ computation, then sends the result over the captured channel.
123125
Finally, the parent continues with some other expensive computation, then waits
124126
for the child's result to arrive on the receiver:
125127

126-
```{rust}
128+
```{rust,ignore}
127129
# fn some_other_expensive_computation() {}
128130
# let (tx, rx) = channel::<int>();
129131
# tx.send(0);
@@ -154,7 +156,7 @@ spawn(move || {
154156

155157
Instead we can clone the `tx`, which allows for multiple senders.
156158

157-
```{rust}
159+
```{rust,ignore}
158160
let (tx, rx) = channel();
159161
160162
for init_val in range(0u, 3) {
@@ -179,7 +181,7 @@ Note that the above cloning example is somewhat contrived since you could also
179181
simply use three `Sender` pairs, but it serves to illustrate the point. For
180182
reference, written with multiple streams, it might look like the example below.
181183

182-
```{rust}
184+
```{rust,ignore}
183185
# use std::task::spawn;
184186
185187
// Create a vector of ports, one for each child task
@@ -203,7 +205,7 @@ getting the result later.
203205

204206
The basic example below illustrates this.
205207

206-
```{rust}
208+
```{rust,ignore}
207209
use std::sync::Future;
208210
209211
# fn main() {
@@ -230,7 +232,7 @@ called.
230232
Here is another example showing how futures allow you to background
231233
computations. The workload will be distributed on the available cores.
232234

233-
```{rust}
235+
```{rust,ignore}
234236
# use std::num::Float;
235237
# use std::sync::Future;
236238
fn partial_sum(start: uint) -> f64 {
@@ -268,7 +270,7 @@ Here is a small example showing how to use Arcs. We wish to run concurrently
268270
several computations on a single large vector of floats. Each task needs the
269271
full vector to perform its duty.
270272

271-
```{rust}
273+
```{rust,ignore}
272274
use std::num::Float;
273275
use std::rand;
274276
use std::sync::Arc;
@@ -295,7 +297,7 @@ The function `pnorm` performs a simple computation on the vector (it computes
295297
the sum of its items at the power given as argument and takes the inverse power
296298
of this value). The Arc on the vector is created by the line:
297299

298-
```{rust}
300+
```{rust,ignore}
299301
# use std::rand;
300302
# use std::sync::Arc;
301303
# fn main() {
@@ -309,7 +311,7 @@ the wrapper and not its contents. Within the task's procedure, the captured
309311
Arc reference can be used as a shared reference to the underlying vector as
310312
if it were local.
311313

312-
```{rust}
314+
```{rust,ignore}
313315
# use std::rand;
314316
# use std::sync::Arc;
315317
# fn pnorm(nums: &[f64], p: uint) -> f64 { 4.0 }
@@ -346,11 +348,11 @@ and `()`, callers can pattern-match on a result to check whether it's an `Ok`
346348
result with an `int` field (representing a successful result) or an `Err` result
347349
(representing termination with an error).
348350

349-
```{rust}
351+
```{rust,ignore}
350352
# use std::thread::Thread;
351353
# fn some_condition() -> bool { false }
352354
# fn calculate_result() -> int { 0 }
353-
let result: Result<int, Box<std::any::Any + Send>> = Thread::with_join(move || {
355+
let result: Result<int, Box<std::any::Any + Send>> = Thread::spawn(move || {
354356
if some_condition() {
355357
calculate_result()
356358
} else {

src/doc/guide.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -5217,6 +5217,8 @@ the same function, so our binary is a little bit larger.
52175217
52185218
# Tasks
52195219
5220+
**NOTE**: this section is currently out of date and will be rewritten soon.
5221+
52205222
Concurrency and parallelism are topics that are of increasing interest to a
52215223
broad subsection of software developers. Modern computers are often multi-core,
52225224
to the point that even embedded devices like cell phones have more than one
@@ -5231,7 +5233,7 @@ library, and not part of the language. This means that in the future, other
52315233
concurrency libraries can be written for Rust to help in specific scenarios.
52325234
Here's an example of creating a task:
52335235
5234-
```{rust}
5236+
```{rust,ignore}
52355237
spawn(move || {
52365238
println!("Hello from a task!");
52375239
});
@@ -5261,7 +5263,7 @@ If tasks were only able to capture these values, they wouldn't be very useful.
52615263
Luckily, tasks can communicate with each other through **channel**s. Channels
52625264
work like this:
52635265
5264-
```{rust}
5266+
```{rust,ignore}
52655267
let (tx, rx) = channel();
52665268
52675269
spawn(move || {
@@ -5280,7 +5282,7 @@ which returns an `Result<T, TryRecvError>` and does not block.
52805282
52815283
If you want to send messages to the task as well, create two channels!
52825284
5283-
```{rust}
5285+
```{rust,ignore}
52845286
let (tx1, rx1) = channel();
52855287
let (tx2, rx2) = channel();
52865288
@@ -5340,7 +5342,7 @@ we'll just get the value immediately.
53405342
Tasks don't always succeed, they can also panic. A task that wishes to panic
53415343
can call the `panic!` macro, passing a message:
53425344
5343-
```{rust}
5345+
```{rust,ignore}
53445346
spawn(move || {
53455347
panic!("Nope.");
53465348
});
@@ -5349,7 +5351,7 @@ spawn(move || {
53495351
If a task panics, it is not possible for it to recover. However, it can
53505352
notify other tasks that it has panicked. We can do this with `task::try`:
53515353
5352-
```{rust}
5354+
```{rust,ignore}
53535355
use std::task;
53545356
use std::rand;
53555357

src/doc/intro.md

+17-9
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,13 @@ safe concurrent programs.
389389
Here's an example of a concurrent Rust program:
390390
391391
```{rust}
392+
use std::thread::Thread;
393+
392394
fn main() {
393395
for _ in range(0u, 10u) {
394-
spawn(move || {
396+
Thread::spawn(move || {
395397
println!("Hello, world!");
396-
});
398+
}).detach();
397399
}
398400
}
399401
```
@@ -403,7 +405,8 @@ This program creates ten threads, who all print `Hello, world!`. The
403405
double bars `||`. (The `move` keyword indicates that the closure takes
404406
ownership of any data it uses; we'll have more on the significance of
405407
this shortly.) This closure is executed in a new thread created by
406-
`spawn`.
408+
`spawn`. The `detach` method means that the child thread is allowed to
409+
outlive its parent.
407410
408411
One common form of problem in concurrent programs is a 'data race.'
409412
This occurs when two different threads attempt to access the same
@@ -418,13 +421,15 @@ problem.
418421
Let's see an example. This Rust code will not compile:
419422
420423
```{rust,ignore}
424+
use std::thread::Thread;
425+
421426
fn main() {
422427
let mut numbers = vec![1i, 2i, 3i];
423428

424429
for i in range(0u, 3u) {
425-
spawn(move || {
430+
Thread::spawn(move || {
426431
for j in range(0, 3) { numbers[j] += 1 }
427-
});
432+
}).detach();
428433
}
429434
}
430435
```
@@ -469,20 +474,21 @@ mutation doesn't cause a data race.
469474
Here's what using an Arc with a Mutex looks like:
470475
471476
```{rust}
477+
use std::thread::Thread;
472478
use std::sync::{Arc,Mutex};
473479
474480
fn main() {
475481
let numbers = Arc::new(Mutex::new(vec![1i, 2i, 3i]));
476482
477483
for i in range(0u, 3u) {
478484
let number = numbers.clone();
479-
spawn(move || {
485+
Thread::spawn(move || {
480486
let mut array = number.lock();
481487
482488
(*array)[i] += 1;
483489
484490
println!("numbers[{}] is {}", i, (*array)[i]);
485-
});
491+
}).detach();
486492
}
487493
}
488494
```
@@ -532,13 +538,15 @@ As an example, Rust's ownership system is _entirely_ at compile time. The
532538
safety check that makes this an error about moved values:
533539
534540
```{rust,ignore}
541+
use std::thread::Thread;
542+
535543
fn main() {
536544
let vec = vec![1i, 2, 3];
537545
538546
for i in range(1u, 3) {
539-
spawn(move || {
547+
Thread::spawn(move || {
540548
println!("{}", vec[i]);
541-
});
549+
}).detach();
542550
}
543551
}
544552
```

src/liballoc/arc.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use heap::deallocate;
3939
///
4040
/// ```rust
4141
/// use std::sync::Arc;
42+
/// use std::thread::Thread;
4243
///
4344
/// fn main() {
4445
/// let numbers = Vec::from_fn(100, |i| i as f32);
@@ -47,11 +48,11 @@ use heap::deallocate;
4748
/// for _ in range(0u, 10) {
4849
/// let child_numbers = shared_numbers.clone();
4950
///
50-
/// spawn(move || {
51+
/// Thread::spawn(move || {
5152
/// let local_numbers = child_numbers.as_slice();
5253
///
5354
/// // Work with the local numbers
54-
/// });
55+
/// }).detach();
5556
/// }
5657
/// }
5758
/// ```

src/libcollections/slice.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,7 @@ pub mod raw {
13441344

13451345
#[cfg(test)]
13461346
mod tests {
1347+
use std::boxed::Box;
13471348
use std::cell::Cell;
13481349
use std::default::Default;
13491350
use std::mem;
@@ -1627,7 +1628,10 @@ mod tests {
16271628
#[test]
16281629
fn test_swap_remove_noncopyable() {
16291630
// Tests that we don't accidentally run destructors twice.
1630-
let mut v = vec![Box::new(()), Box::new(()), Box::new(())];
1631+
let mut v = Vec::new();
1632+
v.push(box 0u8);
1633+
v.push(box 0u8);
1634+
v.push(box 0u8);
16311635
let mut _e = v.swap_remove(0);
16321636
assert_eq!(v.len(), 2);
16331637
_e = v.swap_remove(1);

src/libcore/borrow.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl<'a, T, Sized? B> BorrowFrom<Cow<'a, T, B>> for B where B: ToOwned<T> {
9292

9393
/// Trait for moving into a `Cow`
9494
pub trait IntoCow<'a, T, Sized? B> {
95-
/// Moves `serlf` into `Cow`
95+
/// Moves `self` into `Cow`
9696
fn into_cow(self) -> Cow<'a, T, B>;
9797
}
9898

src/libcoretest/finally.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use core::finally::{try_finally, Finally};
12-
use std::task::failing;
12+
use std::thread::Thread;
1313

1414
#[test]
1515
fn test_success() {
@@ -20,7 +20,7 @@ fn test_success() {
2020
*i = 10;
2121
},
2222
|i| {
23-
assert!(!failing());
23+
assert!(!Thread::panicking());
2424
assert_eq!(*i, 10);
2525
*i = 20;
2626
});
@@ -38,7 +38,7 @@ fn test_fail() {
3838
panic!();
3939
},
4040
|i| {
41-
assert!(failing());
41+
assert!(Thread::panicking());
4242
assert_eq!(*i, 10);
4343
})
4444
}

src/librustc_driver/lib.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -475,22 +475,18 @@ pub fn monitor<F:FnOnce()+Send>(f: F) {
475475
static STACK_SIZE: uint = 32000000; // 32MB
476476

477477
let (tx, rx) = channel();
478-
let mut w = Some(io::ChanWriter::new(tx)); // option dance
478+
let w = io::ChanWriter::new(tx);
479479
let mut r = io::ChanReader::new(rx);
480480

481-
let mut cfg = thread::cfg().name("rustc".to_string());
481+
let mut cfg = thread::Builder::new().name("rustc".to_string());
482482

483483
// FIXME: Hacks on hacks. If the env is trying to override the stack size
484484
// then *don't* set it explicitly.
485485
if os::getenv("RUST_MIN_STACK").is_none() {
486486
cfg = cfg.stack_size(STACK_SIZE);
487487
}
488488

489-
let f = proc() {
490-
std::io::stdio::set_stderr(box w.take().unwrap());
491-
f()
492-
};
493-
match cfg.with_join(f).join() {
489+
match cfg.spawn(move || { std::io::stdio::set_stderr(box w); f() }).join() {
494490
Ok(()) => { /* fallthrough */ }
495491
Err(value) => {
496492
// Task panicked without emitting a fatal diagnostic

0 commit comments

Comments
 (0)