@@ -275,8 +275,9 @@ impl TaskPool {
275
275
F : for < ' scope > FnOnce ( & ' scope Scope < ' scope , ' env , T > ) ,
276
276
T : Send + ' static ,
277
277
{
278
- Self :: THREAD_EXECUTOR
279
- . with ( |thread_executor| self . scope_with_executor_inner ( true , thread_executor, f) )
278
+ Self :: THREAD_EXECUTOR . with ( |scope_executor| {
279
+ self . scope_with_executor_inner ( true , scope_executor, scope_executor, f)
280
+ } )
280
281
}
281
282
282
283
/// This allows passing an external executor to spawn tasks on. When you pass an external executor
@@ -291,28 +292,39 @@ impl TaskPool {
291
292
pub fn scope_with_executor < ' env , F , T > (
292
293
& self ,
293
294
tick_task_pool_executor : bool ,
294
- thread_executor : Option < & ThreadExecutor > ,
295
+ external_executor : Option < & ThreadExecutor > ,
295
296
f : F ,
296
297
) -> Vec < T >
297
298
where
298
299
F : for < ' scope > FnOnce ( & ' scope Scope < ' scope , ' env , T > ) ,
299
300
T : Send + ' static ,
300
301
{
301
- // If a `thread_executor` is passed use that. Otherwise get the `thread_executor` stored
302
- // in the `THREAD_EXECUTOR` thread local.
303
- if let Some ( thread_executor) = thread_executor {
304
- self . scope_with_executor_inner ( tick_task_pool_executor, thread_executor, f)
305
- } else {
306
- Self :: THREAD_EXECUTOR . with ( |thread_executor| {
307
- self . scope_with_executor_inner ( tick_task_pool_executor, thread_executor, f)
308
- } )
309
- }
302
+ Self :: THREAD_EXECUTOR . with ( |scope_executor| {
303
+ // If a `external_executor` is passed use that. Otherwise get the executor stored
304
+ // in the `THREAD_EXECUTOR` thread local.
305
+ if let Some ( external_executor) = external_executor {
306
+ self . scope_with_executor_inner (
307
+ tick_task_pool_executor,
308
+ external_executor,
309
+ scope_executor,
310
+ f,
311
+ )
312
+ } else {
313
+ self . scope_with_executor_inner (
314
+ tick_task_pool_executor,
315
+ scope_executor,
316
+ scope_executor,
317
+ f,
318
+ )
319
+ }
320
+ } )
310
321
}
311
322
312
323
fn scope_with_executor_inner < ' env , F , T > (
313
324
& self ,
314
325
tick_task_pool_executor : bool ,
315
- thread_executor : & ThreadExecutor ,
326
+ external_executor : & ThreadExecutor ,
327
+ scope_executor : & ThreadExecutor ,
316
328
f : F ,
317
329
) -> Vec < T >
318
330
where
@@ -326,15 +338,17 @@ impl TaskPool {
326
338
// transmute the lifetimes to 'env here to appease the compiler as it is unable to validate safety.
327
339
let executor: & async_executor:: Executor = & self . executor ;
328
340
let executor: & ' env async_executor:: Executor = unsafe { mem:: transmute ( executor) } ;
329
- let thread_executor: & ' env ThreadExecutor < ' env > =
330
- unsafe { mem:: transmute ( thread_executor) } ;
341
+ let external_executor: & ' env ThreadExecutor < ' env > =
342
+ unsafe { mem:: transmute ( external_executor) } ;
343
+ let scope_executor: & ' env ThreadExecutor < ' env > = unsafe { mem:: transmute ( scope_executor) } ;
331
344
let spawned: ConcurrentQueue < FallibleTask < T > > = ConcurrentQueue :: unbounded ( ) ;
332
345
let spawned_ref: & ' env ConcurrentQueue < FallibleTask < T > > =
333
346
unsafe { mem:: transmute ( & spawned) } ;
334
347
335
348
let scope = Scope {
336
349
executor,
337
- thread_executor,
350
+ external_executor,
351
+ scope_executor,
338
352
spawned : spawned_ref,
339
353
scope : PhantomData ,
340
354
env : PhantomData ,
@@ -357,25 +371,36 @@ impl TaskPool {
357
371
} ;
358
372
359
373
let tick_task_pool_executor = tick_task_pool_executor || self . threads . is_empty ( ) ;
360
- if let Some ( thread_ticker) = thread_executor. ticker ( ) {
374
+
375
+ // we get this from a thread local so we should always be on the scope executors thread.
376
+ let scope_ticker = scope_executor. ticker ( ) . unwrap ( ) ;
377
+ if let Some ( external_ticker) = external_executor. ticker ( ) {
361
378
if tick_task_pool_executor {
362
- Self :: execute_local_global ( thread_ticker, executor, get_results) . await
379
+ Self :: execute_global_external_scope (
380
+ executor,
381
+ external_ticker,
382
+ scope_ticker,
383
+ get_results,
384
+ )
385
+ . await
363
386
} else {
364
- Self :: execute_local ( thread_ticker, get_results) . await
387
+ Self :: execute_external_scope ( external_ticker, scope_ticker, get_results)
388
+ . await
365
389
}
366
390
} else if tick_task_pool_executor {
367
- Self :: execute_global ( executor, get_results) . await
391
+ Self :: execute_global_scope ( executor, scope_ticker , get_results) . await
368
392
} else {
369
- get_results. await
393
+ Self :: execute_scope ( scope_ticker , get_results) . await
370
394
}
371
395
} )
372
396
}
373
397
}
374
398
375
399
#[ inline]
376
- async fn execute_local_global < ' scope , ' ticker , T > (
377
- thread_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
400
+ async fn execute_global_external_scope < ' scope , ' ticker , T > (
378
401
executor : & ' scope async_executor:: Executor < ' scope > ,
402
+ external_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
403
+ scope_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
379
404
get_results : impl Future < Output = Vec < T > > ,
380
405
) -> Vec < T > {
381
406
// we restart the executors if a task errors. if a scoped
@@ -384,7 +409,7 @@ impl TaskPool {
384
409
loop {
385
410
let tick_forever = async {
386
411
loop {
387
- thread_ticker . tick ( ) . await ;
412
+ external_ticker . tick ( ) . or ( scope_ticker . tick ( ) ) . await ;
388
413
}
389
414
} ;
390
415
// we don't care if it errors. If a scoped task errors it will propagate
@@ -399,15 +424,16 @@ impl TaskPool {
399
424
}
400
425
401
426
#[ inline]
402
- async fn execute_local < ' scope , ' ticker , T > (
403
- thread_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
427
+ async fn execute_external_scope < ' scope , ' ticker , T > (
428
+ external_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
429
+ scope_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
404
430
get_results : impl Future < Output = Vec < T > > ,
405
431
) -> Vec < T > {
406
432
let execute_forever = async {
407
433
loop {
408
434
let tick_forever = async {
409
435
loop {
410
- thread_ticker . tick ( ) . await ;
436
+ external_ticker . tick ( ) . or ( scope_ticker . tick ( ) ) . await ;
411
437
}
412
438
} ;
413
439
let _result = AssertUnwindSafe ( tick_forever) . catch_unwind ( ) . await . is_ok ( ) ;
@@ -417,13 +443,19 @@ impl TaskPool {
417
443
}
418
444
419
445
#[ inline]
420
- async fn execute_global < ' scope , T > (
446
+ async fn execute_global_scope < ' scope , ' ticker , T > (
421
447
executor : & ' scope async_executor:: Executor < ' scope > ,
448
+ scope_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
422
449
get_results : impl Future < Output = Vec < T > > ,
423
450
) -> Vec < T > {
424
451
let execute_forever = async {
425
452
loop {
426
- let _result = AssertUnwindSafe ( executor. run ( std:: future:: pending :: < ( ) > ( ) ) )
453
+ let tick_forever = async {
454
+ loop {
455
+ scope_ticker. tick ( ) . await ;
456
+ }
457
+ } ;
458
+ let _result = AssertUnwindSafe ( executor. run ( tick_forever) )
427
459
. catch_unwind ( )
428
460
. await
429
461
. is_ok ( ) ;
@@ -432,6 +464,24 @@ impl TaskPool {
432
464
execute_forever. or ( get_results) . await
433
465
}
434
466
467
+ #[ inline]
468
+ async fn execute_scope < ' scope , ' ticker , T > (
469
+ scope_ticker : ThreadExecutorTicker < ' scope , ' ticker > ,
470
+ get_results : impl Future < Output = Vec < T > > ,
471
+ ) -> Vec < T > {
472
+ let execute_forever = async {
473
+ loop {
474
+ let tick_forever = async {
475
+ loop {
476
+ scope_ticker. tick ( ) . await ;
477
+ }
478
+ } ;
479
+ let _result = AssertUnwindSafe ( tick_forever) . catch_unwind ( ) . await . is_ok ( ) ;
480
+ }
481
+ } ;
482
+ execute_forever. or ( get_results) . await
483
+ }
484
+
435
485
/// Spawns a static future onto the thread pool. The returned Task is a future. It can also be
436
486
/// cancelled and "detached" allowing it to continue running without having to be polled by the
437
487
/// end-user.
@@ -501,7 +551,8 @@ impl Drop for TaskPool {
501
551
#[ derive( Debug ) ]
502
552
pub struct Scope < ' scope , ' env : ' scope , T > {
503
553
executor : & ' scope async_executor:: Executor < ' scope > ,
504
- thread_executor : & ' scope ThreadExecutor < ' scope > ,
554
+ external_executor : & ' scope ThreadExecutor < ' scope > ,
555
+ scope_executor : & ' scope ThreadExecutor < ' scope > ,
505
556
spawned : & ' scope ConcurrentQueue < FallibleTask < T > > ,
506
557
// make `Scope` invariant over 'scope and 'env
507
558
scope : PhantomData < & ' scope mut & ' scope ( ) > ,
@@ -531,7 +582,21 @@ impl<'scope, 'env, T: Send + 'scope> Scope<'scope, 'env, T> {
531
582
///
532
583
/// For more information, see [`TaskPool::scope`].
533
584
pub fn spawn_on_scope < Fut : Future < Output = T > + ' scope + Send > ( & self , f : Fut ) {
534
- let task = self . thread_executor . spawn ( f) . fallible ( ) ;
585
+ let task = self . scope_executor . spawn ( f) . fallible ( ) ;
586
+ // ConcurrentQueue only errors when closed or full, but we never
587
+ // close and use an unbounded queue, so it is safe to unwrap
588
+ self . spawned . push ( task) . unwrap ( ) ;
589
+ }
590
+
591
+ /// Spawns a scoped future onto the thread of the external thread executor.
592
+ /// This is typically the main thread. The scope *must* outlive
593
+ /// the provided future. The results of the future will be returned as a part of
594
+ /// [`TaskPool::scope`]'s return value. Users should generally prefer to use
595
+ /// [`Scope::spawn`] instead, unless the provided future needs to run on the external thread.
596
+ ///
597
+ /// For more information, see [`TaskPool::scope`].
598
+ pub fn spawn_on_external < Fut : Future < Output = T > + ' scope + Send > ( & self , f : Fut ) {
599
+ let task = self . external_executor . spawn ( f) . fallible ( ) ;
535
600
// ConcurrentQueue only errors when closed or full, but we never
536
601
// close and use an unbounded queue, so it is safe to unwrap
537
602
self . spawned . push ( task) . unwrap ( ) ;
0 commit comments