Skip to content

Commit 36f5d12

Browse files
committed
rollup merge of rust-lang#20615: aturon/stab-2-thread
This commit takes a first pass at stabilizing `std::thread`: * It removes the `detach` method in favor of two constructors -- `spawn` for detached threads, `scoped` for "scoped" (i.e., must-join) threads. This addresses some of the surprise/frustrating debug sessions with the previous API, in which `spawn` produced a guard that on destruction joined the thread (unless `detach` was called). The reason to have the division in part is that `Send` will soon not imply `'static`, which means that `scoped` thread creation can take a closure over *shared stack data* of the parent thread. On the other hand, this means that the parent must not pop the relevant stack frames while the child thread is running. The `JoinGuard` is used to prevent this from happening by joining on drop (if you have not already explicitly `join`ed.) The APIs around `scoped` are future-proofed for the `Send` changes by taking an additional lifetime parameter. With the current definition of `Send`, this is forced to be `'static`, but when `Send` changes these APIs will gain their full flexibility immediately. Threads that are `spawn`ed, on the other hand, are detached from the start and do not yield an RAII guard. The hope is that, by making `scoped` an explicit opt-in with a very suggestive name, it will be drastically less likely to be caught by a surprising deadlock due to an implicit join at the end of a scope. * The module itself is marked stable. * Existing methods other than `spawn` and `scoped` are marked stable. The migration path is: ```rust Thread::spawn(f).detached() ``` becomes ```rust Thread::spawn(f) ``` while ```rust let res = Thread::spawn(f); res.join() ``` becomes ```rust let res = Thread::scoped(f); res.join() ``` [breaking-change]
2 parents 0631b46 + caca9b2 commit 36f5d12

File tree

97 files changed

+359
-291
lines changed

Some content is hidden

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

97 files changed

+359
-291
lines changed

src/compiletest/runtest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
447447
loop {
448448
//waiting 1 second for gdbserver start
449449
timer::sleep(Duration::milliseconds(1000));
450-
let result = Thread::spawn(move || {
450+
let result = Thread::scoped(move || {
451451
tcp::TcpStream::connect("127.0.0.1:5039").unwrap();
452452
}).join();
453453
if result.is_err() {

src/doc/intro.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ fn main() {
395395
for _ in range(0u, 10u) {
396396
Thread::spawn(move || {
397397
println!("Hello, world!");
398-
}).detach();
398+
});
399399
}
400400
}
401401
```
@@ -405,8 +405,7 @@ This program creates ten threads, who all print `Hello, world!`. The
405405
double bars `||`. (The `move` keyword indicates that the closure takes
406406
ownership of any data it uses; we'll have more on the significance of
407407
this shortly.) This closure is executed in a new thread created by
408-
`spawn`. The `detach` method means that the child thread is allowed to
409-
outlive its parent.
408+
`spawn`.
410409
411410
One common form of problem in concurrent programs is a 'data race.'
412411
This occurs when two different threads attempt to access the same
@@ -429,7 +428,7 @@ fn main() {
429428
for i in range(0u, 3u) {
430429
Thread::spawn(move || {
431430
for j in range(0, 3) { numbers[j] += 1 }
432-
}).detach();
431+
});
433432
}
434433
}
435434
```
@@ -488,7 +487,7 @@ fn main() {
488487
(*array)[i] += 1;
489488
490489
println!("numbers[{}] is {}", i, (*array)[i]);
491-
}).detach();
490+
});
492491
}
493492
}
494493
```

src/liballoc/arc.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
//!
4343
//! Thread::spawn(move || {
4444
//! println!("{}", five);
45-
//! }).detach();
45+
//! });
4646
//! }
4747
//! ```
4848
//!
@@ -63,7 +63,7 @@
6363
//! *number += 1;
6464
//!
6565
//! println!("{}", *number); // prints 6
66-
//! }).detach();
66+
//! });
6767
//! }
6868
//! ```
6969
@@ -106,7 +106,7 @@ use heap::deallocate;
106106
/// let local_numbers = child_numbers.as_slice();
107107
///
108108
/// // Work with the local numbers
109-
/// }).detach();
109+
/// });
110110
/// }
111111
/// }
112112
/// ```

