Skip to content

Commit b9d9a37

Browse files
committed
rollup merge of rust-lang#24385: aturon/unstable-scoped
Conflicts: src/libstd/thread/mod.rs src/test/bench/shootout-mandelbrot.rs src/test/bench/shootout-reverse-complement.rs src/test/run-pass/capturing-logging.rs src/test/run-pass/issue-9396.rs src/test/run-pass/tcp-accept-stress.rs src/test/run-pass/tcp-connect-timeouts.rs src/test/run-pass/tempfile.rs
2 parents ae7959d + a9fd41e commit b9d9a37

Some content is hidden

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

51 files changed

+537
-131
lines changed

src/doc/intro.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ safe concurrent programs.
389389
Here's an example of a concurrent Rust program:
390390
391391
```{rust}
392+
# #![feature(scoped)]
392393
use std::thread;
393394
394395
fn main() {
@@ -421,6 +422,7 @@ problem.
421422
Let's see an example. This Rust code will not compile:
422423
423424
```{rust,ignore}
425+
# #![feature(scoped)]
424426
use std::thread;
425427
426428
fn main() {
@@ -467,6 +469,7 @@ that our mutation doesn't cause a data race.
467469
Here's what using a Mutex looks like:
468470
469471
```{rust}
472+
# #![feature(scoped)]
470473
use std::thread;
471474
use std::sync::Mutex;
472475
@@ -527,6 +530,7 @@ As an example, Rust's ownership system is _entirely_ at compile time. The
527530
safety check that makes this an error about moved values:
528531
529532
```{rust,ignore}
533+
# #![feature(scoped)]
530534
use std::thread;
531535
532536
fn main() {

src/doc/trpl/concurrency.md

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -56,68 +56,35 @@ place!
5656

5757
## Threads
5858

59-
Rust's standard library provides a library for 'threads', which allow you to
59+
Rust's standard library provides a library for threads, which allow you to
6060
run Rust code in parallel. Here's a basic example of using `std::thread`:
6161

6262
```
6363
use std::thread;
6464
6565
fn main() {
66-
thread::scoped(|| {
66+
thread::spawn(|| {
6767
println!("Hello from a thread!");
6868
});
6969
}
7070
```
7171

72-
The `thread::scoped()` method accepts a closure, which is executed in a new
73-
thread. It's called `scoped` because this thread returns a join guard:
72+
The `thread::spawn()` method accepts a closure, which is executed in a
73+
new thread. It returns a handle to the thread, that can be used to
74+
wait for the child thread to finish and extract its result:
7475

7576
```
7677
use std::thread;
7778
7879
fn main() {
79-
let guard = thread::scoped(|| {
80-
println!("Hello from a thread!");
80+
let handle = thread::spawn(|| {
81+
"Hello from a thread!"
8182
});
8283
83-
// guard goes out of scope here
84+
println!("{}", handle.join().unwrap());
8485
}
8586
```
8687

87-
When `guard` goes out of scope, it will block execution until the thread is
88-
finished. If we didn't want this behaviour, we could use `thread::spawn()`:
89-
90-
```
91-
use std::thread;
92-
93-
fn main() {
94-
thread::spawn(|| {
95-
println!("Hello from a thread!");
96-
});
97-
98-
thread::sleep_ms(50);
99-
}
100-
```
101-
102-
We need to `sleep` here because when `main()` ends, it kills all of the
103-
running threads.
104-
105-
[`scoped`](std/thread/struct.Builder.html#method.scoped) has an interesting
106-
type signature:
107-
108-
```text
109-
fn scoped<'a, T, F>(self, f: F) -> JoinGuard<'a, T>
110-
where T: Send + 'a,
111-
F: FnOnce() -> T,
112-
F: Send + 'a
113-
```
114-
115-
Specifically, `F`, the closure that we pass to execute in the new thread. It
116-
has two restrictions: It must be a `FnOnce` from `()` to `T`. Using `FnOnce`
117-
allows the closure to take ownership of any data it mentions from the parent
118-
thread. The other restriction is that `F` must be `Send`. We aren't allowed to
119-
transfer this ownership unless the type thinks that's okay.
120-
12188
Many languages have the ability to execute threads, but it's wildly unsafe.
12289
There are entire books about how to prevent errors that occur from shared
12390
mutable state. Rust helps out with its type system here as well, by preventing

src/librustdoc/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,10 @@ struct Output {
130130

131131
pub fn main() {
132132
const STACK_SIZE: usize = 32000000; // 32MB
133-
let res = std::thread::Builder::new().stack_size(STACK_SIZE).scoped(move || {
133+
let res = std::thread::Builder::new().stack_size(STACK_SIZE).spawn(move || {
134134
let s = env::args().collect::<Vec<_>>();
135135
main_args(&s)
136-
}).unwrap().join();
136+
}).unwrap().join().unwrap();
137137
env::set_exit_status(res as i32);
138138
}
139139

src/libstd/thread/mod.rs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,33 @@
6767
//! thread. This means that it can outlive its parent (the thread that spawned
6868
//! it), unless this parent is the main thread.
6969
//!
70+
//! The parent thread can also wait on the completion of the child
71+
//! thread; a call to `spawn` produces a `JoinHandle`, which provides
72+
//! a `join` method for waiting:
73+
//!
74+
//! ```rust
75+
//! use std::thread;
76+
//!
77+
//! let child = thread::spawn(move || {
78+
//! // some work here
79+
//! });
80+
//! // some work here
81+
//! let res = child.join();
82+
//! ```
83+
//!
84+
//! The `join` method returns a `Result` containing `Ok` of the final
85+
//! value produced by the child thread, or `Err` of the value given to
86+
//! a call to `panic!` if the child panicked.
87+
//!
7088
//! ## Scoped threads
7189
//!
72-
//! Often a parent thread uses a child thread to perform some particular task,
73-
//! and at some point must wait for the child to complete before continuing.
74-
//! For this scenario, use the `thread::scoped` function:
90+
//! The `spawn` method does not allow the child and parent threads to
91+
//! share any stack data, since that is not safe in general. However,
92+
//! `scoped` makes it possible to share the parent's stack by forcing
93+
//! a join before any relevant stack frames are popped:
7594
//!
7695
//! ```rust
96+
//! # #![feature(scoped)]
7797
//! use std::thread;
7898
//!
7999
//! let guard = thread::scoped(move || {
@@ -253,8 +273,8 @@ impl Builder {
253273
/// `io::Result` to capture any failure to create the thread at
254274
/// the OS level.
255275
#[stable(feature = "rust1", since = "1.0.0")]
256-
pub fn spawn<F>(self, f: F) -> io::Result<JoinHandle> where
257-
F: FnOnce(), F: Send + 'static
276+
pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>> where
277+
F: FnOnce() -> T, F: Send + 'static, T: Send + 'static
258278
{
259279
self.spawn_inner(Box::new(f)).map(|i| JoinHandle(i))
260280
}
@@ -274,7 +294,8 @@ impl Builder {
274294
/// Unlike the `scoped` free function, this method yields an
275295
/// `io::Result` to capture any failure to create the thread at
276296
/// the OS level.
277-
#[stable(feature = "rust1", since = "1.0.0")]
297+
#[unstable(feature = "scoped",
298+
reason = "memory unsafe if destructor is avoided, see #24292")]
278299
pub fn scoped<'a, T, F>(self, f: F) -> io::Result<JoinGuard<'a, T>> where
279300
T: Send + 'a, F: FnOnce() -> T, F: Send + 'a
280301
{
@@ -370,7 +391,9 @@ impl Builder {
370391
/// Panics if the OS fails to create a thread; use `Builder::spawn`
371392
/// to recover from such errors.
372393
#[stable(feature = "rust1", since = "1.0.0")]
373-
pub fn spawn<F>(f: F) -> JoinHandle where F: FnOnce(), F: Send + 'static {
394+
pub fn spawn<F, T>(f: F) -> JoinHandle<T> where
395+
F: FnOnce() -> T, F: Send + 'static, T: Send + 'static
396+
{
374397
Builder::new().spawn(f).unwrap()
375398
}
376399

@@ -387,7 +410,8 @@ pub fn spawn<F>(f: F) -> JoinHandle where F: FnOnce(), F: Send + 'static {
387410
///
388411
/// Panics if the OS fails to create a thread; use `Builder::scoped`
389412
/// to recover from such errors.
390-
#[stable(feature = "rust1", since = "1.0.0")]
413+
#[unstable(feature = "scoped",
414+
reason = "memory unsafe if destructor is avoided, see #24292")]
391415
pub fn scoped<'a, T, F>(f: F) -> JoinGuard<'a, T> where
392416
T: Send + 'a, F: FnOnce() -> T, F: Send + 'a
393417
{
@@ -635,9 +659,9 @@ impl<T> JoinInner<T> {
635659
/// handle: the ability to join a child thread is a uniquely-owned
636660
/// permission.
637661
#[stable(feature = "rust1", since = "1.0.0")]
638-
pub struct JoinHandle(JoinInner<()>);
662+
pub struct JoinHandle<T>(JoinInner<T>);
639663

640-
impl JoinHandle {
664+
impl<T> JoinHandle<T> {
641665
/// Extracts a handle to the underlying thread
642666
#[stable(feature = "rust1", since = "1.0.0")]
643667
pub fn thread(&self) -> &Thread {
@@ -649,13 +673,14 @@ impl JoinHandle {
649673
/// If the child thread panics, `Err` is returned with the parameter given
650674
/// to `panic`.
651675
#[stable(feature = "rust1", since = "1.0.0")]
652-
pub fn join(mut self) -> Result<()> {
676+
pub fn join(mut self) -> Result<T> {
653677
self.0.join()
654678
}
655679
}
656680

657681
#[stable(feature = "rust1", since = "1.0.0")]
658-
impl Drop for JoinHandle {
682+
#[unsafe_destructor]
683+
impl<T> Drop for JoinHandle<T> {
659684
fn drop(&mut self) {
660685
if !self.0.joined {
661686
unsafe { imp::detach(self.0.native) }
@@ -674,7 +699,8 @@ impl Drop for JoinHandle {
674699
/// handle: the ability to join a child thread is a uniquely-owned
675700
/// permission.
676701
#[must_use = "thread will be immediately joined if `JoinGuard` is not used"]
677-
#[stable(feature = "rust1", since = "1.0.0")]
702+
#[unstable(feature = "scoped",
703+
reason = "memory unsafe if destructor is avoided, see #24292")]
678704
pub struct JoinGuard<'a, T: Send + 'a> {
679705
inner: JoinInner<T>,
680706
_marker: PhantomData<&'a T>,
@@ -706,7 +732,8 @@ impl<'a, T: Send + 'a> JoinGuard<'a, T> {
706732
}
707733

708734
#[unsafe_destructor]
709-
#[stable(feature = "rust1", since = "1.0.0")]
735+
#[unstable(feature = "scoped",
736+
reason = "memory unsafe if destructor is avoided, see #24292")]
710737
impl<'a, T: Send + 'a> Drop for JoinGuard<'a, T> {
711738
fn drop(&mut self) {
712739
if !self.inner.joined {

src/test/bench/shootout-binarytrees.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ fn main() {
111111
let messages = (min_depth..max_depth + 1).step_by(2).map(|depth| {
112112
use std::num::Int;
113113
let iterations = 2.pow((max_depth - depth + min_depth) as u32);
114-
thread::scoped(move || inner(depth, iterations))
114+
thread::spawn(move || inner(depth, iterations))
115115
}).collect::<Vec<_>>();
116116

117117
for message in messages {
118-
println!("{}", message.join());
118+
println!("{}", message.join().unwrap());
119119
}
120120

121121
println!("long lived tree of depth {}\t check: {}",

src/test/bench/shootout-fannkuch-redux.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,15 @@ fn fannkuch(n: i32) -> (i32, i32) {
166166
for (_, j) in (0..N).zip((0..).step_by(k)) {
167167
let max = cmp::min(j+k, perm.max());
168168

169-
futures.push(thread::scoped(move|| {
169+
futures.push(thread::spawn(move|| {
170170
work(perm, j as usize, max as usize)
171171
}))
172172
}
173173

174174
let mut checksum = 0;
175175
let mut maxflips = 0;
176176
for fut in futures {
177-
let (cs, mf) = fut.join();
177+
let (cs, mf) = fut.join().unwrap();
178178
checksum += cs;
179179
maxflips = cmp::max(maxflips, mf);
180180
}

src/test/bench/shootout-k-nucleotide.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,17 +307,17 @@ fn main() {
307307

308308
let nb_freqs: Vec<_> = (1..3).map(|i| {
309309
let input = input.clone();
310-
(i, thread::scoped(move|| generate_frequencies(&input, i)))
310+
(i, thread::spawn(move|| generate_frequencies(&input, i)))
311311
}).collect();
312312
let occ_freqs: Vec<_> = OCCURRENCES.iter().map(|&occ| {
313313
let input = input.clone();
314-
thread::scoped(move|| generate_frequencies(&input, occ.len()))
314+
thread::spawn(move|| generate_frequencies(&input, occ.len()))
315315
}).collect();
316316

317317
for (i, freq) in nb_freqs {
318-
print_frequencies(&freq.join(), i);
318+
print_frequencies(&freq.join().unwrap(), i);
319319
}
320320
for (&occ, freq) in OCCURRENCES.iter().zip(occ_freqs.into_iter()) {
321-
print_occurrences(&mut freq.join(), occ);
321+
print_occurrences(&mut freq.join().unwrap(), occ);
322322
}
323323
}

src/test/bench/shootout-mandelbrot.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> {
8181
let mut precalc_i = Vec::with_capacity(h);
8282

8383
let precalc_futures = (0..WORKERS).map(|i| {
84-
thread::scoped(move|| {
84+
thread::spawn(move|| {
8585
let mut rs = Vec::with_capacity(w / WORKERS);
8686
let mut is = Vec::with_capacity(w / WORKERS);
8787

@@ -107,7 +107,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> {
107107
}).collect::<Vec<_>>();
108108

109109
for res in precalc_futures {
110-
let (rs, is) = res.join();
110+
let (rs, is) = res.join().unwrap();
111111
precalc_r.extend(rs.into_iter());
112112
precalc_i.extend(is.into_iter());
113113
}
@@ -122,7 +122,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> {
122122
let vec_init_r = arc_init_r.clone();
123123
let vec_init_i = arc_init_i.clone();
124124

125-
thread::scoped(move|| {
125+
thread::spawn(move|| {
126126
let mut res: Vec<u8> = Vec::with_capacity((chunk_size * w) / 8);
127127
let init_r_slice = vec_init_r;
128128

@@ -143,7 +143,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> {
143143

144144
try!(writeln!(&mut out, "P4\n{} {}", w, h));
145145
for res in data {
146-
try!(out.write_all(&res.join()));
146+
try!(out.write_all(&res.join().unwrap()));
147147
}
148148
out.flush()
149149
}

src/test/bench/shootout-reverse-complement.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
// ignore-android see #10393 #13206
4242

43-
#![feature(libc)]
43+
#![feature(libc, scoped)]
4444

4545
extern crate libc;
4646

src/test/bench/shootout-spectralnorm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
// no-pretty-expanded FIXME #15189
4242

4343
#![allow(non_snake_case)]
44-
#![feature(unboxed_closures, core, os)]
44+
#![feature(unboxed_closures, core, os, scoped)]
4545

4646
use std::iter::repeat;
4747
use std::thread;

src/test/run-fail/panic-task-name-owned.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
use std::thread::Builder;
1414

1515
fn main() {
16-
let r: () = Builder::new().name("owned name".to_string()).scoped(move|| {
16+
let r: () = Builder::new().name("owned name".to_string()).spawn(move|| {
1717
panic!("test");
1818
()
19-
}).unwrap().join();
19+
}).unwrap().join().unwrap();
2020
panic!();
2121
}

src/test/run-fail/rt-set-exit-status-panic2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn r(x:isize) -> r {
3737

3838
fn main() {
3939
error!("whatever");
40-
let _t = thread::scoped(move|| {
40+
let _t = thread::spawn(move|| {
4141
let _i = r(5);
4242
});
4343
panic!();

src/test/run-pass/atomic-print.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn main(){
2727
if env::args().count() == 2 {
2828
let barrier = sync::Arc::new(sync::Barrier::new(2));
2929
let tbarrier = barrier.clone();
30-
let t = thread::scoped(||{
30+
let t = thread::spawn(move || {
3131
tbarrier.wait();
3232
do_print(1);
3333
});

src/test/run-pass/clone-with-exterior.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ struct Pair {
2222
pub fn main() {
2323
let z: Box<_> = box Pair { a : 10, b : 12};
2424

25-
let _t = thread::scoped(move|| {
25+
thread::spawn(move|| {
2626
assert_eq!(z.a, 10);
2727
assert_eq!(z.b, 12);
28-
});
28+
}).join();
2929
}

0 commit comments

Comments
 (0)