Skip to content

Commit

Permalink
Fallout: move from scoped to spawn
Browse files Browse the repository at this point in the history
  • Loading branch information
aturon committed Apr 15, 2015
1 parent 6e0fb70 commit e72adca
Show file tree
Hide file tree
Showing 52 changed files with 128 additions and 124 deletions.
4 changes: 4 additions & 0 deletions src/doc/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ safe concurrent programs.
Here's an example of a concurrent Rust program:
```{rust}
# #![feature(scoped)]
use std::thread;
fn main() {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
49 changes: 8 additions & 41 deletions src/doc/trpl/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions src/libstd/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
//! a join before any relevant stack frames are popped:
//!
//! ```rust
//! # #![feature(scoped)]
//! use std::thread;
//!
//! let guard = thread::scoped(move || {
Expand Down
4 changes: 2 additions & 2 deletions src/test/bench/shootout-binarytrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: {}",
Expand Down
4 changes: 2 additions & 2 deletions src/test/bench/shootout-fannkuch-redux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,15 @@ 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)
}))
}

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);
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/bench/shootout-k-nucleotide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
8 changes: 4 additions & 4 deletions src/test/bench/shootout-mandelbrot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fn mandelbrot<W: old_io::Writer>(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);

Expand All @@ -108,7 +108,7 @@ fn mandelbrot<W: old_io::Writer>(w: usize, mut out: W) -> old_io::IoResult<()> {
}).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());
}
Expand All @@ -123,7 +123,7 @@ fn mandelbrot<W: old_io::Writer>(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<u8> = Vec::with_capacity((chunk_size * w) / 8);
let init_r_slice = vec_init_r;

Expand All @@ -144,7 +144,7 @@ fn mandelbrot<W: old_io::Writer>(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()
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/bench/shootout-reverse-complement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion src/test/bench/shootout-spectralnorm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/test/run-fail/panic-task-name-owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!();
}
2 changes: 1 addition & 1 deletion src/test/run-fail/rt-set-exit-status-panic2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!();
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/atomic-print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
3 changes: 2 additions & 1 deletion src/test/run-pass/capturing-logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ 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<Logger+Send>);
debug!("debug");
info!("info");
});
let s = r.read_to_string().unwrap();
assert!(s.contains("info"));
assert!(!s.contains("debug"));
t.join();
}
4 changes: 2 additions & 2 deletions src/test/run-pass/clone-with-exterior.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
3 changes: 2 additions & 1 deletion src/test/run-pass/comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>) {
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/extern-call-deep2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/test/run-pass/extern-call-scrub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
7 changes: 2 additions & 5 deletions src/test/run-pass/fds-are-cloexec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,19 @@ 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();
let udp2 = udp1.try_clone().unwrap();

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())
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/init-large-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
3 changes: 2 additions & 1 deletion src/test/run-pass/issue-13494.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -38,4 +38,5 @@ fn main() {
}
}
drop(tx);
t.join();
}
4 changes: 2 additions & 2 deletions src/test/run-pass/issue-20454.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Loading

0 comments on commit e72adca

Please sign in to comment.