Skip to content

Commit 1dfc5f9

Browse files
committed
Job.asCompletableFuture
Fixes #1104
1 parent 4398350 commit 1dfc5f9

File tree

4 files changed

+148
-46
lines changed

4 files changed

+148
-46
lines changed

binary-compatibility-validator/reference-public-api/kotlinx-coroutines-jdk8.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
public final class kotlinx/coroutines/future/FutureKt {
22
public static final fun asCompletableFuture (Lkotlinx/coroutines/Deferred;)Ljava/util/concurrent/CompletableFuture;
3+
public static final fun asCompletableFuture (Lkotlinx/coroutines/Job;)Ljava/util/concurrent/CompletableFuture;
34
public static final fun asDeferred (Ljava/util/concurrent/CompletionStage;)Lkotlinx/coroutines/Deferred;
45
public static final fun await (Ljava/util/concurrent/CompletionStage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
56
public static final fun future (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;)Ljava/util/concurrent/CompletableFuture;

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

+23-5
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,7 @@ private class CompletableFutureCoroutine<T>(
7171
*/
7272
public fun <T> Deferred<T>.asCompletableFuture(): CompletableFuture<T> {
7373
val future = CompletableFuture<T>()
74-
future.whenComplete { _, exception ->
75-
cancel(exception?.let {
76-
it as? CancellationException ?: CancellationException("CompletableFuture was completed exceptionally", it)
77-
})
78-
}
74+
setupCancellation(future)
7975
invokeOnCompletion {
8076
try {
8177
future.complete(getCompleted())
@@ -86,6 +82,28 @@ public fun <T> Deferred<T>.asCompletableFuture(): CompletableFuture<T> {
8682
return future
8783
}
8884

85+
/**
86+
* Converts this completable job to the instance of [CompletableFuture].
87+
* The job is cancelled when the resulting future is cancelled or otherwise completed.
88+
*/
89+
public fun Job.asCompletableFuture(): CompletableFuture<Unit> {
90+
val future = CompletableFuture<Unit>()
91+
setupCancellation(future)
92+
invokeOnCompletion { throwable ->
93+
if (throwable === null) future.complete(Unit)
94+
else future.completeExceptionally(throwable)
95+
}
96+
return future
97+
}
98+
99+
private fun Job.setupCancellation(future: CompletableFuture<*>) {
100+
future.whenComplete { _, exception ->
101+
cancel(exception?.let {
102+
it as? CancellationException ?: CancellationException("CompletableFuture was completed exceptionally", it)
103+
})
104+
}
105+
}
106+
89107
/**
90108
* Converts this completion stage to an instance of [Deferred].
91109
* When this completion stage is an instance of [Future], then it is cancelled when
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.coroutines.future
6+
7+
import kotlinx.coroutines.*
8+
import org.junit.*
9+
import org.junit.Assert.*
10+
import java.util.concurrent.*
11+
import java.util.concurrent.CancellationException
12+
13+
class AsFutureTest : TestBase() {
14+
15+
@Test
16+
fun testCompletedDeferredAsCompletableFuture() = runTest {
17+
expect(1)
18+
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
19+
expect(2) // completed right away
20+
"OK"
21+
}
22+
expect(3)
23+
val future = deferred.asCompletableFuture()
24+
assertEquals("OK", future.await())
25+
finish(4)
26+
}
27+
28+
@Test
29+
fun testCompletedJobAsCompletableFuture() = runTest {
30+
val job = Job().apply { complete() }
31+
val future = job.asCompletableFuture()
32+
assertEquals(Unit, future.await())
33+
}
34+
35+
@Test
36+
fun testWaitForDeferredAsCompletableFuture() = runTest {
37+
expect(1)
38+
val deferred = async {
39+
expect(3) // will complete later
40+
"OK"
41+
}
42+
expect(2)
43+
val future = deferred.asCompletableFuture()
44+
assertEquals("OK", future.await()) // await yields main thread to deferred coroutine
45+
finish(4)
46+
}
47+
48+
@Test
49+
fun testWaitForJobAsCompletableFuture() = runTest {
50+
val job = Job()
51+
val future = job.asCompletableFuture()
52+
assertTrue(job.isActive)
53+
job.complete()
54+
assertFalse(job.isActive)
55+
assertEquals(Unit, future.await())
56+
}
57+
58+
@Test
59+
fun testAsCompletableFutureThrowable() {
60+
val deferred = GlobalScope.async<Unit> { throw OutOfMemoryError() }
61+
val future = deferred.asCompletableFuture()
62+
try {
63+
expect(1)
64+
future.get()
65+
expectUnreached()
66+
} catch (e: ExecutionException) {
67+
assertTrue(future.isCompletedExceptionally)
68+
assertTrue(e.cause is OutOfMemoryError)
69+
finish(2)
70+
}
71+
}
72+
73+
@Test
74+
fun testJobAsCompletableFutureThrowable() {
75+
val job = Job()
76+
CompletableDeferred<Unit>(parent = job).apply { completeExceptionally(OutOfMemoryError()) }
77+
val future = job.asCompletableFuture()
78+
try {
79+
expect(1)
80+
future.get()
81+
expectUnreached()
82+
} catch (e: ExecutionException) {
83+
assertTrue(future.isCompletedExceptionally)
84+
assertTrue(e.cause is OutOfMemoryError)
85+
finish(2)
86+
}
87+
}
88+
89+
@Test
90+
fun testJobAsCompletableFutureCancellation() {
91+
val job = Job()
92+
val future = job.asCompletableFuture()
93+
job.cancel()
94+
try {
95+
expect(1)
96+
future.get()
97+
expectUnreached()
98+
} catch (e: CancellationException) {
99+
assertTrue(future.isCompletedExceptionally)
100+
finish(2)
101+
}
102+
}
103+
104+
@Test
105+
fun testJobCancellation() {
106+
val job = Job()
107+
val future = job.asCompletableFuture()
108+
future.cancel(true)
109+
assertTrue(job.isCancelled)
110+
assertTrue(job.isCompleted)
111+
assertFalse(job.isActive)
112+
}
113+
114+
@Test
115+
fun testDeferredCancellation() {
116+
val deferred = CompletableDeferred<Int>()
117+
val future = deferred.asCompletableFuture()
118+
future.cancel(true)
119+
assertTrue(deferred.isCancelled)
120+
assertTrue(deferred.isCompleted)
121+
assertFalse(deferred.isActive)
122+
assertTrue(deferred.getCompletionExceptionOrNull() is CancellationException)
123+
}
124+
}

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

-41
Original file line numberDiff line numberDiff line change
@@ -162,47 +162,6 @@ class FutureTest : TestBase() {
162162
}
163163
}
164164

165-
@Test
166-
fun testCompletedDeferredAsCompletableFuture() = runBlocking {
167-
expect(1)
168-
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
169-
expect(2) // completed right away
170-
"OK"
171-
}
172-
expect(3)
173-
val future = deferred.asCompletableFuture()
174-
assertThat(future.await(), IsEqual("OK"))
175-
finish(4)
176-
}
177-
178-
@Test
179-
fun testWaitForDeferredAsCompletableFuture() = runBlocking {
180-
expect(1)
181-
val deferred = async {
182-
expect(3) // will complete later
183-
"OK"
184-
}
185-
expect(2)
186-
val future = deferred.asCompletableFuture()
187-
assertThat(future.await(), IsEqual("OK")) // await yields main thread to deferred coroutine
188-
finish(4)
189-
}
190-
191-
@Test
192-
fun testAsCompletableFutureThrowable() {
193-
val deferred = GlobalScope.async {
194-
throw OutOfMemoryError()
195-
}
196-
197-
val future = deferred.asCompletableFuture()
198-
try {
199-
future.get()
200-
} catch (e: ExecutionException) {
201-
assertTrue(future.isCompletedExceptionally)
202-
assertTrue(e.cause is OutOfMemoryError)
203-
}
204-
}
205-
206165
@Test
207166
fun testCancellableAwaitFuture() = runBlocking {
208167
expect(1)

0 commit comments

Comments
 (0)