@@ -34,6 +34,7 @@ use std::str::FromStr;
34
34
35
35
extern crate crossbeam_deque;
36
36
extern crate crossbeam_queue;
37
+ extern crate crossbeam_utils;
37
38
#[ cfg( any( debug_assertions, rayon_unstable) ) ]
38
39
#[ macro_use]
39
40
extern crate lazy_static;
@@ -46,6 +47,8 @@ extern crate rand_xorshift;
46
47
47
48
#[ macro_use]
48
49
mod log;
50
+ #[ macro_use]
51
+ mod private;
49
52
50
53
mod job;
51
54
mod join;
@@ -64,13 +67,16 @@ mod test;
64
67
#[ cfg( rayon_unstable) ]
65
68
pub mod internal;
66
69
pub use join:: { join, join_context} ;
70
+ pub use registry:: ThreadBuilder ;
67
71
pub use scope:: { scope, Scope } ;
68
72
pub use scope:: { scope_fifo, ScopeFifo } ;
69
73
pub use spawn:: { spawn, spawn_fifo} ;
70
74
pub use thread_pool:: current_thread_has_pending_tasks;
71
75
pub use thread_pool:: current_thread_index;
72
76
pub use thread_pool:: ThreadPool ;
73
77
78
+ use registry:: { CustomSpawn , DefaultSpawn , ThreadSpawn } ;
79
+
74
80
/// Returns the number of threads in the current registry. If this
75
81
/// code is executing within a Rayon thread-pool, then this will be
76
82
/// the number of threads for the thread-pool of the current
@@ -123,8 +129,7 @@ enum ErrorKind {
123
129
///
124
130
/// [`ThreadPool`]: struct.ThreadPool.html
125
131
/// [`build_global()`]: struct.ThreadPoolBuilder.html#method.build_global
126
- #[ derive( Default ) ]
127
- pub struct ThreadPoolBuilder {
132
+ pub struct ThreadPoolBuilder < S = DefaultSpawn > {
128
133
/// The number of threads in the rayon thread pool.
129
134
/// If zero will use the RAYON_NUM_THREADS environment variable.
130
135
/// If RAYON_NUM_THREADS is invalid or zero will use the default.
@@ -146,6 +151,9 @@ pub struct ThreadPoolBuilder {
146
151
/// Closure invoked on worker thread exit.
147
152
exit_handler : Option < Box < ExitHandler > > ,
148
153
154
+ /// Closure invoked to spawn threads.
155
+ spawn_handler : S ,
156
+
149
157
/// If false, worker threads will execute spawned jobs in a
150
158
/// "depth-first" fashion. If true, they will do a "breadth-first"
151
159
/// fashion. Depth-first is the default.
@@ -174,12 +182,35 @@ type StartHandler = Fn(usize) + Send + Sync;
174
182
/// Note that this same closure may be invoked multiple times in parallel.
175
183
type ExitHandler = Fn ( usize ) + Send + Sync ;
176
184
185
+ // NB: We can't `#[derive(Default)]` because `S` is left ambiguous.
186
+ impl Default for ThreadPoolBuilder {
187
+ fn default ( ) -> Self {
188
+ ThreadPoolBuilder {
189
+ num_threads : 0 ,
190
+ panic_handler : None ,
191
+ get_thread_name : None ,
192
+ stack_size : None ,
193
+ start_handler : None ,
194
+ exit_handler : None ,
195
+ spawn_handler : DefaultSpawn ,
196
+ breadth_first : false ,
197
+ }
198
+ }
199
+ }
200
+
177
201
impl ThreadPoolBuilder {
178
202
/// Creates and returns a valid rayon thread pool builder, but does not initialize it.
179
- pub fn new ( ) -> ThreadPoolBuilder {
180
- ThreadPoolBuilder :: default ( )
203
+ pub fn new ( ) -> Self {
204
+ Self :: default ( )
181
205
}
206
+ }
182
207
208
+ /// Note: the `S: ThreadSpawn` constraint is an internal implementation detail for the
209
+ /// default spawn and those set by [`spawn_handler`](#method.spawn_handler).
210
+ impl < S > ThreadPoolBuilder < S >
211
+ where
212
+ S : ThreadSpawn ,
213
+ {
183
214
/// Create a new `ThreadPool` initialized using this configuration.
184
215
pub fn build ( self ) -> Result < ThreadPool , ThreadPoolBuildError > {
185
216
ThreadPool :: build ( self )
@@ -207,6 +238,154 @@ impl ThreadPoolBuilder {
207
238
registry. wait_until_primed ( ) ;
208
239
Ok ( ( ) )
209
240
}
241
+ }
242
+
243
+ impl ThreadPoolBuilder {
244
+ /// Create a scoped `ThreadPool` initialized using this configuration.
245
+ ///
246
+ /// This is a convenience function for building a pool using [`crossbeam::scope`]
247
+ /// to spawn threads in a [`spawn_handler`](#method.spawn_handler).
248
+ /// The threads in this pool will start by calling `wrapper`, which should
249
+ /// do initialization and continue by calling `ThreadBuilder::run()`.
250
+ ///
251
+ /// [`crossbeam::scope`]: https://docs.rs/crossbeam/0.7/crossbeam/fn.scope.html
252
+ ///
253
+ /// # Examples
254
+ ///
255
+ /// A scoped pool may be useful in combination with scoped thread-local variables.
256
+ ///
257
+ /// ```
258
+ /// #[macro_use]
259
+ /// extern crate scoped_tls;
260
+ /// # use rayon_core as rayon;
261
+ ///
262
+ /// scoped_thread_local!(static POOL_DATA: Vec<i32>);
263
+ ///
264
+ /// fn main() -> Result<(), rayon::ThreadPoolBuildError> {
265
+ /// let pool_data = vec![1, 2, 3];
266
+ ///
267
+ /// // We haven't assigned any TLS data yet.
268
+ /// assert!(!POOL_DATA.is_set());
269
+ ///
270
+ /// rayon::ThreadPoolBuilder::new()
271
+ /// .build_scoped(
272
+ /// // Borrow `pool_data` in TLS for each thread.
273
+ /// |thread| POOL_DATA.set(&pool_data, || thread.run()),
274
+ /// // Do some work that needs the TLS data.
275
+ /// |pool| pool.install(|| assert!(POOL_DATA.is_set())),
276
+ /// )?;
277
+ ///
278
+ /// // Once we've returned, `pool_data` is no longer borrowed.
279
+ /// drop(pool_data);
280
+ /// Ok(())
281
+ /// }
282
+ /// ```
283
+ pub fn build_scoped < W , F , R > ( self , wrapper : W , with_pool : F ) -> Result < R , ThreadPoolBuildError >
284
+ where
285
+ W : Fn ( ThreadBuilder ) + Sync , // expected to call `run()`
286
+ F : FnOnce ( & ThreadPool ) -> R ,
287
+ {
288
+ let result = crossbeam_utils:: thread:: scope ( |scope| {
289
+ let wrapper = & wrapper;
290
+ let pool = self
291
+ . spawn_handler ( |thread| {
292
+ let mut builder = scope. builder ( ) ;
293
+ if let Some ( name) = thread. name ( ) {
294
+ builder = builder. name ( name. to_string ( ) ) ;
295
+ }
296
+ if let Some ( size) = thread. stack_size ( ) {
297
+ builder = builder. stack_size ( size) ;
298
+ }
299
+ builder. spawn ( move |_| wrapper ( thread) ) ?;
300
+ Ok ( ( ) )
301
+ } )
302
+ . build ( ) ?;
303
+ Ok ( with_pool ( & pool) )
304
+ } ) ;
305
+
306
+ match result {
307
+ Ok ( result) => result,
308
+ Err ( err) => unwind:: resume_unwinding ( err) ,
309
+ }
310
+ }
311
+ }
312
+
313
+ impl < S > ThreadPoolBuilder < S > {
314
+ /// Set a custom function for spawning threads.
315
+ ///
316
+ /// Note that the threads will not exit until after the pool is dropped. It
317
+ /// is up to the caller to wait for thread termination if that is important
318
+ /// for any invariants. For instance, threads created in [`crossbeam::scope`]
319
+ /// will be joined before that scope returns, and this will block indefinitely
320
+ /// if the pool is leaked. Furthermore, the global thread pool doesn't terminate
321
+ /// until the entire process exits!
322
+ ///
323
+ /// [`crossbeam::scope`]: https://docs.rs/crossbeam/0.7/crossbeam/fn.scope.html
324
+ ///
325
+ /// # Examples
326
+ ///
327
+ /// A minimal spawn handler just needs to call `run()` from an independent thread.
328
+ ///
329
+ /// ```
330
+ /// # use rayon_core as rayon;
331
+ /// fn main() -> Result<(), rayon::ThreadPoolBuildError> {
332
+ /// let pool = rayon::ThreadPoolBuilder::new()
333
+ /// .spawn_handler(|thread| {
334
+ /// std::thread::spawn(|| thread.run());
335
+ /// Ok(())
336
+ /// })
337
+ /// .build()?;
338
+ ///
339
+ /// pool.install(|| println!("Hello from my custom thread!"));
340
+ /// Ok(())
341
+ /// }
342
+ /// ```
343
+ ///
344
+ /// The default spawn handler sets the name and stack size if given, and propagates
345
+ /// any errors from the thread builder.
346
+ ///
347
+ /// ```
348
+ /// # use rayon_core as rayon;
349
+ /// fn main() -> Result<(), rayon::ThreadPoolBuildError> {
350
+ /// let pool = rayon::ThreadPoolBuilder::new()
351
+ /// .spawn_handler(|thread| {
352
+ /// let mut b = std::thread::Builder::new();
353
+ /// if let Some(name) = thread.name() {
354
+ /// b = b.name(name.to_owned());
355
+ /// }
356
+ /// if let Some(stack_size) = thread.stack_size() {
357
+ /// b = b.stack_size(stack_size);
358
+ /// }
359
+ /// b.spawn(|| thread.run())?;
360
+ /// Ok(())
361
+ /// })
362
+ /// .build()?;
363
+ ///
364
+ /// pool.install(|| println!("Hello from my fully custom thread!"));
365
+ /// Ok(())
366
+ /// }
367
+ /// ```
368
+ pub fn spawn_handler < F > ( self , spawn : F ) -> ThreadPoolBuilder < CustomSpawn < F > >
369
+ where
370
+ F : FnMut ( ThreadBuilder ) -> io:: Result < ( ) > ,
371
+ {
372
+ ThreadPoolBuilder {
373
+ spawn_handler : CustomSpawn :: new ( spawn) ,
374
+ // ..self
375
+ num_threads : self . num_threads ,
376
+ panic_handler : self . panic_handler ,
377
+ get_thread_name : self . get_thread_name ,
378
+ stack_size : self . stack_size ,
379
+ start_handler : self . start_handler ,
380
+ exit_handler : self . exit_handler ,
381
+ breadth_first : self . breadth_first ,
382
+ }
383
+ }
384
+
385
+ /// Returns a reference to the current spawn handler.
386
+ fn get_spawn_handler ( & mut self ) -> & mut S {
387
+ & mut self . spawn_handler
388
+ }
210
389
211
390
/// Get the number of threads that will be used for the thread
212
391
/// pool. See `num_threads()` for more information.
@@ -276,7 +455,7 @@ impl ThreadPoolBuilder {
276
455
/// replacement of the now deprecated `RAYON_RS_NUM_CPUS` environment
277
456
/// variable. If both variables are specified, `RAYON_NUM_THREADS` will
278
457
/// be prefered.
279
- pub fn num_threads ( mut self , num_threads : usize ) -> ThreadPoolBuilder {
458
+ pub fn num_threads ( mut self , num_threads : usize ) -> Self {
280
459
self . num_threads = num_threads;
281
460
self
282
461
}
@@ -300,7 +479,7 @@ impl ThreadPoolBuilder {
300
479
/// If the panic handler itself panics, this will abort the
301
480
/// process. To prevent this, wrap the body of your panic handler
302
481
/// in a call to `std::panic::catch_unwind()`.
303
- pub fn panic_handler < H > ( mut self , panic_handler : H ) -> ThreadPoolBuilder
482
+ pub fn panic_handler < H > ( mut self , panic_handler : H ) -> Self
304
483
where
305
484
H : Fn ( Box < Any + Send > ) + Send + Sync + ' static ,
306
485
{
@@ -368,7 +547,7 @@ impl ThreadPoolBuilder {
368
547
/// Note that this same closure may be invoked multiple times in parallel.
369
548
/// If this closure panics, the panic will be passed to the panic handler.
370
549
/// If that handler returns, then startup will continue normally.
371
- pub fn start_handler < H > ( mut self , start_handler : H ) -> ThreadPoolBuilder
550
+ pub fn start_handler < H > ( mut self , start_handler : H ) -> Self
372
551
where
373
552
H : Fn ( usize ) + Send + Sync + ' static ,
374
553
{
@@ -387,7 +566,7 @@ impl ThreadPoolBuilder {
387
566
/// Note that this same closure may be invoked multiple times in parallel.
388
567
/// If this closure panics, the panic will be passed to the panic handler.
389
568
/// If that handler returns, then the thread will exit normally.
390
- pub fn exit_handler < H > ( mut self , exit_handler : H ) -> ThreadPoolBuilder
569
+ pub fn exit_handler < H > ( mut self , exit_handler : H ) -> Self
391
570
where
392
571
H : Fn ( usize ) + Send + Sync + ' static ,
393
572
{
@@ -503,7 +682,7 @@ pub fn initialize(config: Configuration) -> Result<(), Box<Error>> {
503
682
config. into_builder ( ) . build_global ( ) . map_err ( Box :: from)
504
683
}
505
684
506
- impl fmt:: Debug for ThreadPoolBuilder {
685
+ impl < S > fmt:: Debug for ThreadPoolBuilder < S > {
507
686
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
508
687
let ThreadPoolBuilder {
509
688
ref num_threads,
@@ -512,6 +691,7 @@ impl fmt::Debug for ThreadPoolBuilder {
512
691
ref stack_size,
513
692
ref start_handler,
514
693
ref exit_handler,
694
+ spawn_handler : _,
515
695
ref breadth_first,
516
696
} = * self ;
517
697
0 commit comments