Skip to content

Commit 4503d2a

Browse files
committed
Reliably run finalizers even if runBlocking got interrupted.
1 parent 8627cc3 commit 4503d2a

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

kotlinx-coroutines-core/jvm/src/Builders.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private class BlockingCoroutine<T>(
9292
try {
9393
while (true) {
9494
@Suppress("DEPRECATION")
95-
if (Thread.interrupted()) throw InterruptedException().also { cancelCoroutine(it) }
95+
if (Thread.interrupted()) cancelCoroutine(InterruptedException())
9696
val parkNanos = eventLoop?.processNextEvent() ?: Long.MAX_VALUE
9797
// note: process next even may loose unpark flag, so check if completed before parking
9898
if (isCompleted) break

kotlinx-coroutines-core/jvm/test/RunBlockingJvmTest.kt

+62-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package kotlinx.coroutines
22

33
import kotlinx.coroutines.testing.*
4-
import org.junit.*
4+
import java.util.concurrent.CountDownLatch
5+
import kotlin.concurrent.thread
6+
import kotlin.test.*
7+
import kotlin.time.Duration
58

69
class RunBlockingJvmTest : TestBase() {
710
@Test
@@ -12,5 +15,62 @@ class RunBlockingJvmTest : TestBase() {
1215
}
1316
rb.hashCode() // unused
1417
}
15-
}
1618

19+
/** Tests that the [runBlocking] coroutine runs to completion even it was interrupted. */
20+
@Test
21+
fun testFinishingWhenInterrupted() {
22+
startInSeparateThreadAndInterrupt { mayInterrupt ->
23+
expect(1)
24+
try {
25+
runBlocking {
26+
try {
27+
mayInterrupt()
28+
expect(2)
29+
delay(Duration.INFINITE)
30+
} finally {
31+
withContext(NonCancellable) {
32+
expect(3)
33+
repeat(10) { yield() }
34+
expect(4)
35+
}
36+
}
37+
}
38+
} catch (_: InterruptedException) {
39+
expect(5)
40+
}
41+
}
42+
finish(6)
43+
}
44+
45+
/** Tests that [runBlocking] will exit if it gets interrupted. */
46+
@Test
47+
fun testCancellingWhenInterrupted() {
48+
startInSeparateThreadAndInterrupt { mayInterrupt ->
49+
expect(1)
50+
try {
51+
runBlocking {
52+
try {
53+
mayInterrupt()
54+
expect(2)
55+
delay(Duration.INFINITE)
56+
} catch (_: CancellationException) {
57+
expect(3)
58+
}
59+
}
60+
} catch (_: InterruptedException) {
61+
expect(4)
62+
}
63+
}
64+
finish(5)
65+
}
66+
67+
private fun startInSeparateThreadAndInterrupt(action: (mayInterrupt: () -> Unit) -> Unit) {
68+
val latch = CountDownLatch(1)
69+
val thread = thread {
70+
action { latch.countDown() }
71+
}
72+
latch.await()
73+
thread.interrupt()
74+
thread.join()
75+
}
76+
}

0 commit comments

Comments
 (0)