src/libcollections/dlist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ mod tests {
924924
#[test]
925925
fn test_send() {
926926
let n = list_from(&[1i,2,3]);
927-
Thread::spawn(move || {
927+
Thread::scoped(move || {
928928
check_links(&n);
929929
let a: &[_] = &[&1,&2,&3];
930930
assert_eq!(a, n.iter().collect::<Vec<&int>>());

src/libcore/atomic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
//! let spinlock_clone = spinlock.clone();
5151
//! Thread::spawn(move|| {
5252
//! spinlock_clone.store(0, Ordering::SeqCst);
53-
//! }).detach();
53+
//! });
5454
//!
5555
//! // Wait for the other task to release the lock
5656
//! while spinlock.load(Ordering::SeqCst) != 0 {}

src/librustc_driver/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ pub fn monitor<F:FnOnce()+Send>(f: F) {
543543
cfg = cfg.stack_size(STACK_SIZE);
544544
}
545545

546-
match cfg.spawn(move || { std::io::stdio::set_stderr(box w); f() }).join() {
546+
match cfg.scoped(move || { std::io::stdio::set_stderr(box w); f() }).join() {
547547
Ok(()) => { /* fallthrough */ }
548548
Err(value) => {
549549
// Thread panicked without emitting a fatal diagnostic

src/librustc_trans/back/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ fn run_work_multithreaded(sess: &Session,
928928
}
929929

930930
tx.take().unwrap().send(()).unwrap();
931-
}).detach();
931+
});
932932
}
933933

934934
let mut panicked = false;

src/librustdoc/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ struct Output {
103103

104104
pub fn main() {
105105
static STACK_SIZE: uint = 32000000; // 32MB
106-
let res = std::thread::Builder::new().stack_size(STACK_SIZE).spawn(move || {
106+
let res = std::thread::Builder::new().stack_size(STACK_SIZE).scoped(move || {
107107
main_args(std::os::args().as_slice())
108108
}).join();
109109
std::os::set_exit_status(res.map_err(|_| ()).unwrap());
@@ -345,7 +345,7 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche
345345
let cr = Path::new(cratefile);
346346
info!("starting to run rustc");
347347

348-
let (mut krate, analysis) = std::thread::Thread::spawn(move |:| {
348+
let (mut krate, analysis) = std::thread::Thread::scoped(move |:| {
349349
let cr = cr;
350350
core::run_core(paths, cfgs, externs, &cr, triple)
351351
}).join().map_err(|_| "rustc failed").unwrap();

src/librustdoc/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ fn runtest(test: &str, cratename: &str, libs: SearchPaths,
157157
None => box io::stderr() as Box<Writer>,
158158
};
159159
io::util::copy(&mut p, &mut err).unwrap();
160-
}).detach();
160+
});
161161
let emitter = diagnostic::EmitterWriter::new(box w2, None);
162162

163163
// Compile the code

src/libstd/io/comm_adapters.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ mod test {
173173
tx.send(vec![3u8, 4u8]).unwrap();
174174
tx.send(vec![5u8, 6u8]).unwrap();
175175
tx.send(vec![7u8, 8u8]).unwrap();
176-
}).detach();
176+
});
177177

178178
let mut reader = ChanReader::new(rx);
179179
let mut buf = [0u8; 3];
@@ -216,7 +216,7 @@ mod test {
216216
tx.send(b"rld\nhow ".to_vec()).unwrap();
217217
tx.send(b"are you?".to_vec()).unwrap();
218218
tx.send(b"".to_vec()).unwrap();
219-
}).detach();
219+
});
220220

221221
let mut reader = ChanReader::new(rx);
222222

