@@ -831,7 +831,7 @@ internal class CoroutineScheduler(
831
831
// set termination deadline the first time we are here (it is reset in idleReset)
832
832
if (terminationDeadline == 0L ) terminationDeadline = System .nanoTime() + idleWorkerKeepAliveNs
833
833
// actually park
834
- doPark(idleWorkerKeepAliveNs)
834
+ if ( ! doPark(idleWorkerKeepAliveNs)) return
835
835
// try terminate when we are idle past termination deadline
836
836
// note, that comparison is written like this to protect against potential nanoTime wraparound
837
837
if (System .nanoTime() - terminationDeadline >= 0 ) {
@@ -840,9 +840,15 @@ internal class CoroutineScheduler(
840
840
}
841
841
}
842
842
843
- private fun doPark (nanos : Long ) {
843
+ private fun doPark (nanos : Long ): Boolean {
844
+ /*
845
+ * Here we are trying to park, then check whether there are new blocking tasks
846
+ * (because submitting thread could have missed this thread in tryUnpark)
847
+ */
844
848
parkedWorkersStackPush(this )
849
+ if (! blockingQuiescence()) return false
845
850
LockSupport .parkNanos(nanos)
851
+ return true
846
852
}
847
853
848
854
/* *
@@ -909,7 +915,7 @@ internal class CoroutineScheduler(
909
915
* Returns `true` if there is no blocking tasks in the queue.
910
916
*/
911
917
private fun blockingQuiescence (): Boolean {
912
- globalQueue.removeFirstBlockingModeOrNull( )?.let {
918
+ globalQueue.removeFirstWithModeOrNull( TaskMode . PROBABLY_BLOCKING )?.let {
913
919
localQueue.add(it, globalQueue)
914
920
return false
915
921
}
@@ -944,7 +950,7 @@ internal class CoroutineScheduler(
944
950
* 2) It helps with rare race when external submitter sends depending blocking tasks
945
951
* one by one and one of the requested workers may miss CPU token
946
952
*/
947
- return localQueue.poll() ? : globalQueue.removeFirstBlockingModeOrNull( )
953
+ return localQueue.poll() ? : globalQueue.removeFirstWithModeOrNull( TaskMode . PROBABLY_BLOCKING )
948
954
}
949
955
950
956
private fun findTaskWithCpuPermit (): Task ? {
@@ -953,10 +959,13 @@ internal class CoroutineScheduler(
953
959
* or local work is frequently offloaded, global queue polling will
954
960
* starve tasks from local queue. But if we never poll global queue,
955
961
* then local tasks may starve global queue, so poll global queue
956
- * once per two core pool size iterations
962
+ * once per two core pool size iterations.
963
+ * Poll global queue only for non-blocking tasks as for blocking task a separate thread was woken up.
964
+ * If current thread is woken up, then its local queue is empty and it will poll global queue anyway,
965
+ * otherwise current thread may already have blocking task in its local queue.
957
966
*/
958
967
val globalFirst = nextInt(2 * corePoolSize) == 0
959
- if (globalFirst) globalQueue.removeFirstOrNull( )?.let { return it }
968
+ if (globalFirst) globalQueue.removeFirstWithModeOrNull( TaskMode . NON_BLOCKING )?.let { return it }
960
969
localQueue.poll()?.let { return it }
961
970
if (! globalFirst) globalQueue.removeFirstOrNull()?.let { return it }
962
971
return trySteal()
0 commit comments