diff options
51 files changed, 537 insertions, 131 deletions
diff --git a/src/doc/intro.md b/src/doc/intro.md index 1dc9f09c220..e6d560d8122 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 f9358f28b01..159e04e9429 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 bdee53cd009..1393c39f66c 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::<Vec<_>>(); 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 a073f669d26..4fd0340f09a 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<F>(self, f: F) -> io::Result<JoinHandle> where - F: FnOnce(), F: Send + 'static + pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>> 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<JoinGuard<'a, T>> 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: F) -> JoinHandle where F: FnOnce(), F: Send + 'static { +pub fn spawn<F, T>(f: F) -> JoinHandle<T> where + F: FnOnce() -> T, F: Send + 'static, T: Send + 'static +{ Builder::new().spawn(f).unwrap() } @@ -387,7 +410,8 @@ pub fn spawn<F>(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<T> JoinInner<T> { /// 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<T>(JoinInner<T>); -impl JoinHandle { +impl<T> JoinHandle<T> { /// Extracts 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<T> { self.0.join() } } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for JoinHandle { +#[unsafe_destructor] +impl<T> Drop for JoinHandle<T> { 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<T>, _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 ce050cc7323..61fe6593dc3 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::<Vec<_>>(); 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 4489a124abe..32504350e42 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 db131bcfdc3..07cb120ef0e 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 93a0a461746..f2714d55e5e 100644 --- a/src/test/bench/shootout-mandelbrot.rs +++ b/src/test/bench/shootout-mandelbrot.rs @@ -81,7 +81,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> { 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); @@ -107,7 +107,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> { }).collect::<Vec<_>>(); 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()); } @@ -122,7 +122,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> { 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<u8> = Vec::with_capacity((chunk_size * w) / 8); let init_r_slice = vec_init_r; @@ -143,7 +143,7 @@ fn mandelbrot<W: Write>(w: usize, mut out: W) -> io::Result<()> { try!(writeln!(&mut out, "P4\n{} {}", w, h)); for res in data { - try!(out.write_all(&res.join())); + try!(out.write_all(&res.join().unwrap())); } out.flush() } diff --git a/src/test/bench/shootout-reverse-complement.rs b/src/test/bench/shootout-reverse-complement.rs index 96ac1e064bf..7c9f33678a3 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(libc)] +#![feature(libc, scoped)] extern crate libc; diff --git a/src/test/bench/shootout-spectralnorm.rs b/src/test/bench/shootout-spectralnorm.rs index 5fcbe773299..b0e8c395673 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 8cab9e05f96..561f141100c 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 fddff3c5a9f..b4f0d7ceb99 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 df3b572bce4..ae0a358ac4e 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/clone-with-exterior.rs b/src/test/run-pass/clone-with-exterior.rs index 352733601f2..5a7b1c83dfd 100644 --- a/src/test/run-pass/clone-with-exterior.rs +++ b/src/test/run-pass/clone-with-exterior.rs @@ -22,8 +22,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 859599596ae..72f623ccfde 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<isize>) { diff --git a/src/test/run-pass/extern-call-deep2.rs b/src/test/run-pass/extern-call-deep2.rs index 198745f5b19..b35095171ec 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 e8c9bc76335..39938680681 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 cbf7830513a..3be47e8430d 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 26d58d34b9d..dafa8ee1033 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 d1b1647de78..71897ea68c2 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<Sender<()>>) { fn main() { let (tx, rx) = channel(); - let _t = thread::scoped(move|| { helper(rx) }); + let t = thread::spawn(move|| { helper(rx) }); let (snd, rcv) = channel::<isize>(); 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 d527d9519cf..522f544a21c 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 2167a3df976..61de3c6385e 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<Msg>) { - 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<Msg>) { }); 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 394fd84781d..ed67630bcac 100644 --- a/src/test/run-pass/issue-9396.rs +++ b/src/test/run-pass/issue-9396.rs @@ -13,7 +13,7 @@ use std::thread; pub fn main() { let (tx, rx) = channel(); - let _t = thread::scoped(move||{ + let t = thread::spawn(move||{ thread::sleep_ms(10); tx.send(()).unwrap(); }); @@ -24,4 +24,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 8ae084dce8c..3f0daf2610c 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<Vec<u8>>) { pub fn main() { let (tx, rx) = channel::<Vec<u8>>(); - let _prod = thread::scoped(move|| { + let prod = thread::spawn(move|| { producer(&tx) }); let _data: Vec<u8> = 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 11b1d70137d..a81c0846a27 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 b6509d28036..c7ef9776367 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 d321d9142ca..0d0a5bee8a4 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<String> = 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 3517e4a29b8..59179206104 100644 --- a/src/test/run-pass/rust-log-filter.rs +++ b/src/test/run-pass/rust-log-filter.rs @@ -40,7 +40,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"); @@ -53,4 +53,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 99ae3b7c7d8..5f0902d34d3 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<I, F>(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 3f64b2adb63..66878d98c84 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 efddf0455cd..4a35ed609e0 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 786dd2c7612..1409caf9c70 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<isize>) { 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 9c3466f162b..b3327d82c3e 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 f25bb3ff71a..a796750ef88 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<Sender<String>>) { 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 ec9ed53c1dc..7af8f5d3b35 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<Sender<isize>>) { 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 03305091a2d..f8d608d3168 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 15ceacd672f..156ddd9c77f 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<isize>, 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 1e2d9fe52df..0048d7d2d73 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 2663595aecf..1d853b3e67f 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 de334c77aa3..8f6f971ce35 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 254ad653c48..25f40757b7b 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 a5282b1097e..e37160f979c 100644 --- a/src/test/run-pass/task-comm-7.rs +++ b/src/test/run-pass/task-comm-7.rs @@ -30,19 +30,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); }); @@ -60,4 +60,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 758764aa9fd..d8eec4169e3 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 b97f4355b3e..ba8819fd0b0 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 3f18c0c7464..6a84ec47c93 100644 --- a/src/test/run-pass/task-spawn-move-and-copy.rs +++ b/src/test/run-pass/task-spawn-move-and-copy.rs @@ -21,11 +21,13 @@ pub fn main() { let x: Box<isize> = 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 new file mode 100644 index 00000000000..3347287748e --- /dev/null +++ b/src/test/run-pass/tcp-accept-stress.rs @@ -0,0 +1,91 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-macos osx really doesn't like cycling through large numbers of +// sockets as calls to connect() will start returning EADDRNOTAVAIL +// quite quickly and it takes a few seconds for the sockets to get +// recycled. + +#![feature(old_io, io, std_misc)] + +use std::old_io::{TcpListener, Listener, Acceptor, EndOfFile, TcpStream}; +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::thread; + +static N: usize = 8; +static M: usize = 20; + +fn main() { + test(); +} + +fn test() { + let mut l = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = l.socket_name().unwrap(); + let mut a = l.listen().unwrap(); + let cnt = Arc::new(AtomicUsize::new(0)); + + let (srv_tx, srv_rx) = channel(); + let (cli_tx, cli_rx) = channel(); + let ts1 = (0..N).map(|_| { + let a = a.clone(); + let cnt = cnt.clone(); + let srv_tx = srv_tx.clone(); + thread::spawn(move|| { + let mut a = a; + loop { + match a.accept() { + Ok(..) => { + if cnt.fetch_add(1, Ordering::SeqCst) == N * M - 1 { + break + } + } + Err(ref e) if e.kind == EndOfFile => break, + Err(e) => panic!("{}", e), + } + } + srv_tx.send(()); + }) + }).collect::<Vec<_>>(); + + let ts2 = (0..N).map(|_| { + let cli_tx = cli_tx.clone(); + thread::scoped(move|| { + for _ in 0..M { + let _s = TcpStream::connect(addr).unwrap(); + } + cli_tx.send(()); + }) + }).collect::<Vec<_>>(); + drop((cli_tx, srv_tx)); + + // wait for senders + if cli_rx.iter().take(N).count() != N { + a.close_accept().unwrap(); + panic!("clients panicked"); + } + + // wait for one acceptor to die + let _ = srv_rx.recv(); + + // Notify other receivers should die + a.close_accept().unwrap(); + + // wait for receivers + assert_eq!(srv_rx.iter().take(N - 1).count(), N - 1); + + // 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 new file mode 100644 index 00000000000..c31400a832c --- /dev/null +++ b/src/test/run-pass/tcp-connect-timeouts.rs @@ -0,0 +1,77 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty +// compile-flags:--test +// exec-env:RUST_TEST_THREADS=1 + +// Tests for the connect_timeout() function on a TcpStream. This runs with only +// one test task to ensure that errors are timeouts, not file descriptor +// exhaustion. + +#![reexport_test_harness_main = "test_main"] + +#![allow(unused_imports)] +#![feature(old_io, std_misc, io)] + +use std::old_io::*; +use std::old_io::test::*; +use std::old_io; +use std::time::Duration; +use std::sync::mpsc::channel; +use std::thread; + +#[cfg_attr(target_os = "freebsd", ignore)] +fn eventual_timeout() { + let addr = next_test_ip4(); + + let (tx1, rx1) = channel(); + let (_tx2, rx2) = channel::<()>(); + let t = thread::spawn(move|| { + let _l = TcpListener::bind(addr).unwrap().listen(); + tx1.send(()).unwrap(); + let _ = rx2.recv(); + }); + rx1.recv().unwrap(); + + let mut v = Vec::new(); + for _ in 0_usize..10000 { + match TcpStream::connect_timeout(addr, Duration::milliseconds(100)) { + Ok(e) => v.push(e), + Err(ref e) if e.kind == old_io::TimedOut => return, + Err(e) => panic!("other error: {}", e), + } + } + panic!("never timed out!"); + t.join(); +} + +fn timeout_success() { + let addr = next_test_ip4(); + let _l = TcpListener::bind(addr).unwrap().listen(); + + assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(1000)).is_ok()); +} + +fn timeout_error() { + let addr = next_test_ip4(); + + assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(1000)).is_err()); +} + +fn connect_timeout_zero() { + let addr = next_test_ip4(); + assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(0)).is_err()); +} + +fn connect_timeout_negative() { + let addr = next_test_ip4(); + assert!(TcpStream::connect_timeout(addr, Duration::milliseconds(-1)).is_err()); +} diff --git a/src/test/run-pass/tempfile.rs b/src/test/run-pass/tempfile.rs new file mode 100644 index 00000000000..3f99c338c0e --- /dev/null +++ b/src/test/run-pass/tempfile.rs @@ -0,0 +1,213 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-windows TempDir may cause IoError on windows: #10463 + +// These tests are here to exercise the functionality of the `tempfile` module. +// One might expect these tests to be located in that module, but sadly they +// cannot. The tests need to invoke `os::change_dir` which cannot be done in the +// normal test infrastructure. If the tests change the current working +// directory, then *all* tests which require relative paths suddenly break b/c +// they're in a different location than before. Hence, these tests are all run +// serially here. + +#![feature(old_io, old_path, os, old_fs)] + +use std::old_path::{Path, GenericPath}; +use std::old_io::fs::PathExtensions; +use std::old_io::{fs, TempDir}; +use std::old_io; +use std::env; +use std::sync::mpsc::channel; +use std::thread; + +fn test_tempdir() { + let path = { + let p = TempDir::new_in(&Path::new("."), "foobar").unwrap(); + let p = p.path(); + assert!(p.as_str().unwrap().contains("foobar")); + p.clone() + }; + assert!(!path.exists()); +} + +fn test_rm_tempdir() { + let (tx, rx) = channel(); + let f = move|| -> () { + let tmp = TempDir::new("test_rm_tempdir").unwrap(); + tx.send(tmp.path().clone()).unwrap(); + panic!("panic to unwind past `tmp`"); + }; + thread::spawn(f).join(); + let path = rx.recv().unwrap(); + assert!(!path.exists()); + + let tmp = TempDir::new("test_rm_tempdir").unwrap(); + let path = tmp.path().clone(); + let f = move|| -> () { + let _tmp = tmp; + panic!("panic to unwind past `tmp`"); + }; + thread::spawn(f).join(); + assert!(!path.exists()); + + let path; + { + let f = move || { + TempDir::new("test_rm_tempdir").unwrap() + }; + // FIXME(#16640) `: TempDir` annotation shouldn't be necessary + let tmp: TempDir = thread::spawn(f).join().unwrap(); + path = tmp.path().clone(); + assert!(path.exists()); + } + assert!(!path.exists()); + + let path; + { + let tmp = TempDir::new("test_rm_tempdir").unwrap(); + path = tmp.into_inner(); + } + assert!(path.exists()); + fs::rmdir_recursive(&path); + assert!(!path.exists()); +} + +fn test_rm_tempdir_close() { + let (tx, rx) = channel(); + let f = move|| -> () { + let tmp = TempDir::new("test_rm_tempdir").unwrap(); + tx.send(tmp.path().clone()).unwrap(); + tmp.close(); + panic!("panic when unwinding past `tmp`"); + }; + thread::spawn(f).join(); + let path = rx.recv().unwrap(); + assert!(!path.exists()); + + let tmp = TempDir::new("test_rm_tempdir").unwrap(); + let path = tmp.path().clone(); + let f = move|| -> () { + let tmp = tmp; + tmp.close(); + panic!("panic when unwinding past `tmp`"); + }; + thread::spawn(f).join(); + assert!(!path.exists()); + + let path; + { + let f = move || { + TempDir::new("test_rm_tempdir").unwrap() + }; + // FIXME(#16640) `: TempDir` annotation shouldn't be necessary + let tmp: TempDir = thread::spawn(f).join().unwrap(); + path = tmp.path().clone(); + assert!(path.exists()); + tmp.close(); + } + assert!(!path.exists()); + + let path; + { + let tmp = TempDir::new("test_rm_tempdir").unwrap(); + path = tmp.into_inner(); + } + assert!(path.exists()); + fs::rmdir_recursive(&path); + assert!(!path.exists()); +} + +// Ideally these would be in std::os but then core would need +// to depend on std +fn recursive_mkdir_rel() { + let path = Path::new("frob"); + let cwd = Path::new(env::current_dir().unwrap().to_str().unwrap()); + println!("recursive_mkdir_rel: Making: {} in cwd {} [{}]", path.display(), + cwd.display(), path.exists()); + fs::mkdir_recursive(&path, old_io::USER_RWX); + assert!(path.is_dir()); + fs::mkdir_recursive(&path, old_io::USER_RWX); + assert!(path.is_dir()); +} + +fn recursive_mkdir_dot() { + let dot = Path::new("."); + fs::mkdir_recursive(&dot, old_io::USER_RWX); + let dotdot = Path::new(".."); + fs::mkdir_recursive(&dotdot, old_io::USER_RWX); +} + +fn recursive_mkdir_rel_2() { + let path = Path::new("./frob/baz"); + let cwd = Path::new(env::current_dir().unwrap().to_str().unwrap()); + println!("recursive_mkdir_rel_2: Making: {} in cwd {} [{}]", path.display(), + cwd.display(), path.exists()); + fs::mkdir_recursive(&path, old_io::USER_RWX); + assert!(path.is_dir()); + assert!(path.dir_path().is_dir()); + let path2 = Path::new("quux/blat"); + println!("recursive_mkdir_rel_2: Making: {} in cwd {}", path2.display(), + cwd.display()); + fs::mkdir_recursive(&path2, old_io::USER_RWX); + assert!(path2.is_dir()); + assert!(path2.dir_path().is_dir()); +} + +// Ideally this would be in core, but needs TempFile +pub fn test_rmdir_recursive_ok() { + let rwx = old_io::USER_RWX; + + let tmpdir = TempDir::new("test").ok().expect("test_rmdir_recursive_ok: \ + couldn't create temp dir"); + let tmpdir = tmpdir.path(); + let root = tmpdir.join("foo"); + + println!("making {}", root.display()); + fs::mkdir(&root, rwx); + fs::mkdir(&root.join("foo"), rwx); + fs::mkdir(&root.join("foo").join("bar"), rwx); + fs::mkdir(&root.join("foo").join("bar").join("blat"), rwx); + fs::rmdir_recursive(&root); + assert!(!root.exists()); + assert!(!root.join("bar").exists()); + assert!(!root.join("bar").join("blat").exists()); +} + +pub fn dont_double_panic() { + let r: Result<(), _> = thread::spawn(move|| { + let tmpdir = TempDir::new("test").unwrap(); + // Remove the temporary directory so that TempDir sees + // an error on drop + fs::rmdir(tmpdir.path()); + // Panic. If TempDir panics *again* due to the rmdir + // error then the process will abort. + panic!(); + }).join(); + assert!(r.is_err()); +} + +fn in_tmpdir<F>(f: F) where F: FnOnce() { + let tmpdir = TempDir::new("test").ok().expect("can't make tmpdir"); + assert!(env::set_current_dir(tmpdir.path().as_str().unwrap()).is_ok()); + + f(); +} + +pub fn main() { + in_tmpdir(test_tempdir); + in_tmpdir(test_rm_tempdir); + in_tmpdir(test_rm_tempdir_close); + in_tmpdir(recursive_mkdir_rel); + in_tmpdir(recursive_mkdir_dot); + in_tmpdir(recursive_mkdir_rel_2); + in_tmpdir(test_rmdir_recursive_ok); + in_tmpdir(dont_double_panic); +} diff --git a/src/test/run-pass/threads.rs b/src/test/run-pass/threads.rs index 969a42a6f87..184338c3294 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 02ea7037056..21205a2d7fa 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<Pet+Sync+Send>)); 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<Vec<Box<Pet+Sync+Send>>>) { diff --git a/src/test/run-pass/unique-send-2.rs b/src/test/run-pass/unique-send-2.rs index d80d0e82f4f..c32483f629e 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::<Vec<_>>(); @@ -38,4 +38,6 @@ pub fn main() { } assert_eq!(expected, actual); + + for t in ts { t.join(); } } |
