Skip to content

Commit 389e9eb

Browse files
committed
Ensure that LimitedDispatcher doesn't keep stale runningWorkers
1 parent 660ee49 commit 389e9eb

File tree

1 file changed

+31
-15
lines changed

1 file changed

+31
-15
lines changed

Diff for: kotlinx-coroutines-core/common/src/internal/LimitedDispatcher.kt

+31-15
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,15 @@ internal class LimitedDispatcher(
6565
// `runningWorkers` when they observed an empty queue.
6666
if (!tryAllocateWorker()) return
6767
val task = obtainTaskOrDeallocateWorker() ?: return
68-
startWorker(Worker(task))
68+
try {
69+
startWorker(Worker(task))
70+
} catch (e: Throwable) {
71+
// If we failed to start a worker, we should deallocate the worker slot
72+
synchronized(workerAllocationLock) {
73+
runningWorkers.decrementAndGet()
74+
}
75+
throw e
76+
}
6977
}
7078

7179
/**
@@ -107,21 +115,29 @@ internal class LimitedDispatcher(
107115
*/
108116
private inner class Worker(private var currentTask: Runnable) : Runnable {
109117
override fun run() {
110-
var fairnessCounter = 0
111-
while (true) {
112-
try {
113-
currentTask.run()
114-
} catch (e: Throwable) {
115-
handleCoroutineException(EmptyCoroutineContext, e)
118+
try {
119+
var fairnessCounter = 0
120+
while (true) {
121+
try {
122+
currentTask.run()
123+
} catch (e: Throwable) {
124+
handleCoroutineException(EmptyCoroutineContext, e)
125+
}
126+
currentTask = obtainTaskOrDeallocateWorker() ?: return
127+
// 16 is our out-of-thin-air constant to emulate fairness. Used in JS dispatchers as well
128+
if (++fairnessCounter >= 16 && dispatcher.safeIsDispatchNeeded(this@LimitedDispatcher)) {
129+
// Do "yield" to let other views execute their runnable as well
130+
// Note that we do not decrement 'runningWorkers' as we are still committed to our part of work
131+
dispatcher.safeDispatch(this@LimitedDispatcher, this)
132+
return
133+
}
116134
}
117-
currentTask = obtainTaskOrDeallocateWorker() ?: return
118-
// 16 is our out-of-thin-air constant to emulate fairness. Used in JS dispatchers as well
119-
if (++fairnessCounter >= 16 && dispatcher.safeIsDispatchNeeded(this@LimitedDispatcher)) {
120-
// Do "yield" to let other views execute their runnable as well
121-
// Note that we do not decrement 'runningWorkers' as we are still committed to our part of work
122-
dispatcher.safeDispatch(this@LimitedDispatcher, this)
123-
return
135+
} catch (e: Throwable) {
136+
// If the worker failed, we should deallocate its slot
137+
synchronized(workerAllocationLock) {
138+
runningWorkers.decrementAndGet()
124139
}
140+
throw e
125141
}
126142
}
127143
}
@@ -132,4 +148,4 @@ internal fun Int.checkParallelism() = require(this >= 1) { "Expected positive pa
132148
internal fun CoroutineDispatcher.namedOrThis(name: String?): CoroutineDispatcher {
133149
if (name != null) return NamedDispatcher(this, name)
134150
return this
135-
}
151+
}

0 commit comments

Comments
 (0)