Skip to content

Commit fc64d38

Browse files
committed
Use crossbeam to get safety & stability.
This converts `map`, `unordered_map`, `Pool::map` and `Pool::unordered_map` to take a `crossbeam::Scope` argument, instead of using the API of the old `thread::scoped` to return a guard (where the destructor was required to run to ensure safety, something not guaranteed). See rust-lang/rust#24292 for more discussion of the issues with that API.
1 parent cdff00b commit fc64d38

File tree

7 files changed

+207
-126
lines changed

7 files changed

+207
-126
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ operations, including parallel maps, for loops and thread pools.
2020
strided = "*"
2121
num = "*"
2222
num_cpus = "0.2"
23+
[dependencies]
24+
crossbeam = "0.1"
2325

2426
[features]
2527
unstable = []

src/lib.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -211,26 +211,19 @@
211211
//! contains more intricate example(s), such as a parallel fast
212212
//! Fourier transform implementation (it really works, and the
213213
//! parallelism does buy something... when tuned).
214-
#![cfg_attr(feature = "unstable", feature(scoped))]
215214
216-
#[cfg(feature = "unstable")]
217-
use std::thread;
218-
#[cfg(feature = "unstable")]
219-
use std::iter::IntoIterator;
215+
extern crate crossbeam;
220216

221-
#[cfg(feature = "unstable")]
222217
mod maps;
223218

224219
mod fnbox;
225220

226221
pub mod pool;
227222

228223
pub mod one_to_one {
229-
#[cfg(feature = "unstable")]
230224
pub use maps::{unordered_map, UnorderedParMap, map, ParMap};
231225
}
232226

233-
#[cfg(feature = "unstable")]
234227
pub use one_to_one::{map, unordered_map};
235228

236229
pub use pool::Pool;
@@ -241,15 +234,14 @@ pub use pool::Pool;
241234
/// If `f` panics, so does `for_`. If this occurs, the number of
242235
/// elements of `iter` that have had `f` called on them is
243236
/// unspecified.
244-
#[cfg(feature = "unstable")]
245237
pub fn for_<I: IntoIterator, F>(iter: I, ref f: F)
246238
where I::Item: Send, F: Fn(I::Item) + Sync
247239
{
248-
let _guards: Vec<_> = iter.into_iter().map(|elem| {
249-
thread::scoped(move || {
250-
f(elem)
251-
})
252-
}).collect();
240+
crossbeam::scope(|scope| {
241+
for elem in iter {
242+
scope.spawn(move || f(elem));
243+
}
244+
});
253245
}
254246

255247
/// Execute `f` on both `x` and `y`, in parallel, returning the
@@ -258,14 +250,16 @@ pub fn for_<I: IntoIterator, F>(iter: I, ref f: F)
258250
/// This is the same (including panic semantics) as `(f(x), f(y))`, up
259251
/// to ordering. It is designed to be used for divide-and-conquer
260252
/// algorithms.
261-
#[cfg(feature = "unstable")]
262253
pub fn both<T, U, F>(x: T, y: T, ref f: F) -> (U, U)
263254
where T: Send,
264255
U: Send,
265256
F: Sync + Fn(T) -> U
266257
{
267-
let guard = thread::scoped(move || f(y));
268-
let a = f(x);
269-
let b = guard.join();
270-
(a, b)
258+
crossbeam::scope(|scope| {
259+
let guard = scope.spawn(move || f(y));
260+
let a = f(x);
261+
let b = guard.join();
262+
(a, b)
263+
264+
})
271265
}

src/maps.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::sync::mpsc::{self, Sender, Receiver};
2-
use std::thread::{self,JoinGuard};
32
use std::cmp::Ordering;
43
use std::collections::BinaryHeap;
54
use std::iter::IntoIterator;
65