@@ -235,7 +235,7 @@ mod test {
235235
writer.write_be_u32(42).unwrap();
236236

237237
let wanted = vec![0u8, 0u8, 0u8, 42u8];
238-
let got = match Thread::spawn(move|| { rx.recv().unwrap() }).join() {
238+
let got = match Thread::scoped(move|| { rx.recv().unwrap() }).join() {
239239
Ok(got) => got,
240240
Err(_) => panic!(),
241241
};

src/libstd/io/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,12 @@
120120
//! for stream in acceptor.incoming() {
121121
//! match stream {
122122
//! Err(e) => { /* connection failed */ }
123-
//! Ok(stream) => Thread::spawn(move|| {
124-
//! // connection succeeded
125-
//! handle_client(stream)
126-
//! }).detach()
123+
//! Ok(stream) => {
124+
//! Thread::spawn(move|| {
125+
//! // connection succeeded
126+
//! handle_client(stream)
127+
//! });
128+
//! }
127129
//! }
128130
//! }
129131
//!

src/libstd/io/net/pipe.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ mod tests {
608608
let mut a = a;
609609
let _s = a.accept().unwrap();
610610
let _ = rx.recv();
611-
}).detach();
611+
});
612612

613613
let mut b = [0];
614614
let mut s = UnixStream::connect(&addr).unwrap();
@@ -645,7 +645,7 @@ mod tests {
645645
let mut a = a;
646646
let _s = a.accept().unwrap();
647647
let _ = rx.recv();
648-
}).detach();
648+
});
649649

650650
let mut s = UnixStream::connect(&addr).unwrap();
651651
let s2 = s.clone();
@@ -672,7 +672,7 @@ mod tests {
672672
rx.recv().unwrap();
673673
assert!(s.write(&[0]).is_ok());
674674
let _ = rx.recv();
675-
}).detach();
675+
});
676676

677677
let mut s = a.accept().unwrap();
678678
s.set_timeout(Some(20));
@@ -716,7 +716,7 @@ mod tests {
716716
}
717717
}
718718
let _ = rx.recv();
719-
}).detach();
719+
});
720720

721721
let mut s = a.accept().unwrap();
722722
s.set_read_timeout(Some(20));
@@ -739,7 +739,7 @@ mod tests {
739739
rx.recv().unwrap();
740740
assert!(s.write(&[0]).is_ok());
741741
let _ = rx.recv();
742-
}).detach();
742+
});
743743

744744
let mut s = a.accept().unwrap();
745745
s.set_write_timeout(Some(20));
@@ -766,7 +766,7 @@ mod tests {
766766
rx.recv().unwrap();
767767
assert!(s.write(&[0]).is_ok());
768768
let _ = rx.recv();
769-
}).detach();
769+
});
770770

771771
let mut s = a.accept().unwrap();
772772
let s2 = s.clone();

src/libstd/io/net/tcp.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ impl TcpStream {
146146
/// timer::sleep(Duration::seconds(1));
147147
/// let mut stream = stream2;
148148
/// stream.close_read();
149-
/// }).detach();
149+
/// });
150150
///
151151
/// // wait for some data, will get canceled after one second
152152
/// let mut buf = [0];
@@ -295,10 +295,12 @@ impl sys_common::AsInner<TcpStreamImp> for TcpStream {
295295
/// for stream in acceptor.incoming() {
296296
/// match stream {
297297
/// Err(e) => { /* connection failed */ }
298-
/// Ok(stream) => Thread::spawn(move|| {
299-
/// // connection succeeded
300-
/// handle_client(stream)
301-
/// }).detach()
298+
/// Ok(stream) => {
299+
/// Thread::spawn(move|| {
300+
/// // connection succeeded
301+
/// handle_client(stream)
302+
/// });
303+
/// }
302304
/// }
303305
/// }
304306
///
@@ -432,7 +434,7 @@ impl TcpAcceptor {
432434
/// Err(e) => panic!("unexpected error: {}", e),
433435
/// }
434436
/// }
435-
/// }).detach();
437+
/// });
436438
///
437439
/// # fn wait_for_sigint() {}
438440
/// // Now that our accept loop is running, wait for the program to be
@@ -1186,7 +1188,7 @@ mod test {
11861188
let mut a = a;
11871189
let _s = a.accept().unwrap();
11881190
let _ = rx.recv().unwrap();
1189-
}).detach();
1191+
});
11901192

11911193
let mut b = [0];
11921194
let mut s = TcpStream::connect(addr).unwrap();
@@ -1223,7 +1225,7 @@ mod test {
12231225
let mut a = a;
12241226
let _s = a.accept().unwrap();
12251227
let _ = rx.recv().unwrap();
1226-
}).detach();
1228+
});
12271229

