Skip to content

Commit 03e6a74

Browse files
authored
Properly cancel handles returned by setTimeout in JS dispatchers (#3440)
Otherwise, usage of withTimeout lead to excessive memory pressure and OOMs
1 parent 73f780c commit 03e6a74

File tree

1 file changed

+9
-7
lines changed

1 file changed

+9
-7
lines changed

kotlinx-coroutines-core/js/src/JSDispatcher.kt

+9-7
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ internal sealed class SetTimeoutBasedDispatcher: CoroutineDispatcher(), Delay {
4747

4848
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
4949
val handle = setTimeout({ with(continuation) { resumeUndispatched(Unit) } }, delayToInt(timeMillis))
50-
// Actually on cancellation, but clearTimeout is idempotent
5150
continuation.invokeOnCancellation(handler = ClearTimeout(handle).asHandler)
5251
}
5352
}
@@ -64,7 +63,7 @@ internal object SetTimeoutDispatcher : SetTimeoutBasedDispatcher() {
6463
}
6564
}
6665

67-
private class ClearTimeout(private val handle: Int) : CancelHandler(), DisposableHandle {
66+
private open class ClearTimeout(protected val handle: Int) : CancelHandler(), DisposableHandle {
6867

6968
override fun dispose() {
7069
clearTimeout(handle)
@@ -83,15 +82,18 @@ internal class WindowDispatcher(private val window: Window) : CoroutineDispatche
8382
override fun dispatch(context: CoroutineContext, block: Runnable) = queue.enqueue(block)
8483

8584
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
86-
window.setTimeout({ with(continuation) { resumeUndispatched(Unit) } }, delayToInt(timeMillis))
85+
val handle = window.setTimeout({ with(continuation) { resumeUndispatched(Unit) } }, delayToInt(timeMillis))
86+
continuation.invokeOnCancellation(handler = WindowClearTimeout(handle).asHandler)
8787
}
8888

8989
override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle {
9090
val handle = window.setTimeout({ block.run() }, delayToInt(timeMillis))
91-
return object : DisposableHandle {
92-
override fun dispose() {
93-
window.clearTimeout(handle)
94-
}
91+
return WindowClearTimeout(handle)
92+
}
93+
94+
private inner class WindowClearTimeout(handle: Int) : ClearTimeout(handle) {
95+
override fun dispose() {
96+
window.clearTimeout(handle)
9597
}
9698
}
9799
}

0 commit comments

Comments
 (0)