@@ -47,6 +47,8 @@ extern crate rand_xorshift;
47
47
48
48
#[ macro_use]
49
49
mod log;
50
+ #[ macro_use]
51
+ mod private;
50
52
51
53
mod job;
52
54
mod join;
@@ -73,6 +75,8 @@ pub use thread_pool::current_thread_has_pending_tasks;
73
75
pub use thread_pool:: current_thread_index;
74
76
pub use thread_pool:: ThreadPool ;
75
77
78
+ use registry:: { CustomSpawn , DefaultSpawn , ThreadSpawn } ;
79
+
76
80
/// Returns the number of threads in the current registry. If this
77
81
/// code is executing within a Rayon thread-pool, then this will be
78
82
/// the number of threads for the thread-pool of the current
@@ -125,8 +129,7 @@ enum ErrorKind {
125
129
///
126
130
/// [`ThreadPool`]: struct.ThreadPool.html
127
131
/// [`build_global()`]: struct.ThreadPoolBuilder.html#method.build_global
128
- #[ derive( Default ) ]
129
- pub struct ThreadPoolBuilder {
132
+ pub struct ThreadPoolBuilder < S = DefaultSpawn > {
130
133
/// The number of threads in the rayon thread pool.
131
134
/// If zero will use the RAYON_NUM_THREADS environment variable.
132
135
/// If RAYON_NUM_THREADS is invalid or zero will use the default.
@@ -148,6 +151,9 @@ pub struct ThreadPoolBuilder {
148
151
/// Closure invoked on worker thread exit.
149
152
exit_handler : Option < Box < ExitHandler > > ,
150
153
154
+ /// Closure invoked to spawn threads.
155
+ spawn_handler : S ,
156
+
151
157
/// If false, worker threads will execute spawned jobs in a
152
158
/// "depth-first" fashion. If true, they will do a "breadth-first"
153
159
/// fashion. Depth-first is the default.
@@ -176,19 +182,69 @@ type StartHandler = Fn(usize) + Send + Sync;
176
182
/// Note that this same closure may be invoked multiple times in parallel.
177
183
type ExitHandler = Fn ( usize ) + Send + Sync ;
178
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
+
179
201
impl ThreadPoolBuilder {
180
202
/// Creates and returns a valid rayon thread pool builder, but does not initialize it.
181
- pub fn new ( ) -> ThreadPoolBuilder {
182
- ThreadPoolBuilder :: default ( )
203
+ pub fn new ( ) -> Self {
204
+ Self :: default ( )
183
205
}
206
+ }
184
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
+ {
185
214
/// Create a new `ThreadPool` initialized using this configuration.
186
215
pub fn build ( self ) -> Result < ThreadPool , ThreadPoolBuildError > {
187
216
ThreadPool :: build ( self )
188
217
}
189
218
219
+ /// Initializes the global thread pool. This initialization is
220
+ /// **optional**. If you do not call this function, the thread pool
221
+ /// will be automatically initialized with the default
222
+ /// configuration. Calling `build_global` is not recommended, except
223
+ /// in two scenarios:
224
+ ///
225
+ /// - You wish to change the default configuration.
226
+ /// - You are running a benchmark, in which case initializing may
227
+ /// yield slightly more consistent results, since the worker threads
228
+ /// will already be ready to go even in the first iteration. But
229
+ /// this cost is minimal.
230
+ ///
231
+ /// Initialization of the global thread pool happens exactly
232
+ /// once. Once started, the configuration cannot be
233
+ /// changed. Therefore, if you call `build_global` a second time, it
234
+ /// will return an error. An `Ok` result indicates that this
235
+ /// is the first initialization of the thread pool.
236
+ pub fn build_global ( self ) -> Result < ( ) , ThreadPoolBuildError > {
237
+ let registry = registry:: init_global_registry ( self ) ?;
238
+ registry. wait_until_primed ( ) ;
239
+ Ok ( ( ) )
240
+ }
241
+ }
242
+
243
+ impl ThreadPoolBuilder {
190
244
/// Create a scoped `ThreadPool` initialized using this configuration.
191
245
///
246
+ /// This is a convenience function for building a pool using a `crossbeam`
247
+ /// scope to spawn threads in a [`spawn_handler`](#method.spawn_handler).
192
248
/// The threads in this pool will start by calling `wrapper`, which should
193
249
/// do initialization and continue by calling `ThreadBuilder::run()`.
194
250
pub fn build_scoped < W , F , R > ( self , wrapper : W , with_pool : F ) -> Result < R , ThreadPoolBuildError >
@@ -198,17 +254,19 @@ impl ThreadPoolBuilder {
198
254
{
199
255
let result = crossbeam_utils:: thread:: scope ( |scope| {
200
256
let wrapper = & wrapper;
201
- let pool = self . spawn ( |thread| {
202
- let mut builder = scope. builder ( ) ;
203
- if let Some ( name) = thread. name ( ) {
204
- builder = builder. name ( name. to_string ( ) ) ;
205
- }
206
- if let Some ( size) = thread. stack_size ( ) {
207
- builder = builder. stack_size ( size) ;
208
- }
209
- builder. spawn ( move |_| wrapper ( thread) ) ?;
210
- Ok ( ( ) )
211
- } ) ?;
257
+ let pool = self
258
+ . spawn_handler ( |thread| {
259
+ let mut builder = scope. builder ( ) ;
260
+ if let Some ( name) = thread. name ( ) {
261
+ builder = builder. name ( name. to_string ( ) ) ;
262
+ }
263
+ if let Some ( size) = thread. stack_size ( ) {
264
+ builder = builder. stack_size ( size) ;
265
+ }
266
+ builder. spawn ( move |_| wrapper ( thread) ) ?;
267
+ Ok ( ( ) )
268
+ } )
269
+ . build ( ) ?;
212
270
Ok ( with_pool ( & pool) )
213
271
} ) ;
214
272
@@ -217,58 +275,37 @@ impl ThreadPoolBuilder {
217
275
Err ( err) => unwind:: resume_unwinding ( err) ,
218
276
}
219
277
}
278
+ }
220
279
221
- /// Create a new `ThreadPool` initialized using this configuration and a
222
- /// custom function for spawning threads.
280
+ impl < S > ThreadPoolBuilder < S > {
281
+ /// Set a custom function for spawning threads.
223
282
///
224
283
/// Note that the threads will not exit until after the pool is dropped. It
225
284
/// is up to the caller to wait for thread termination if that is important
226
285
/// for any invariants. For instance, threads created in `crossbeam::scope`
227
286
/// will be joined before that scope returns, and this will block indefinitely
228
- /// if the pool is leaked.
229
- pub fn spawn (
230
- self ,
231
- spawn : impl FnMut ( ThreadBuilder ) -> io:: Result < ( ) > ,
232
- ) -> Result < ThreadPool , ThreadPoolBuildError > {
233
- ThreadPool :: build_spawn ( self , spawn)
234
- }
235
-
236
- /// Initializes the global thread pool. This initialization is
237
- /// **optional**. If you do not call this function, the thread pool
238
- /// will be automatically initialized with the default
239
- /// configuration. Calling `build_global` is not recommended, except
240
- /// in two scenarios:
241
- ///
242
- /// - You wish to change the default configuration.
243
- /// - You are running a benchmark, in which case initializing may
244
- /// yield slightly more consistent results, since the worker threads
245
- /// will already be ready to go even in the first iteration. But
246
- /// this cost is minimal.
247
- ///
248
- /// Initialization of the global thread pool happens exactly
249
- /// once. Once started, the configuration cannot be
250
- /// changed. Therefore, if you call `build_global` a second time, it
251
- /// will return an error. An `Ok` result indicates that this
252
- /// is the first initialization of the thread pool.
253
- pub fn build_global ( self ) -> Result < ( ) , ThreadPoolBuildError > {
254
- let registry = registry:: init_global_registry ( self ) ?;
255
- registry. wait_until_primed ( ) ;
256
- Ok ( ( ) )
287
+ /// if the pool is leaked. Furthermore, the global thread pool doesn't terminate
288
+ /// until the entire process exits!
289
+ pub fn spawn_handler < F > ( self , spawn : F ) -> ThreadPoolBuilder < CustomSpawn < F > >
290
+ where
291
+ F : FnMut ( ThreadBuilder ) -> io:: Result < ( ) > ,
292
+ {
293
+ ThreadPoolBuilder {
294
+ spawn_handler : CustomSpawn :: new ( spawn) ,
295
+ // ..self
296
+ num_threads : self . num_threads ,
297
+ panic_handler : self . panic_handler ,
298
+ get_thread_name : self . get_thread_name ,
299
+ stack_size : self . stack_size ,
300
+ start_handler : self . start_handler ,
301
+ exit_handler : self . exit_handler ,
302
+ breadth_first : self . breadth_first ,
303
+ }
257
304
}
258
305
259
- /// Initializes the global thread pool using a custom function for spawning
260
- /// threads.
261
- ///
262
- /// Note that the global thread pool doesn't terminate until the entire process
263
- /// exits! If this is used with something like `crossbeam::scope` that tries to
264
- /// join threads, that will block indefinitely.
265
- pub fn spawn_global (
266
- self ,
267
- spawn : impl FnMut ( ThreadBuilder ) -> io:: Result < ( ) > ,
268
- ) -> Result < ( ) , ThreadPoolBuildError > {
269
- let registry = registry:: spawn_global_registry ( self , spawn) ?;
270
- registry. wait_until_primed ( ) ;
271
- Ok ( ( ) )
306
+ /// Returns a reference to the current spawn handler.
307
+ fn get_spawn_handler ( & mut self ) -> & mut S {
308
+ & mut self . spawn_handler
272
309
}
273
310
274
311
/// Get the number of threads that will be used for the thread
@@ -339,7 +376,7 @@ impl ThreadPoolBuilder {
339
376
/// replacement of the now deprecated `RAYON_RS_NUM_CPUS` environment
340
377
/// variable. If both variables are specified, `RAYON_NUM_THREADS` will
341
378
/// be prefered.
342
- pub fn num_threads ( mut self , num_threads : usize ) -> ThreadPoolBuilder {
379
+ pub fn num_threads ( mut self , num_threads : usize ) -> Self {
343
380
self . num_threads = num_threads;
344
381
self
345
382
}
@@ -363,7 +400,7 @@ impl ThreadPoolBuilder {
363
400
/// If the panic handler itself panics, this will abort the
364
401
/// process. To prevent this, wrap the body of your panic handler
365
402
/// in a call to `std::panic::catch_unwind()`.
366
- pub fn panic_handler < H > ( mut self , panic_handler : H ) -> ThreadPoolBuilder
403
+ pub fn panic_handler < H > ( mut self , panic_handler : H ) -> Self
367
404
where
368
405
H : Fn ( Box < Any + Send > ) + Send + Sync + ' static ,
369
406
{
@@ -431,7 +468,7 @@ impl ThreadPoolBuilder {
431
468
/// Note that this same closure may be invoked multiple times in parallel.
432
469
/// If this closure panics, the panic will be passed to the panic handler.
433
470
/// If that handler returns, then startup will continue normally.
434
- pub fn start_handler < H > ( mut self , start_handler : H ) -> ThreadPoolBuilder
471
+ pub fn start_handler < H > ( mut self , start_handler : H ) -> Self
435
472
where
436
473
H : Fn ( usize ) + Send + Sync + ' static ,
437
474
{
@@ -450,7 +487,7 @@ impl ThreadPoolBuilder {
450
487
/// Note that this same closure may be invoked multiple times in parallel.
451
488
/// If this closure panics, the panic will be passed to the panic handler.
452
489
/// If that handler returns, then the thread will exit normally.
453
- pub fn exit_handler < H > ( mut self , exit_handler : H ) -> ThreadPoolBuilder
490
+ pub fn exit_handler < H > ( mut self , exit_handler : H ) -> Self
454
491
where
455
492
H : Fn ( usize ) + Send + Sync + ' static ,
456
493
{
@@ -566,7 +603,7 @@ pub fn initialize(config: Configuration) -> Result<(), Box<Error>> {
566
603
config. into_builder ( ) . build_global ( ) . map_err ( Box :: from)
567
604
}
568
605
569
- impl fmt:: Debug for ThreadPoolBuilder {
606
+ impl < S > fmt:: Debug for ThreadPoolBuilder < S > {
570
607
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
571
608
let ThreadPoolBuilder {
572
609
ref num_threads,
@@ -575,6 +612,7 @@ impl fmt::Debug for ThreadPoolBuilder {
575
612
ref stack_size,
576
613
ref start_handler,
577
614
ref exit_handler,
615
+ spawn_handler : _,
578
616
ref breadth_first,
579
617
} = * self ;
580
618
0 commit comments