6+
use crossbeam::{Scope, ScopedJoinHandle};
7+
78
struct Packet<T> {
89
// this should be unique for a given instance of `*ParMap`
910
idx: usize,
@@ -25,12 +26,12 @@ impl<T> Eq for Packet<T> {}
2526

2627
/// A parallel-mapping iterator that doesn't care about the order in
2728
/// which elements come out.
28-
pub struct UnorderedParMap<'a, T: 'a + Send> {
29+
pub struct UnorderedParMap<T: Send> {
2930
rx: Receiver<Packet<T>>,
30-
_guards: Vec<JoinGuard<'a, ()>>
31+
_guards: Vec<ScopedJoinHandle<()>>
3132
}
3233

33-
impl<'a,T: 'static + Send> Iterator for UnorderedParMap<'a, T> {
34+
impl<T: 'static + Send> Iterator for UnorderedParMap<T> {
3435
type Item = (usize, T);
3536

3637
fn next(&mut self) -> Option<(usize, T)> {
@@ -62,9 +63,9 @@ impl<T: Send + 'static> Drop for Panicker<T> {
6263
/// This behaves like `simple_parallel::map`, but does not make
6364
/// efforts to ensure that the elements are returned in the order of
6465
/// `iter`, hence this is cheaper.
65-
pub fn unordered_map<'a, I: IntoIterator, F, T>(iter: I, f: &'a F) -> UnorderedParMap<'a, T>
66+
pub fn unordered_map<'a, I: IntoIterator, F, T>(scope: &Scope<'a>, iter: I, f: &'a F) -> UnorderedParMap<T>
6667
where I::Item: Send + 'a,
67-
F: 'a + Sync + Fn(I::Item) -> T,
68+
F: Sync + Fn(I::Item) -> T,
6869
T: Send + 'static
6970
{
7071
let (tx, rx) = mpsc::channel();
@@ -73,7 +74,7 @@ pub fn unordered_map<'a, I: IntoIterator, F, T>(iter: I, f: &'a F) -> UnorderedP
7374
let tx = tx.clone();
7475
let f = f.clone();
7576

76-
thread::scoped(move || {
77+
scope.spawn(move || {
7778
let mut p = Panicker { tx: tx, idx: idx, all_ok: false };
7879
let val = f(elem);
7980
let _ = p.tx.send(Packet { idx: idx, data: Some(val) });
@@ -88,13 +89,13 @@ pub fn unordered_map<'a, I: IntoIterator, F, T>(iter: I, f: &'a F) -> UnorderedP
8889
}
8990

9091
/// A parallel-mapping iterator.
91-
pub struct ParMap<'a, T: 'a + Send> {
92-
unordered: UnorderedParMap<'a, T>,
92+
pub struct ParMap<T: Send> {
93+
unordered: UnorderedParMap<T>,
9394
looking_for: usize,
9495
queue: BinaryHeap<Packet<T>>
9596
}
9697

97-
impl<'a, T: Send + 'static> Iterator for ParMap<'a, T> {
98+
impl<T: Send + 'static> Iterator for ParMap<T> {
9899
type Item = T;
99100

100101
fn next(&mut self) -> Option<T> {
@@ -127,13 +128,13 @@ impl<'a, T: Send + 'static> Iterator for ParMap<'a, T> {
127128
/// This is a drop-in replacement for `iter.map(f)`, that runs in
128129
/// parallel, and eagerly consumes `iter` spawning a thread for each
129130
/// element.
130-
pub fn map<'a, I: IntoIterator, F, T>(iter: I, f: &'a F) -> ParMap<'a, T>
131-
where I::Item: Send + 'a,
132-
F: 'a + Sync + Fn(I::Item) -> T,
131+
pub fn map<'a, I: IntoIterator, F, T>(scope: &Scope<'a>, iter: I, f: &'a F) -> ParMap<T>
132+
where I::Item: 'a + Send,
133+
F: Sync + Fn(I::Item) -> T,
133134
T: Send + 'static
134135
{
135136
ParMap {
136-
unordered: unordered_map(iter, f),
137+
unordered: unordered_map(scope, iter, f),
137138
looking_for: 0,
138139
queue: BinaryHeap::new(),
139140
}

0 commit comments

Comments
 (0)