@@ -301,23 +301,29 @@ internal class CoroutineScheduler(
301
301
val currentWorker = Thread .currentThread() as ? Worker
302
302
// Capture # of created workers that cannot change anymore (mind the synchronized block!)
303
303
val created = synchronized(workers) { createdWorkers }
304
+ // Shutdown all workers with the only exception of the current thread
304
305
for (i in 1 .. created) {
305
306
val worker = workers[i]!!
306
- if (worker.isAlive && worker != = currentWorker) {
307
- LockSupport .unpark(worker)
308
- worker.join(timeout)
307
+ if (worker != = currentWorker) {
308
+ while (worker.isAlive) {
309
+ LockSupport .unpark(worker)
310
+ worker.join(timeout)
311
+ }
312
+ val state = worker.state
313
+ check(state == = WorkerState .TERMINATED ) { " Expected TERMINATED state, but found $state " }
309
314
worker.localQueue.offloadAllWork(globalQueue)
310
315
}
311
-
312
316
}
317
+ // Make sure no more work is added to GlobalQueue from anywhere
318
+ check(globalQueue.add(CLOSED_TASK )) { " GlobalQueue could not be closed yet" }
313
319
// Finish processing tasks from globalQueue and/or from this worker's local queue
314
320
while (true ) {
315
- val task = currentWorker?.findTask() ? : globalQueue.removeFirstOrNull () ? : break
321
+ val task = currentWorker?.findTask() ? : globalQueue.removeFirstIfNotClosed () ? : break
316
322
runSafely(task)
317
323
}
318
324
// Shutdown current thread
319
325
currentWorker?.tryReleaseCpu(WorkerState .TERMINATED )
320
- // cleanup state to make sure that tryUnpark tries to create new threads and fails because isTerminated
326
+ // check & cleanup state
321
327
assert (cpuPermits.availablePermits() == corePoolSize)
322
328
parkedWorkersStack.value = 0L
323
329
controlState.value = 0L
@@ -339,8 +345,12 @@ internal class CoroutineScheduler(
339
345
when (submitToLocalQueue(task, fair)) {
340
346
ADDED -> return
341
347
NOT_ADDED -> {
342
- globalQueue.addLast(task) // offload task to local queue
343
- requestCpuWorker() // ask for help
348
+ // try to offload task to global queue
349
+ if (! globalQueue.add(task)) {
350
+ // Global queue is closed in the last step of close/shutdown -- no more tasks should be accepted
351
+ throw RejectedExecutionException (" $schedulerName was terminated" )
352
+ }
353
+ requestCpuWorker()
344
354
}
345
355
else -> requestCpuWorker() // ask for help
346
356
}
@@ -439,7 +449,7 @@ internal class CoroutineScheduler(
439
449
private fun createNewWorker (): Int {
440
450
synchronized(workers) {
441
451
// Make sure we're not trying to resurrect terminated scheduler
442
- if (isTerminated) throw RejectedExecutionException ( " $schedulerName was terminated " )
452
+ if (isTerminated) return - 1
443
453
val state = controlState.value
444
454
val created = createdWorkers(state)
445
455
val blocking = blockingWorkers(state)
@@ -464,6 +474,12 @@ internal class CoroutineScheduler(
464
474
? : return NOT_ADDED
465
475
if (worker.scheduler != = this ) return NOT_ADDED // different scheduler's worker (!!!)
466
476
477
+ /*
478
+ * This worker could have been already terminated from this thread by close/shutdown and it should not
479
+ * accept any more tasks into its local queue.
480
+ */
481
+ if (worker.state == = WorkerState .TERMINATED ) return NOT_ADDED
482
+
467
483
var result = ADDED
468
484
if (task.mode == TaskMode .NON_BLOCKING ) {
469
485
/*
@@ -923,9 +939,9 @@ internal class CoroutineScheduler(
923
939
* once per two core pool size iterations
924
940
*/
925
941
val globalFirst = nextInt(2 * corePoolSize) == 0
926
- if (globalFirst) globalQueue.removeFirstOrNull ()?.let { return it }
942
+ if (globalFirst) globalQueue.removeFirstIfNotClosed ()?.let { return it }
927
943
localQueue.poll()?.let { return it }
928
- if (! globalFirst) globalQueue.removeFirstOrNull ()?.let { return it }
944
+ if (! globalFirst) globalQueue.removeFirstIfNotClosed ()?.let { return it }
929
945
return trySteal()
930
946
}
931
947
0 commit comments