12281230
let mut s = TcpStream::connect(addr).unwrap();
12291231
let s2 = s.clone();
@@ -1250,7 +1252,7 @@ mod test {
12501252
rx.recv().unwrap();
12511253
assert!(s.write(&[0]).is_ok());
12521254
let _ = rx.recv();
1253-
}).detach();
1255+
});
12541256

12551257
let mut s = a.accept().unwrap();
12561258
s.set_timeout(Some(20));
@@ -1289,7 +1291,7 @@ mod test {
12891291
}
12901292
}
12911293
let _ = rx.recv();
1292-
}).detach();
1294+
});
12931295

12941296
let mut s = a.accept().unwrap();
12951297
s.set_read_timeout(Some(20));
@@ -1312,7 +1314,7 @@ mod test {
13121314
rx.recv().unwrap();
13131315
assert!(s.write(&[0]).is_ok());
13141316
let _ = rx.recv();
1315-
}).detach();
1317+
});
13161318

13171319
let mut s = a.accept().unwrap();
13181320
s.set_write_timeout(Some(20));
@@ -1340,7 +1342,7 @@ mod test {
13401342
rx.recv().unwrap();
13411343
assert_eq!(s.write(&[0]), Ok(()));
13421344
let _ = rx.recv();
1343-
}).detach();
1345+
});
13441346

13451347
let mut s = a.accept().unwrap();
13461348
let s2 = s.clone();

src/libstd/io/process.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ impl Process {
720720
Thread::spawn(move |:| {
721721
let mut stream = stream;
722722
tx.send(stream.read_to_end()).unwrap();
723-
}).detach();
723+
});
724724
}
725725
None => tx.send(Ok(Vec::new())).unwrap()
726726
}

src/libstd/io/timer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ mod test {
358358

359359
Thread::spawn(move|| {
360360
let _ = timer_rx.recv();
361-
}).detach();
361+
});
362362

363363
// when we drop the TimerWatcher we're going to destroy the channel,
364364
// which must wake up the task on the other end
@@ -372,7 +372,7 @@ mod test {
372372

373373
Thread::spawn(move|| {
374374
let _ = timer_rx.recv();
375-
}).detach();
375+
});
376376

377377
timer.oneshot(Duration::milliseconds(1));
378378
}
@@ -385,7 +385,7 @@ mod test {
385385

386386
Thread::spawn(move|| {
387387
let _ = timer_rx.recv();
388-
}).detach();
388+
});
389389

390390
timer.sleep(Duration::milliseconds(1));
391391
}

src/libstd/macros.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ macro_rules! try {
303303
/// # fn long_running_task() {}
304304
/// # fn calculate_the_answer() -> int { 42i }
305305
///
306-
/// Thread::spawn(move|| { long_running_task(); tx1.send(()) }).detach();
307-
/// Thread::spawn(move|| { tx2.send(calculate_the_answer()) }).detach();
306+
/// Thread::spawn(move|| { long_running_task(); tx1.send(()).unwrap(); });
307+
/// Thread::spawn(move|| { tx2.send(calculate_the_answer()).unwrap(); });
308308
///
309309
/// select! (
310310
/// _ = rx1.recv() => println!("the long running task finished first"),

src/libstd/path/posix.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,17 +510,17 @@ mod tests {
510510
#[test]
511511
fn test_null_byte() {
512512
use thread::Thread;
513-
let result = Thread::spawn(move|| {
513+
let result = Thread::scoped(move|| {
514514
Path::new(b"foo/bar\0")
515515
}).join();
516516
assert!(result.is_err());
517517

518-
let result = Thread::spawn(move|| {
518+
let result = Thread::scoped(move|| {
519519
Path::new("test").set_filename(b"f\0o")
520520
}).join();
521521
assert!(result.is_err());
522522

523-
let result = Thread::spawn(move|| {
523+
let result = Thread::scoped(move|| {
524524
Path::new("test").push(b"f\0o");
525525
}).join();
526526
assert!(result.is_err());

0 commit comments

Comments
 (0)