Skip to content

Commit 5639304

Browse files
committed
Properly report exception in future builders if thrown exception is racing with external cancellation or completion
1 parent 2bac00f commit 5639304

File tree

4 files changed

+38
-2
lines changed

4 files changed

+38
-2
lines changed

integration/kotlinx-coroutines-guava/src/ListenableFuture.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ private class ListenableFutureCoroutine<T>(
7070
}
7171

7272
override fun onCompletedExceptionally(exception: Throwable) {
73-
completion.setException(exception)
73+
if (!completion.setException(exception)) {
74+
handleCoroutineException(parentContext, exception, this)
75+
}
7476
}
7577
}
7678

integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,22 @@ class ListenableFutureTest : TestBase() {
300300
finish(3)
301301
}
302302

303+
@Test
304+
fun testExceptionOnExternalCancellation() = runTest(expected = {it is TestException}) {
305+
expect(1)
306+
val result = future(Dispatchers.Unconfined) {
307+
try {
308+
delay(Long.MAX_VALUE)
309+
} finally {
310+
expect(2)
311+
throw TestException()
312+
}
313+
}
314+
315+
result.cancel(true)
316+
finish(3)
317+
}
318+
303319
private inline fun <reified T: Throwable> ListenableFuture<*>.checkFutureException(vararg suppressed: KClass<out Throwable>) {
304320
val e = assertFailsWith<ExecutionException> { get() }
305321
val cause = e.cause!!

integration/kotlinx-coroutines-jdk8/src/future/Future.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ private class CompletableFutureCoroutine<T>(
5858
}
5959

6060
override fun onCompletedExceptionally(exception: Throwable) {
61-
completion.completeExceptionally(exception)
61+
if (!completion.completeExceptionally(exception)) {
62+
handleCoroutineException(parentContext, exception, this)
63+
}
6264
}
6365
}
6466

integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,22 @@ class FutureTest : TestBase() {
411411
finish(3)
412412
}
413413

414+
@Test
415+
fun testExceptionOnExternalCompletion() = runTest(expected = {it is TestException}) {
416+
expect(1)
417+
val result = future(Dispatchers.Unconfined) {
418+
try {
419+
delay(Long.MAX_VALUE)
420+
} finally {
421+
expect(2)
422+
throw TestException()
423+
}
424+
}
425+
426+
result.complete(Unit)
427+
finish(3)
428+
}
429+
414430
private inline fun <reified T: Throwable> CompletableFuture<*>.checkFutureException(vararg suppressed: KClass<out Throwable>) {
415431
val e = assertFailsWith<ExecutionException> { get() }
416432
val cause = e.cause!!

0 commit comments

Comments
 (0)