@@ -372,25 +372,34 @@ internal class CoroutineScheduler(
372
372
* Dispatches execution of a runnable [block] with a hint to a scheduler whether
373
373
* this [block] may execute blocking operations (IO, system calls, locking primitives etc.)
374
374
*
375
- * @param taskContext concurrency context of given [block]
376
- * @param fair whether the task should be dispatched fairly (strict FIFO) or not (semi-FIFO)
375
+ * [taskContext] -- concurrency context of given [block].
376
+ * [tailDispatch] -- whether this [dispatch] call is the last action the (presumably) worker thread does in its current task.
377
+ * If `true`, then the task will be dispatched in a FIFO manner and no additional workers will be requested,
378
+ * but only if the current thread is a corresponding worker thread.
379
+ * Note that caller cannot be ensured that it is being executed on worker thread for the following reasons:
380
+ * * [CoroutineStart.UNDISPATCHED]
381
+ * * Concurrent [close] that effectively shutdowns the worker thread
377
382
*/
378
- fun dispatch (block : Runnable , taskContext : TaskContext = NonBlockingContext , fair : Boolean = false) {
383
+ fun dispatch (block : Runnable , taskContext : TaskContext = NonBlockingContext , tailDispatch : Boolean = false) {
379
384
trackTask() // this is needed for virtual time support
380
385
val task = createTask(block, taskContext)
381
386
// try to submit the task to the local queue and act depending on the result
382
- val notAdded = submitToLocalQueue(task, fair)
387
+ val currentWorker = currentWorker()
388
+ val notAdded = currentWorker.submitToLocalQueue(task, tailDispatch)
383
389
if (notAdded != null ) {
384
390
if (! addToGlobalQueue(notAdded)) {
385
391
// Global queue is closed in the last step of close/shutdown -- no more tasks should be accepted
386
392
throw RejectedExecutionException (" $schedulerName was terminated" )
387
393
}
388
394
}
395
+ val skipUnpark = tailDispatch && currentWorker != null
389
396
// Checking 'task' instead of 'notAdded' is completely okay
390
397
if (task.mode == TaskMode .NON_BLOCKING ) {
398
+ if (skipUnpark) return
391
399
signalCpuWork()
392
400
} else {
393
- signalBlockingWork()
401
+ // Increment blocking tasks anyway
402
+ signalBlockingWork(skipUnpark = skipUnpark)
394
403
}
395
404
}
396
405
@@ -404,9 +413,10 @@ internal class CoroutineScheduler(
404
413
return TaskImpl (block, nanoTime, taskContext)
405
414
}
406
415
407
- private fun signalBlockingWork () {
416
+ private fun signalBlockingWork (skipUnpark : Boolean ) {
408
417
// Use state snapshot to avoid thread overprovision
409
418
val stateSnapshot = incrementBlockingTasks()
419
+ if (skipUnpark) return
410
420
if (tryUnpark()) return
411
421
if (tryCreateWorker(stateSnapshot)) return
412
422
tryUnpark() // Try unpark again in case there was race between permit release and parking
@@ -481,19 +491,19 @@ internal class CoroutineScheduler(
481
491
* Returns `null` if task was successfully added or an instance of the
482
492
* task that was not added or replaced (thus should be added to global queue).
483
493
*/
484
- private fun submitToLocalQueue (task : Task , fair : Boolean ): Task ? {
485
- val worker = currentWorker() ? : return task
494
+ private fun Worker?. submitToLocalQueue (task : Task , tailDispatch : Boolean ): Task ? {
495
+ if ( this == null ) return task
486
496
/*
487
497
* This worker could have been already terminated from this thread by close/shutdown and it should not
488
498
* accept any more tasks into its local queue.
489
499
*/
490
- if (worker. state == = WorkerState .TERMINATED ) return task
500
+ if (state == = WorkerState .TERMINATED ) return task
491
501
// Do not add CPU tasks in local queue if we are not able to execute it
492
- if (task.mode == = TaskMode .NON_BLOCKING && worker. state == = WorkerState .BLOCKING ) {
502
+ if (task.mode == = TaskMode .NON_BLOCKING && state == = WorkerState .BLOCKING ) {
493
503
return task
494
504
}
495
- worker. mayHaveLocalTasks = true
496
- return worker. localQueue.add(task, fair = fair )
505
+ mayHaveLocalTasks = true
506
+ return localQueue.add(task, fair = tailDispatch )
497
507
}
498
508
499
509
private fun currentWorker (): Worker ? = (Thread .currentThread() as ? Worker )?.takeIf { it.scheduler == this }
0 commit comments