diff --git a/src/doc/intro.md b/src/doc/intro.md index 1dc9f09c22011..e6d560d8122e6 100644 --- a/src/doc/intro.md +++ b/src/doc/intro.md @@ -389,6 +389,7 @@ safe concurrent programs. Here's an example of a concurrent Rust program: ```{rust} +# #![feature(scoped)] use std::thread; fn main() { @@ -421,6 +422,7 @@ problem. Let's see an example. This Rust code will not compile: ```{rust,ignore} +# #![feature(scoped)] use std::thread; fn main() { @@ -467,6 +469,7 @@ that our mutation doesn't cause a data race. Here's what using a Mutex looks like: ```{rust} +# #![feature(scoped)] use std::thread; use std::sync::Mutex; @@ -527,6 +530,7 @@ As an example, Rust's ownership system is _entirely_ at compile time. The safety check that makes this an error about moved values: ```{rust,ignore} +# #![feature(scoped)] use std::thread; fn main() { diff --git a/src/doc/trpl/concurrency.md b/src/doc/trpl/concurrency.md index f9358f28b0194..159e04e9429a0 100644 --- a/src/doc/trpl/concurrency.md +++ b/src/doc/trpl/concurrency.md @@ -56,68 +56,35 @@ place! ## Threads -Rust's standard library provides a library for 'threads', which allow you to +Rust's standard library provides a library for threads, which allow you to run Rust code in parallel. Here's a basic example of using `std::thread`: ``` use std::thread; fn main() { - thread::scoped(|| { + thread::spawn(|| { println!("Hello from a thread!"); }); } ``` -The `thread::scoped()` method accepts a closure, which is executed in a new -thread. It's called `scoped` because this thread returns a join guard: +The `thread::spawn()` method accepts a closure, which is executed in a +new thread. It returns a handle to the thread, that can be used to +wait for the child thread to finish and extract its result: ``` use std::thread; fn main() { - let guard = thread::scoped(|| { - println!("Hello from a thread!"); + let handle = thread::spawn(|| { + "Hello from a thread!" }); - // guard goes out of scope here + println!("{}", handle.join().unwrap()); } ``` -When `guard` goes out of scope, it will block execution until the thread is -finished. If we didn't want this behaviour, we could use `thread::spawn()`: - -``` -use std::thread; - -fn main() { - thread::spawn(|| { - println!("Hello from a thread!"); - }); - - thread::sleep_ms(50); -} -``` - -We need to `sleep` here because when `main()` ends, it kills all of the -running threads. - -[`scoped`](std/thread/struct.Builder.html#method.scoped) has an interesting -type signature: - -```text -fn scoped<'a, T, F>(self, f: F) -> JoinGuard<'a, T> - where T: Send + 'a, - F: FnOnce() -> T, - F: Send + 'a -``` - -Specifically, `F`, the closure that we pass to execute in the new thread. It -has two restrictions: It must be a `FnOnce` from `()` to `T`. Using `FnOnce` -allows the closure to take ownership of any data it mentions from the parent -thread. The other restriction is that `F` must be `Send`. We aren't allowed to -transfer this ownership unless the type thinks that's okay. - Many languages have the ability to execute threads, but it's wildly unsafe. There are entire books about how to prevent errors that occur from shared mutable state. Rust helps out with its type system here as well, by preventing diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index bdee53cd00964..1393c39f66c95 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -130,10 +130,10 @@ struct Output { pub fn main() { const STACK_SIZE: usize = 32000000; // 32MB - let res = std::thread::Builder::new().stack_size(STACK_SIZE).scoped(move || { + let res = std::thread::Builder::new().stack_size(STACK_SIZE).spawn(move || { let s = env::args().collect::>(); main_args(&s) - }).unwrap().join(); + }).unwrap().join().unwrap(); env::set_exit_status(res as i32); } diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 0e5fee27ffe22..90ef94bb6e15b 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -67,13 +67,33 @@ //! thread. This means that it can outlive its parent (the thread that spawned //! it), unless this parent is the main thread. //! +//! The parent thread can also wait on the completion of the child +//! thread; a call to `spawn` produces a `JoinHandle`, which provides +//! a `join` method for waiting: +//! +//! ```rust +//! use std::thread; +//! +//! let child = thread::spawn(move || { +//! // some work here +//! }); +//! // some work here +//! let res = child.join(); +//! ``` +//! +//! The `join` method returns a `Result` containing `Ok` of the final +//! value produced by the child thread, or `Err` of the value given to +//! a call to `panic!` if the child panicked. +//! //! ## Scoped threads //! -//! Often a parent thread uses a child thread to perform some particular task, -//! and at some point must wait for the child to complete before continuing. -//! For this scenario, use the `thread::scoped` function: +//! The `spawn` method does not allow the child and parent threads to +//! share any stack data, since that is not safe in general. However, +//! `scoped` makes it possible to share the parent's stack by forcing +//! a join before any relevant stack frames are popped: //! //! ```rust +//! # #![feature(scoped)] //! use std::thread; //! //! let guard = thread::scoped(move || { @@ -253,8 +273,8 @@ impl Builder { /// `io::Result` to capture any failure to create the thread at /// the OS level. #[stable(feature = "rust1", since = "1.0.0")] - pub fn spawn(self, f: F) -> io::Result where - F: FnOnce(), F: Send + 'static + pub fn spawn(self, f: F) -> io::Result> where + F: FnOnce() -> T, F: Send + 'static, T: Send + 'static { self.spawn_inner(Box::new(f)).map(|i| JoinHandle(i)) } @@ -274,7 +294,8 @@ impl Builder { /// Unlike the `scoped` free function, this method yields an /// `io::Result` to capture any failure to create the thread at /// the OS level. - #[stable(feature = "rust1", since = "1.0.0")] + #[unstable(feature = "scoped", + reason = "memory unsafe if destructor is avoided, see #24292")] pub fn scoped<'a, T, F>(self, f: F) -> io::Result> where T: Send + 'a, F: FnOnce() -> T, F: Send + 'a { @@ -370,7 +391,9 @@ impl Builder { /// Panics if the OS fails to create a thread; use `Builder::spawn` /// to recover from such errors. #[stable(feature = "rust1", since = "1.0.0")] -pub fn spawn(f: F) -> JoinHandle where F: FnOnce(), F: Send + 'static { +pub fn spawn(f: F) -> JoinHandle where + F: FnOnce() -> T, F: Send + 'static, T: Send + 'static +{ Builder::new().spawn(f).unwrap() } @@ -387,7 +410,8 @@ pub fn spawn(f: F) -> JoinHandle where F: FnOnce(), F: Send + 'static { /// /// Panics if the OS fails to create a thread; use `Builder::scoped` /// to recover from such errors. -#[stable(feature = "rust1", since = "1.0.0")] +#[unstable(feature = "scoped", + reason = "memory unsafe if destructor is avoided, see #24292")] pub fn scoped<'a, T, F>(f: F) -> JoinGuard<'a, T> where T: Send + 'a, F: FnOnce() -> T, F: Send + 'a { @@ -635,9 +659,9 @@ impl JoinInner { /// handle: the ability to join a child thread is a uniquely-owned /// permission. #[stable(feature = "rust1", since = "1.0.0")] -pub struct JoinHandle(JoinInner<()>); +pub struct JoinHandle(JoinInner); -impl JoinHandle { +impl JoinHandle { /// Extract a handle to the underlying thread #[stable(feature = "rust1", since = "1.0.0")] pub fn thread(&self) -> &Thread { @@ -649,13 +673,14 @@ impl JoinHandle { /// If the child thread panics, `Err` is returned with the parameter given /// to `panic`. #[stable(feature = "rust1", since = "1.0.0")] - pub fn join(mut self) -> Result<()> { + pub fn join(mut self) -> Result { self.0.join() } } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for JoinHandle { +#[unsafe_destructor] +impl Drop for JoinHandle { fn drop(&mut self) { if !self.0.joined { unsafe { imp::detach(self.0.native) } @@ -674,7 +699,8 @@ impl Drop for JoinHandle { /// handle: the ability to join a child thread is a uniquely-owned /// permission. #[must_use = "thread will be immediately joined if `JoinGuard` is not used"] -#[stable(feature = "rust1", since = "1.0.0")] +#[unstable(feature = "scoped", + reason = "memory unsafe if destructor is avoided, see #24292")] pub struct JoinGuard<'a, T: Send + 'a> { inner: JoinInner, _marker: PhantomData<&'a T>, @@ -706,7 +732,8 @@ impl<'a, T: Send + 'a> JoinGuard<'a, T> { } #[unsafe_destructor] -#[stable(feature = "rust1", since = "1.0.0")] +#[unstable(feature = "scoped", + reason = "memory unsafe if destructor is avoided, see #24292")] impl<'a, T: Send + 'a> Drop for JoinGuard<'a, T> { fn drop(&mut self) { if !self.inner.joined { diff --git a/src/test/bench/shootout-binarytrees.rs b/src/test/bench/shootout-binarytrees.rs index ce050cc73235d..61fe6593dc398 100644 --- a/src/test/bench/shootout-binarytrees.rs +++ b/src/test/bench/shootout-binarytrees.rs @@ -111,11 +111,11 @@ fn main() { let messages = (min_depth..max_depth + 1).step_by(2).map(|depth| { use std::num::Int; let iterations = 2.pow((max_depth - depth + min_depth) as u32); - thread::scoped(move || inner(depth, iterations)) + thread::spawn(move || inner(depth, iterations)) }).collect::>(); for message in messages { - println!("{}", message.join()); + println!("{}", message.join().unwrap()); } println!("long lived tree of depth {}\t check: {}", diff --git a/src/test/bench/shootout-fannkuch-redux.rs b/src/test/bench/shootout-fannkuch-redux.rs index 4489a124abe0f..32504350e4268 100644 --- a/src/test/bench/shootout-fannkuch-redux.rs +++ b/src/test/bench/shootout-fannkuch-redux.rs @@ -166,7 +166,7 @@ fn fannkuch(n: i32) -> (i32, i32) { for (_, j) in (0..N).zip((0..).step_by(k)) { let max = cmp::min(j+k, perm.max()); - futures.push(thread::scoped(move|| { + futures.push(thread::spawn(move|| { work(perm, j as usize, max as usize) })) } @@ -174,7 +174,7 @@ fn fannkuch(n: i32) -> (i32, i32) { let mut checksum = 0; let mut maxflips = 0; for fut in futures { - let (cs, mf) = fut.join(); + let (cs, mf) = fut.join().unwrap(); checksum += cs; maxflips = cmp::max(maxflips, mf); } diff --git a/src/test/bench/shootout-k-nucleotide.rs b/src/test/bench/shootout-k-nucleotide.rs index db131bcfdc354..07cb120ef0e29 100644 --- a/src/test/bench/shootout-k-nucleotide.rs +++ b/src/test/bench/shootout-k-nucleotide.rs @@ -307,17 +307,17 @@ fn main() { let nb_freqs: Vec<_> = (1..3).map(|i| { let input = input.clone(); - (i, thread::scoped(move|| generate_frequencies(&input, i))) + (i, thread::spawn(move|| generate_frequencies(&input, i))) }).collect(); let occ_freqs: Vec<_> = OCCURRENCES.iter().map(|&occ| { let input = input.clone(); - thread::scoped(move|| generate_frequencies(&input, occ.len())) + thread::spawn(move|| generate_frequencies(&input, occ.len())) }).collect(); for (i, freq) in nb_freqs { - print_frequencies(&freq.join(), i); + print_frequencies(&freq.join().unwrap(), i); } for (&occ, freq) in OCCURRENCES.iter().zip(occ_freqs.into_iter()) { - print_occurrences(&mut freq.join(), occ); + print_occurrences(&mut freq.join().unwrap(), occ); } } diff --git a/src/test/bench/shootout-mandelbrot.rs b/src/test/bench/shootout-mandelbrot.rs index d248293103bde..bf41838bfa7e7 100644 --- a/src/test/bench/shootout-mandelbrot.rs +++ b/src/test/bench/shootout-mandelbrot.rs @@ -82,7 +82,7 @@ fn mandelbrot(w: usize, mut out: W) -> old_io::IoResult<()> { let mut precalc_i = Vec::with_capacity(h); let precalc_futures = (0..WORKERS).map(|i| { - thread::scoped(move|| { + thread::spawn(move|| { let mut rs = Vec::with_capacity(w / WORKERS); let mut is = Vec::with_capacity(w / WORKERS); @@ -108,7 +108,7 @@ fn mandelbrot(w: usize, mut out: W) -> old_io::IoResult<()> { }).collect::>(); for res in precalc_futures { - let (rs, is) = res.join(); + let (rs, is) = res.join().unwrap(); precalc_r.extend(rs.into_iter()); precalc_i.extend(is.into_iter()); } @@ -123,7 +123,7 @@ fn mandelbrot(w: usize, mut out: W) -> old_io::IoResult<()> { let vec_init_r = arc_init_r.clone(); let vec_init_i = arc_init_i.clone(); - thread::scoped(move|| { + thread::spawn(move|| { let mut res: Vec = Vec::with_capacity((chunk_size * w) / 8); let init_r_slice = vec_init_r; @@ -144,7 +144,7 @@ fn mandelbrot(w: usize, mut out: W) -> old_io::IoResult<()> { try!(writeln!(&mut out as &mut Writer, "P4\n{} {}", w, h)); for res in data { - try!(out.write(&res.join())); + try!(out.write(&res.join().unwrap())); } out.flush() } diff --git a/src/test/bench/shootout-reverse-complement.rs b/src/test/bench/shootout-reverse-complement.rs index cda90c08f23ad..e200682acfdd9 100644 --- a/src/test/bench/shootout-reverse-complement.rs +++ b/src/test/bench/shootout-reverse-complement.rs @@ -40,7 +40,7 @@ // ignore-android see #10393 #13206 -#![feature(unboxed_closures, libc, old_io, collections, io, core)] +#![feature(unboxed_closures, libc, old_io, collections, io, core, scoped)] extern crate libc; diff --git a/src/test/bench/shootout-spectralnorm.rs b/src/test/bench/shootout-spectralnorm.rs index 5fcbe77329992..b0e8c39567323 100644 --- a/src/test/bench/shootout-spectralnorm.rs +++ b/src/test/bench/shootout-spectralnorm.rs @@ -41,7 +41,7 @@ // no-pretty-expanded FIXME #15189 #![allow(non_snake_case)] -#![feature(unboxed_closures, core, os)] +#![feature(unboxed_closures, core, os, scoped)] use std::iter::repeat; use std::thread; diff --git a/src/test/run-fail/panic-task-name-owned.rs b/src/test/run-fail/panic-task-name-owned.rs index 8cab9e05f96e8..561f141100ca7 100644 --- a/src/test/run-fail/panic-task-name-owned.rs +++ b/src/test/run-fail/panic-task-name-owned.rs @@ -13,9 +13,9 @@ use std::thread::Builder; fn main() { - let r: () = Builder::new().name("owned name".to_string()).scoped(move|| { + let r: () = Builder::new().name("owned name".to_string()).spawn(move|| { panic!("test"); () - }).unwrap().join(); + }).unwrap().join().unwrap(); panic!(); } diff --git a/src/test/run-fail/rt-set-exit-status-panic2.rs b/src/test/run-fail/rt-set-exit-status-panic2.rs index fddff3c5a9f4f..b4f0d7ceb99eb 100644 --- a/src/test/run-fail/rt-set-exit-status-panic2.rs +++ b/src/test/run-fail/rt-set-exit-status-panic2.rs @@ -37,7 +37,7 @@ fn r(x:isize) -> r { fn main() { error!("whatever"); - let _t = thread::scoped(move|| { + let _t = thread::spawn(move|| { let _i = r(5); }); panic!(); diff --git a/src/test/run-pass/atomic-print.rs b/src/test/run-pass/atomic-print.rs index df3b572bce49b..ae0a358ac4e3f 100644 --- a/src/test/run-pass/atomic-print.rs +++ b/src/test/run-pass/atomic-print.rs @@ -27,7 +27,7 @@ fn main(){ if env::args().count() == 2 { let barrier = sync::Arc::new(sync::Barrier::new(2)); let tbarrier = barrier.clone(); - let t = thread::scoped(||{ + let t = thread::spawn(move || { tbarrier.wait(); do_print(1); }); diff --git a/src/test/run-pass/capturing-logging.rs b/src/test/run-pass/capturing-logging.rs index f9b429a935a6a..f7e8edca4405a 100644 --- a/src/test/run-pass/capturing-logging.rs +++ b/src/test/run-pass/capturing-logging.rs @@ -36,7 +36,7 @@ impl Logger for MyWriter { fn main() { let (tx, rx) = channel(); let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { set_logger(box MyWriter(w) as Box); debug!("debug"); info!("info"); @@ -44,4 +44,5 @@ fn main() { let s = r.read_to_string().unwrap(); assert!(s.contains("info")); assert!(!s.contains("debug")); + t.join(); } diff --git a/src/test/run-pass/clone-with-exterior.rs b/src/test/run-pass/clone-with-exterior.rs index 16efceb9d7e0e..ef2796243c807 100644 --- a/src/test/run-pass/clone-with-exterior.rs +++ b/src/test/run-pass/clone-with-exterior.rs @@ -23,8 +23,8 @@ struct Pair { pub fn main() { let z: Box<_> = box Pair { a : 10, b : 12}; - let _t = thread::scoped(move|| { + thread::spawn(move|| { assert_eq!(z.a, 10); assert_eq!(z.b, 12); - }); + }).join(); } diff --git a/src/test/run-pass/comm.rs b/src/test/run-pass/comm.rs index 859599596ae7b..72f623ccfde7a 100644 --- a/src/test/run-pass/comm.rs +++ b/src/test/run-pass/comm.rs @@ -15,11 +15,12 @@ use std::sync::mpsc::{channel, Sender}; pub fn main() { let (tx, rx) = channel(); - let _t = thread::scoped(move|| { child(&tx) }); + let t = thread::spawn(move|| { child(&tx) }); let y = rx.recv().unwrap(); println!("received"); println!("{}", y); assert_eq!(y, 10); + t.join(); } fn child(c: &Sender) { diff --git a/src/test/run-pass/extern-call-deep2.rs b/src/test/run-pass/extern-call-deep2.rs index 198745f5b19fe..b35095171ece0 100644 --- a/src/test/run-pass/extern-call-deep2.rs +++ b/src/test/run-pass/extern-call-deep2.rs @@ -42,7 +42,7 @@ fn count(n: libc::uintptr_t) -> libc::uintptr_t { pub fn main() { // Make sure we're on a task with small Rust stacks (main currently // has a large stack) - thread::scoped(move|| { + thread::spawn(move|| { let result = count(1000); println!("result = {}", result); assert_eq!(result, 1000); diff --git a/src/test/run-pass/extern-call-scrub.rs b/src/test/run-pass/extern-call-scrub.rs index e8c9bc76335f7..3993868068197 100644 --- a/src/test/run-pass/extern-call-scrub.rs +++ b/src/test/run-pass/extern-call-scrub.rs @@ -46,9 +46,9 @@ fn count(n: libc::uintptr_t) -> libc::uintptr_t { pub fn main() { // Make sure we're on a task with small Rust stacks (main currently // has a large stack) - let _t = thread::scoped(move|| { + thread::spawn(move|| { let result = count(12); println!("result = {}", result); assert_eq!(result, 2048); - }); + }).join(); } diff --git a/src/test/run-pass/fds-are-cloexec.rs b/src/test/run-pass/fds-are-cloexec.rs index cbf7830513ac9..3be47e8430d59 100644 --- a/src/test/run-pass/fds-are-cloexec.rs +++ b/src/test/run-pass/fds-are-cloexec.rs @@ -34,14 +34,12 @@ fn main() { fn parent() { let file = File::open("Makefile").unwrap(); - let _dir = fs::read_dir("/").unwrap(); let tcp1 = TcpListener::bind("127.0.0.1:0").unwrap(); - assert_eq!(tcp1.as_raw_fd(), file.as_raw_fd() + 2); let tcp2 = tcp1.try_clone().unwrap(); let addr = tcp1.local_addr().unwrap(); - let t = thread::scoped(|| TcpStream::connect(addr).unwrap()); + let t = thread::spawn(move || TcpStream::connect(addr).unwrap()); let tcp3 = tcp1.accept().unwrap().0; - let tcp4 = t.join(); + let tcp4 = t.join().unwrap(); let tcp5 = tcp3.try_clone().unwrap(); let tcp6 = tcp4.try_clone().unwrap(); let udp1 = UdpSocket::bind("127.0.0.1:0").unwrap(); @@ -49,7 +47,6 @@ fn parent() { let status = Command::new(env::args().next().unwrap()) .arg(file.as_raw_fd().to_string()) - .arg((file.as_raw_fd() + 1).to_string()) .arg(tcp1.as_raw_fd().to_string()) .arg(tcp2.as_raw_fd().to_string()) .arg(tcp3.as_raw_fd().to_string()) diff --git a/src/test/run-pass/init-large-type.rs b/src/test/run-pass/init-large-type.rs index 26d58d34b9dea..dafa8ee10339e 100644 --- a/src/test/run-pass/init-large-type.rs +++ b/src/test/run-pass/init-large-type.rs @@ -26,7 +26,7 @@ const SIZE: usize = 1024 * 1024; fn main() { // do the test in a new thread to avoid (spurious?) stack overflows - let _ = thread::scoped(|| { + thread::spawn(|| { let _memory: [u8; SIZE] = unsafe { init() }; }).join(); } diff --git a/src/test/run-pass/issue-13494.rs b/src/test/run-pass/issue-13494.rs index d1b1647de7881..71897ea68c209 100644 --- a/src/test/run-pass/issue-13494.rs +++ b/src/test/run-pass/issue-13494.rs @@ -26,7 +26,7 @@ fn helper(rx: Receiver>) { fn main() { let (tx, rx) = channel(); - let _t = thread::scoped(move|| { helper(rx) }); + let t = thread::spawn(move|| { helper(rx) }); let (snd, rcv) = channel::(); for _ in 1..100000 { snd.send(1).unwrap(); @@ -38,4 +38,5 @@ fn main() { } } drop(tx); + t.join(); } diff --git a/src/test/run-pass/issue-20454.rs b/src/test/run-pass/issue-20454.rs index d527d9519cfbc..522f544a21c10 100644 --- a/src/test/run-pass/issue-20454.rs +++ b/src/test/run-pass/issue-20454.rs @@ -13,11 +13,11 @@ use std::thread; fn _foo() { - let _t = thread::scoped(move || { // no need for -> () + thread::spawn(move || { // no need for -> () loop { println!("hello"); } - }); + }).join(); } fn main() {} diff --git a/src/test/run-pass/issue-3609.rs b/src/test/run-pass/issue-3609.rs index 2167a3df9766f..61de3c6385e7b 100644 --- a/src/test/run-pass/issue-3609.rs +++ b/src/test/run-pass/issue-3609.rs @@ -23,7 +23,7 @@ enum Msg } fn foo(name: String, samples_chan: Sender) { - let _t = thread::scoped(move|| { + thread::spawn(move|| { let mut samples_chan = samples_chan; // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. @@ -34,7 +34,7 @@ fn foo(name: String, samples_chan: Sender) { }); samples_chan.send(Msg::GetSamples(name.clone(), callback)); - }); + }).join(); } pub fn main() {} diff --git a/src/test/run-pass/issue-9396.rs b/src/test/run-pass/issue-9396.rs index bfaf060e43c30..6845d3b3aec5b 100644 --- a/src/test/run-pass/issue-9396.rs +++ b/src/test/run-pass/issue-9396.rs @@ -19,7 +19,7 @@ use std::time::Duration; pub fn main() { let (tx, rx) = channel(); - let _t = thread::scoped(move||{ + let t = thread::spawn(move||{ let mut timer = Timer::new().unwrap(); timer.sleep(Duration::milliseconds(10)); tx.send(()).unwrap(); @@ -31,4 +31,5 @@ pub fn main() { Err(TryRecvError::Disconnected) => unreachable!() } } + t.join(); } diff --git a/src/test/run-pass/ivec-tag.rs b/src/test/run-pass/ivec-tag.rs index 8ae084dce8c37..3f0daf2610ce9 100644 --- a/src/test/run-pass/ivec-tag.rs +++ b/src/test/run-pass/ivec-tag.rs @@ -23,9 +23,10 @@ fn producer(tx: &Sender>) { pub fn main() { let (tx, rx) = channel::>(); - let _prod = thread::scoped(move|| { + let prod = thread::spawn(move|| { producer(&tx) }); let _data: Vec = rx.recv().unwrap(); + prod.join(); } diff --git a/src/test/run-pass/kindck-implicit-close-over-mut-var.rs b/src/test/run-pass/kindck-implicit-close-over-mut-var.rs index 11b1d70137def..a81c0846a2794 100644 --- a/src/test/run-pass/kindck-implicit-close-over-mut-var.rs +++ b/src/test/run-pass/kindck-implicit-close-over-mut-var.rs @@ -18,12 +18,13 @@ fn foo() { // Here, i is *copied* into the proc (heap closure). // Requires allocation. The proc's copy is not mutable. let mut i = 0; - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { user(i); println!("spawned {}", i) }); i += 1; - println!("original {}", i) + println!("original {}", i); + t.join(); } fn bar() { @@ -31,10 +32,11 @@ fn bar() { // mutable outside of the proc. let mut i = 0; while i < 10 { - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { user(i); }); i += 1; + t.join(); } } @@ -42,12 +44,13 @@ fn car() { // Here, i must be shadowed in the proc to be mutable. let mut i = 0; while i < 10 { - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { let mut i = i; i += 1; user(i); }); i += 1; + t.join(); } } diff --git a/src/test/run-pass/moves-based-on-type-capture-clause.rs b/src/test/run-pass/moves-based-on-type-capture-clause.rs index b6509d280366a..c7ef9776367bd 100644 --- a/src/test/run-pass/moves-based-on-type-capture-clause.rs +++ b/src/test/run-pass/moves-based-on-type-capture-clause.rs @@ -14,7 +14,7 @@ use std::thread; pub fn main() { let x = "Hello world!".to_string(); - let _t = thread::scoped(move|| { + thread::spawn(move|| { println!("{}", x); - }); + }).join(); } diff --git a/src/test/run-pass/out-of-stack-new-thread-no-split.rs b/src/test/run-pass/out-of-stack-new-thread-no-split.rs index f08ed6e7f9cdc..6e4901b82d729 100644 --- a/src/test/run-pass/out-of-stack-new-thread-no-split.rs +++ b/src/test/run-pass/out-of-stack-new-thread-no-split.rs @@ -37,7 +37,7 @@ fn recurse() { fn main() { let args: Vec = env::args().collect(); if args.len() > 1 && args[1] == "recurse" { - let _t = thread::scoped(recurse); + thread::spawn(recurse).join(); } else { let recurse = Command::new(&args[0]).arg("recurse").output().unwrap(); assert!(!recurse.status.success()); diff --git a/src/test/run-pass/rust-log-filter.rs b/src/test/run-pass/rust-log-filter.rs index 660b1e2036d33..c65d8dd3d1326 100644 --- a/src/test/run-pass/rust-log-filter.rs +++ b/src/test/run-pass/rust-log-filter.rs @@ -41,7 +41,7 @@ impl log::Logger for ChannelLogger { pub fn main() { let (logger, rx) = ChannelLogger::new(); - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { log::set_logger(logger); info!("foo"); @@ -54,4 +54,6 @@ pub fn main() { assert_eq!(rx.recv().unwrap(), "foo bar"); assert_eq!(rx.recv().unwrap(), "bar foo"); assert!(rx.recv().is_err()); + + t.join(); } diff --git a/src/test/run-pass/send-is-not-static-par-for.rs b/src/test/run-pass/send-is-not-static-par-for.rs index 99ae3b7c7d87c..5f0902d34d3e8 100644 --- a/src/test/run-pass/send-is-not-static-par-for.rs +++ b/src/test/run-pass/send-is-not-static-par-for.rs @@ -10,7 +10,7 @@ // pretty-expanded FIXME #23616 -#![feature(core, std_misc)] +#![feature(core, std_misc, scoped)] use std::thread; use std::sync::Mutex; @@ -25,7 +25,6 @@ fn par_for(iter: I, f: F) f(elem) }) }).collect(); - } fn sum(x: &[i32]) { diff --git a/src/test/run-pass/send-resource.rs b/src/test/run-pass/send-resource.rs index 3f64b2adb6395..66878d98c840c 100644 --- a/src/test/run-pass/send-resource.rs +++ b/src/test/run-pass/send-resource.rs @@ -32,7 +32,7 @@ fn test(f: isize) -> test { pub fn main() { let (tx, rx) = channel(); - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { let (tx2, rx2) = channel(); tx.send(tx2).unwrap(); @@ -40,4 +40,6 @@ pub fn main() { }); rx.recv().unwrap().send(test(42)).unwrap(); + + t.join(); } diff --git a/src/test/run-pass/spawn-fn.rs b/src/test/run-pass/spawn-fn.rs index efddf0455cd5f..4a35ed609e080 100644 --- a/src/test/run-pass/spawn-fn.rs +++ b/src/test/run-pass/spawn-fn.rs @@ -16,13 +16,16 @@ fn x(s: String, n: isize) { } pub fn main() { - let _t = thread::scoped(|| x("hello from first spawned fn".to_string(), 65) ); - let _t = thread::scoped(|| x("hello from second spawned fn".to_string(), 66) ); - let _t = thread::scoped(|| x("hello from third spawned fn".to_string(), 67) ); + let t1 = thread::spawn(|| x("hello from first spawned fn".to_string(), 65) ); + let t2 = thread::spawn(|| x("hello from second spawned fn".to_string(), 66) ); + let t3 = thread::spawn(|| x("hello from third spawned fn".to_string(), 67) ); let mut i = 30; while i > 0 { i = i - 1; println!("parent sleeping"); thread::yield_now(); } + t1.join(); + t2.join(); + t3.join(); } diff --git a/src/test/run-pass/task-comm-0.rs b/src/test/run-pass/task-comm-0.rs index 786dd2c76123e..1409caf9c702d 100644 --- a/src/test/run-pass/task-comm-0.rs +++ b/src/test/run-pass/task-comm-0.rs @@ -26,7 +26,7 @@ fn test05_start(tx : &Sender) { fn test05() { let (tx, rx) = channel(); - let _t = thread::scoped(move|| { test05_start(&tx) }); + let t = thread::spawn(move|| { test05_start(&tx) }); let mut value: isize = rx.recv().unwrap(); println!("{}", value); value = rx.recv().unwrap(); @@ -34,4 +34,5 @@ fn test05() { value = rx.recv().unwrap(); println!("{}", value); assert_eq!(value, 30); + t.join(); } diff --git a/src/test/run-pass/task-comm-1.rs b/src/test/run-pass/task-comm-1.rs index 9c3466f162b03..b3327d82c3ebe 100644 --- a/src/test/run-pass/task-comm-1.rs +++ b/src/test/run-pass/task-comm-1.rs @@ -17,6 +17,6 @@ pub fn main() { test00(); } fn start() { println!("Started / Finished task."); } fn test00() { - let _ = thread::scoped(move|| start() ).join(); + thread::spawn(move|| start() ).join(); println!("Completing."); } diff --git a/src/test/run-pass/task-comm-10.rs b/src/test/run-pass/task-comm-10.rs index f25bb3ff71ae8..a796750ef88b7 100644 --- a/src/test/run-pass/task-comm-10.rs +++ b/src/test/run-pass/task-comm-10.rs @@ -29,10 +29,12 @@ fn start(tx: &Sender>) { pub fn main() { let (tx, rx) = channel(); - let _child = thread::scoped(move|| { start(&tx) }); + let child = thread::spawn(move|| { start(&tx) }); let mut c = rx.recv().unwrap(); c.send("A".to_string()).unwrap(); c.send("B".to_string()).unwrap(); thread::yield_now(); + + child.join(); } diff --git a/src/test/run-pass/task-comm-11.rs b/src/test/run-pass/task-comm-11.rs index ec9ed53c1dca0..7af8f5d3b3575 100644 --- a/src/test/run-pass/task-comm-11.rs +++ b/src/test/run-pass/task-comm-11.rs @@ -22,8 +22,9 @@ fn start(tx: &Sender>) { pub fn main() { let (tx, rx) = channel(); - let _child = thread::scoped(move|| { + let child = thread::spawn(move|| { start(&tx) }); let _tx = rx.recv().unwrap(); + child.join(); } diff --git a/src/test/run-pass/task-comm-12.rs b/src/test/run-pass/task-comm-12.rs index 03305091a2d07..f8d608d31689c 100644 --- a/src/test/run-pass/task-comm-12.rs +++ b/src/test/run-pass/task-comm-12.rs @@ -18,7 +18,7 @@ fn start(_task_number: isize) { println!("Started / Finished task."); } fn test00() { let i: isize = 0; - let mut result = thread::scoped(move|| { + let mut result = thread::spawn(move|| { start(i) }); diff --git a/src/test/run-pass/task-comm-13.rs b/src/test/run-pass/task-comm-13.rs index 15ceacd672f8b..156ddd9c77fc3 100644 --- a/src/test/run-pass/task-comm-13.rs +++ b/src/test/run-pass/task-comm-13.rs @@ -21,6 +21,6 @@ fn start(tx: &Sender, start: isize, number_of_messages: isize) { pub fn main() { println!("Check that we don't deadlock."); let (tx, rx) = channel(); - let _t = thread::scoped(move|| { start(&tx, 0, 10) }).join(); + let _ = thread::spawn(move|| { start(&tx, 0, 10) }).join(); println!("Joined task"); } diff --git a/src/test/run-pass/task-comm-14.rs b/src/test/run-pass/task-comm-14.rs index 1e2d9fe52df15..0048d7d2d7321 100644 --- a/src/test/run-pass/task-comm-14.rs +++ b/src/test/run-pass/task-comm-14.rs @@ -21,7 +21,7 @@ pub fn main() { while (i > 0) { println!("{}", i); let tx = tx.clone(); - thread::scoped({let i = i; move|| { child(i, &tx) }}); + thread::spawn({let i = i; move|| { child(i, &tx) }}); i = i - 1; } diff --git a/src/test/run-pass/task-comm-15.rs b/src/test/run-pass/task-comm-15.rs index 2663595aecfa1..1d853b3e67ffc 100644 --- a/src/test/run-pass/task-comm-15.rs +++ b/src/test/run-pass/task-comm-15.rs @@ -29,8 +29,9 @@ pub fn main() { // the child's point of view the receiver may die. We should // drop messages on the floor in this case, and not crash! let (tx, rx) = channel(); - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { start(&tx, 10) }); rx.recv(); + t.join(); } diff --git a/src/test/run-pass/task-comm-17.rs b/src/test/run-pass/task-comm-17.rs index de334c77aa310..8f6f971ce35c8 100644 --- a/src/test/run-pass/task-comm-17.rs +++ b/src/test/run-pass/task-comm-17.rs @@ -22,5 +22,5 @@ fn f() { } pub fn main() { - let _t = thread::scoped(move|| f() ).join(); + thread::spawn(move|| f() ).join(); } diff --git a/src/test/run-pass/task-comm-3.rs b/src/test/run-pass/task-comm-3.rs index 254ad653c4813..25f40757b7b8a 100644 --- a/src/test/run-pass/task-comm-3.rs +++ b/src/test/run-pass/task-comm-3.rs @@ -42,7 +42,7 @@ fn test00() { let mut results = Vec::new(); while i < number_of_tasks { let tx = tx.clone(); - results.push(thread::scoped({ + results.push(thread::spawn({ let i = i; move|| { test00_start(&tx, i, number_of_messages) diff --git a/src/test/run-pass/task-comm-7.rs b/src/test/run-pass/task-comm-7.rs index b05e36552a24c..aa73925874fa0 100644 --- a/src/test/run-pass/task-comm-7.rs +++ b/src/test/run-pass/task-comm-7.rs @@ -31,19 +31,19 @@ fn test00() { let number_of_messages: isize = 10; let tx2 = tx.clone(); - let _t = thread::scoped(move|| { + let t1 = thread::spawn(move|| { test00_start(&tx2, number_of_messages * 0, number_of_messages); }); let tx2 = tx.clone(); - let _t = thread::scoped(move|| { + let t2 = thread::spawn(move|| { test00_start(&tx2, number_of_messages * 1, number_of_messages); }); let tx2 = tx.clone(); - let _t = thread::scoped(move|| { + let t3 = thread::spawn(move|| { test00_start(&tx2, number_of_messages * 2, number_of_messages); }); let tx2 = tx.clone(); - let _t = thread::scoped(move|| { + let t4 = thread::spawn(move|| { test00_start(&tx2, number_of_messages * 3, number_of_messages); }); @@ -61,4 +61,9 @@ fn test00() { } assert_eq!(sum, number_of_messages * 4 * (number_of_messages * 4 - 1) / 2); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); } diff --git a/src/test/run-pass/task-comm-9.rs b/src/test/run-pass/task-comm-9.rs index 758764aa9fdcc..d8eec4169e353 100644 --- a/src/test/run-pass/task-comm-9.rs +++ b/src/test/run-pass/task-comm-9.rs @@ -26,7 +26,7 @@ fn test00() { let (tx, rx) = channel(); let number_of_messages: isize = 10; - let result = thread::scoped(move|| { + let result = thread::spawn(move|| { test00_start(&tx, number_of_messages); }); diff --git a/src/test/run-pass/task-life-0.rs b/src/test/run-pass/task-life-0.rs index b97f4355b3ee0..ba8819fd0b0fe 100644 --- a/src/test/run-pass/task-life-0.rs +++ b/src/test/run-pass/task-life-0.rs @@ -15,7 +15,7 @@ use std::thread; pub fn main() { - let _t = thread::scoped(move|| child("Hello".to_string()) ); + thread::spawn(move|| child("Hello".to_string()) ).join(); } fn child(_s: String) { diff --git a/src/test/run-pass/task-spawn-move-and-copy.rs b/src/test/run-pass/task-spawn-move-and-copy.rs index aa7b61bf112f9..548f876421b6a 100644 --- a/src/test/run-pass/task-spawn-move-and-copy.rs +++ b/src/test/run-pass/task-spawn-move-and-copy.rs @@ -22,11 +22,13 @@ pub fn main() { let x: Box = box 1; let x_in_parent = &(*x) as *const isize as usize; - let _t = thread::scoped(move || { + let t = thread::spawn(move || { let x_in_child = &(*x) as *const isize as usize; tx.send(x_in_child).unwrap(); }); let x_in_child = rx.recv().unwrap(); assert_eq!(x_in_parent, x_in_child); + + t.join(); } diff --git a/src/test/run-pass/tcp-accept-stress.rs b/src/test/run-pass/tcp-accept-stress.rs index 00467e563347a..808bec4e8f234 100644 --- a/src/test/run-pass/tcp-accept-stress.rs +++ b/src/test/run-pass/tcp-accept-stress.rs @@ -36,11 +36,11 @@ fn test() { let (srv_tx, srv_rx) = channel(); let (cli_tx, cli_rx) = channel(); - let _t = (0..N).map(|_| { + let ts1 = (0..N).map(|_| { let a = a.clone(); let cnt = cnt.clone(); let srv_tx = srv_tx.clone(); - thread::scoped(move|| { + thread::spawn(move|| { let mut a = a; loop { match a.accept() { @@ -57,7 +57,7 @@ fn test() { }) }).collect::>(); - let _t = (0..N).map(|_| { + let ts2 = (0..N).map(|_| { let cli_tx = cli_tx.clone(); thread::scoped(move|| { for _ in 0..M { @@ -85,4 +85,7 @@ fn test() { // Everything should have been accepted. assert_eq!(cnt.load(Ordering::SeqCst), N * M); + + for t in ts1 { t.join(); } + for t in ts2 { t.join(); } } diff --git a/src/test/run-pass/tcp-connect-timeouts.rs b/src/test/run-pass/tcp-connect-timeouts.rs index 64f07a60b3503..c31400a832c74 100644 --- a/src/test/run-pass/tcp-connect-timeouts.rs +++ b/src/test/run-pass/tcp-connect-timeouts.rs @@ -34,7 +34,7 @@ fn eventual_timeout() { let (tx1, rx1) = channel(); let (_tx2, rx2) = channel::<()>(); - let _t = thread::scoped(move|| { + let t = thread::spawn(move|| { let _l = TcpListener::bind(addr).unwrap().listen(); tx1.send(()).unwrap(); let _ = rx2.recv(); @@ -50,6 +50,7 @@ fn eventual_timeout() { } } panic!("never timed out!"); + t.join(); } fn timeout_success() { diff --git a/src/test/run-pass/tempfile.rs b/src/test/run-pass/tempfile.rs index 49fac24d0b3a5..3f99c338c0e33 100644 --- a/src/test/run-pass/tempfile.rs +++ b/src/test/run-pass/tempfile.rs @@ -64,7 +64,7 @@ fn test_rm_tempdir() { TempDir::new("test_rm_tempdir").unwrap() }; // FIXME(#16640) `: TempDir` annotation shouldn't be necessary - let tmp: TempDir = thread::scoped(f).join(); + let tmp: TempDir = thread::spawn(f).join().unwrap(); path = tmp.path().clone(); assert!(path.exists()); } @@ -108,7 +108,7 @@ fn test_rm_tempdir_close() { TempDir::new("test_rm_tempdir").unwrap() }; // FIXME(#16640) `: TempDir` annotation shouldn't be necessary - let tmp: TempDir = thread::scoped(f).join(); + let tmp: TempDir = thread::spawn(f).join().unwrap(); path = tmp.path().clone(); assert!(path.exists()); tmp.close(); diff --git a/src/test/run-pass/threads.rs b/src/test/run-pass/threads.rs index 969a42a6f8715..184338c3294ff 100644 --- a/src/test/run-pass/threads.rs +++ b/src/test/run-pass/threads.rs @@ -15,7 +15,7 @@ use std::thread; pub fn main() { let mut i = 10; while i > 0 { - thread::scoped({let i = i; move|| child(i)}); + thread::spawn({let i = i; move|| child(i)}).join(); i = i - 1; } println!("main thread exiting"); diff --git a/src/test/run-pass/trait-bounds-in-arc.rs b/src/test/run-pass/trait-bounds-in-arc.rs index 02ea703705605..21205a2d7fa59 100644 --- a/src/test/run-pass/trait-bounds-in-arc.rs +++ b/src/test/run-pass/trait-bounds-in-arc.rs @@ -83,16 +83,19 @@ pub fn main() { box dogge2 as Box)); let (tx1, rx1) = channel(); let arc1 = arc.clone(); - let _t1 = thread::scoped(move|| { check_legs(arc1); tx1.send(()); }); + let t1 = thread::spawn(move|| { check_legs(arc1); tx1.send(()); }); let (tx2, rx2) = channel(); let arc2 = arc.clone(); - let _t2 = thread::scoped(move|| { check_names(arc2); tx2.send(()); }); + let t2 = thread::spawn(move|| { check_names(arc2); tx2.send(()); }); let (tx3, rx3) = channel(); let arc3 = arc.clone(); - let _t3 = thread::scoped(move|| { check_pedigree(arc3); tx3.send(()); }); + let t3 = thread::spawn(move|| { check_pedigree(arc3); tx3.send(()); }); rx1.recv(); rx2.recv(); rx3.recv(); + t1.join(); + t2.join(); + t3.join(); } fn check_legs(arc: Arc>>) { diff --git a/src/test/run-pass/unique-send-2.rs b/src/test/run-pass/unique-send-2.rs index d80d0e82f4fe9..c32483f629e03 100644 --- a/src/test/run-pass/unique-send-2.rs +++ b/src/test/run-pass/unique-send-2.rs @@ -23,10 +23,10 @@ pub fn main() { let (tx, rx) = channel(); let n = 100; let mut expected = 0; - let _t = (0..n).map(|i| { + let ts = (0..n).map(|i| { expected += i; let tx = tx.clone(); - thread::scoped(move|| { + thread::spawn(move|| { child(&tx, i) }) }).collect::>(); @@ -38,4 +38,6 @@ pub fn main() { } assert_eq!(expected, actual); + + for t in ts { t.join(); } }