Skip to content

Commit 19c1f2e

Browse files
committed
Unwrap ExecutionException is fast path of CompletionStage.asDeferred
1 parent 7bd2c50 commit 19c1f2e

File tree

2 files changed

+22
-14
lines changed
  • integration/kotlinx-coroutines-jdk8/src
    • main/kotlin/kotlinx/coroutines/experimental/future
    • test/kotlin/kotlinx/coroutines/experimental/future

2 files changed

+22
-14
lines changed

integration/kotlinx-coroutines-jdk8/src/main/kotlin/kotlinx/coroutines/experimental/future/Future.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,27 +119,25 @@ public suspend fun <T> CompletionStage<T>.await(): T = suspendCoroutine { cont:
119119
* Converts this future to an instance of [Deferred].
120120
*/
121121
public fun <T> CompletionStage<T>.asDeferred(): Deferred<T> {
122-
123122
// Fast path if already completed
124123
if (this is Future<*> && isDone()){
125124
return try {
126125
@Suppress("UNCHECKED_CAST")
127126
CompletableDeferred(get() as T)
128-
} catch (t: Throwable) {
129-
CompletableDeferred<T>().also { it.completeExceptionally(t) }
127+
} catch (e: Throwable) {
128+
// unwrap original cause from ExecutionException
129+
val original = (e as? ExecutionException)?.cause ?: e
130+
CompletableDeferred<T>().also { it.completeExceptionally(original) }
130131
}
131132
}
132-
133133
val result = CompletableDeferred<T>()
134-
135134
whenComplete { value, exception ->
136135
if (exception == null) {
137136
result.complete(value)
138137
} else {
139138
result.completeExceptionally(exception)
140139
}
141140
}
142-
143141
return result
144142
}
145143

integration/kotlinx-coroutines-jdk8/src/test/kotlin/kotlinx/coroutines/experimental/future/FutureTest.kt

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
package kotlinx.coroutines.experimental.future
1818

1919
import kotlinx.coroutines.experimental.*
20+
import kotlinx.coroutines.experimental.CancellationException
2021
import org.hamcrest.core.IsEqual
2122
import org.junit.Assert.*
2223
import org.junit.Before
2324
import org.junit.Test
24-
import java.util.concurrent.CompletableFuture
25-
import java.util.concurrent.CompletionStage
26-
import java.util.concurrent.ExecutionException
25+
import java.util.concurrent.*
2726
import java.util.concurrent.atomic.AtomicInteger
2827
import java.util.concurrent.locks.ReentrantLock
2928
import kotlin.concurrent.withLock
@@ -280,17 +279,22 @@ class FutureTest : TestBase() {
280279

281280
@Test
282281
fun testFailedFutureAsDeferred() = runBlocking {
283-
val future = CompletableFuture<Int>().apply { completeExceptionally(Exception("something went wrong")) }
282+
val future = CompletableFuture<Int>().apply {
283+
completeExceptionally(TestException("something went wrong"))
284+
}
284285
val deferred = future.asDeferred()
285286

286287
assertTrue(deferred.isCompletedExceptionally)
287-
assertEquals("something went wrong", deferred.getCompletionExceptionOrNull()!!.cause!!.message)
288+
val completionException = deferred.getCompletionExceptionOrNull()!!
289+
assertTrue(completionException is TestException)
290+
assertEquals("something went wrong", completionException.message)
288291

289292
try {
290293
deferred.await()
291294
fail("deferred.await() should throw an exception")
292295
} catch (e: Exception) {
293-
assertEquals("something went wrong", e.cause!!.message)
296+
assertTrue(e is TestException)
297+
assertEquals("something went wrong", e.message)
294298
}
295299
}
296300

@@ -299,7 +303,7 @@ class FutureTest : TestBase() {
299303
val lock = ReentrantLock().apply { lock() }
300304

301305
val deferred: Deferred<Int> = CompletableFuture.supplyAsync {
302-
lock.withLock { throw Exception("something went wrong") }
306+
lock.withLock { throw TestException("something went wrong") }
303307
}.asDeferred()
304308

305309
assertFalse(deferred.isCompleted)
@@ -310,10 +314,16 @@ class FutureTest : TestBase() {
310314
fail("deferred.await() should throw an exception")
311315
} catch (e: Exception) {
312316
assertTrue(deferred.isCompletedExceptionally)
313-
assertEquals("something went wrong", e.cause!!.message)
317+
assertTrue(e is CompletionException) // that's how supplyAsync wraps it
318+
val cause = e.cause!!
319+
assertTrue(cause is TestException)
320+
assertEquals("something went wrong", cause.message)
321+
assertSame(e, deferred.getCompletionExceptionOrNull()) // same exception is returns as thrown
314322
}
315323
}
316324

325+
class TestException(message: String) : Exception(message)
326+
317327
private fun wrapContinuation(wrapper: (() -> Unit) -> Unit): CoroutineDispatcher = object : CoroutineDispatcher() {
318328
override fun dispatch(context: CoroutineContext, block: Runnable) {
319329
wrapper {

0 commit comments

Comments
 (0)