@@ -19,33 +19,57 @@ package kotlinx.coroutines.experimental.future
19
19
import kotlinx.coroutines.experimental.*
20
20
import java.util.concurrent.CompletableFuture
21
21
import java.util.concurrent.CompletionStage
22
+ import java.util.function.BiConsumer
22
23
import kotlin.coroutines.experimental.Continuation
23
24
import kotlin.coroutines.experimental.CoroutineContext
24
- import kotlin.coroutines.experimental.startCoroutine
25
25
import kotlin.coroutines.experimental.suspendCoroutine
26
26
27
27
/* *
28
28
* Starts new coroutine and returns its results an an implementation of [CompletableFuture].
29
29
* This coroutine builder uses [CommonPool] context by default and is conceptually similar to [CompletableFuture.supplyAsync].
30
30
*
31
31
* The running coroutine is cancelled when the resulting future is cancelled or otherwise completed.
32
- * If the [context] for the new coroutine is explicitly specified, then it must include [CoroutineDispatcher] element.
32
+ * If the [context] for the new coroutine is explicitly specified and does not include a coroutine interceptor,
33
+ * then [CoroutineDispatcher] element.
33
34
* See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
34
35
* The specified context is added to the context of the parent running coroutine (if any) inside which this function
35
36
* is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
36
37
*
38
+ * By default, the coroutine is immediately scheduled for execution.
39
+ * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
40
+ * A value of [CoroutineStart.LAZY] is not supported
41
+ * (since `ListenableFuture` framework does not provide the corresponding capability) and
42
+ * produces [IllegalArgumentException].
43
+ *
37
44
* See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
45
+ *
46
+ * @param context context of the coroutine
47
+ * @param start coroutine start option
48
+ * @param block the coroutine code
38
49
*/
39
- public fun <T > future (context : CoroutineContext = CommonPool , block : suspend () -> T ): CompletableFuture <T > {
50
+ public fun <T > future (
51
+ context : CoroutineContext = CommonPool ,
52
+ start : CoroutineStart = CoroutineStart .DEFAULT ,
53
+ block : suspend CoroutineScope .() -> T
54
+ ): CompletableFuture <T > {
55
+ require(! start.isLazy) { " $start start is not supported" }
40
56
val newContext = newCoroutineContext(CommonPool + context)
41
57
val job = Job (newContext[Job ])
42
58
val future = CompletableFutureCoroutine <T >(newContext + job)
43
59
job.cancelFutureOnCompletion(future)
44
60
future.whenComplete { _, exception -> job.cancel(exception) }
45
- block.startCoroutine( future)
61
+ start(block, receiver = future, completion = future)
46
62
return future
47
63
}
48
64
65
+ private class CompletableFutureCoroutine <T >(
66
+ override val context : CoroutineContext
67
+ ) : CompletableFuture<T>(), Continuation<T>, CoroutineScope {
68
+ override val isActive: Boolean get() = context[Job ]!! .isActive
69
+ override fun resume (value : T ) { complete(value) }
70
+ override fun resumeWithException (exception : Throwable ) { completeExceptionally(exception) }
71
+ }
72
+
49
73
/* *
50
74
* Converts this deferred value to the instance of [CompletableFuture].
51
75
* The deferred value is cancelled when the resulting future is cancelled or otherwise completed.
@@ -70,20 +94,15 @@ public fun <T> Deferred<T>.asCompletableFuture(): CompletableFuture<T> {
70
94
* Use `CompletableFuture.await()` for cancellation support.
71
95
*/
72
96
public suspend fun <T > CompletionStage<T>.await (): T = suspendCoroutine { cont: Continuation <T > ->
73
- whenComplete { result, exception ->
74
- if (exception == null ) // the stage has been completed normally
75
- cont.resume(result)
76
- else // the stage has completed with an exception
77
- cont.resumeWithException(exception)
78
- }
97
+ whenComplete(ContinuationConsumer (cont))
79
98
}
80
99
81
100
/* *
82
101
* Awaits for completion of the future without blocking a thread.
83
102
*
84
103
* This suspending function is cancellable.
85
104
* If the [Job] of the current coroutine is completed while this suspending function is waiting, this function
86
- * cancels the `CompletableFuture` and immediately resumes with [CancellationException] .
105
+ * stops waiting for the future and immediately resumes with [CancellationException].
87
106
*/
88
107
public suspend fun <T > CompletableFuture<T>.await (): T {
89
108
if (isDone) { // fast path when CompletableFuture is already done (does not suspend)
@@ -99,21 +118,25 @@ public suspend fun <T> CompletableFuture<T>.await(): T {
99
118
}
100
119
// slow path -- suspend
101
120
return suspendCancellableCoroutine { cont: CancellableContinuation <T > ->
102
- val completionFuture = whenComplete { result, exception ->
103
- if (exception == null ) // the future has been completed normally
104
- cont.resume(result)
105
- else // the future has completed with an exception
106
- cont.resumeWithException(exception)
121
+ val consumer = ContinuationConsumer (cont)
122
+ val completionFuture = whenComplete(consumer)
123
+ cont.invokeOnCompletion {
124
+ completionFuture.cancel( false ) // cancel future
125
+ consumer.cont = null // shall clear reference to continuation, because CompletableFuture continues to keep it
107
126
}
108
- cont.cancelFutureOnCompletion(completionFuture)
109
127
}
110
128
}
111
129
112
- private class CompletableFutureCoroutine <T >(
113
- override val context : CoroutineContext
114
- ) : CompletableFuture<T>(), Continuation<T> {
115
- override fun resume (value : T ) { complete(value) }
116
- override fun resumeWithException (exception : Throwable ) { completeExceptionally(exception) }
130
+ private class ContinuationConsumer <T >(
131
+ @JvmField var cont : Continuation <T >?
132
+ ) : BiConsumer<T?, Throwable?> {
133
+ override fun accept (result : T ? , exception : Throwable ? ) {
134
+ val cont = this .cont ? : return // atomically read current value unless null, benign data race when it is set to null
135
+ if (exception == null ) // the future has been completed normally
136
+ cont.resume(result as T )
137
+ else // the future has completed with an exception
138
+ cont.resumeWithException(exception)
139
+ }
117
140
}
118
141
119
142
// --------------------------------------- DEPRECATED APIs ---------------------------------------
@@ -128,3 +151,9 @@ private class CompletableFutureCoroutine<T>(
128
151
@Deprecated(" Renamed to `asCompletableFuture`" ,
129
152
replaceWith = ReplaceWith (" asCompletableFuture()" ))
130
153
public fun <T > Deferred<T>.toCompletableFuture (): CompletableFuture <T > = asCompletableFuture()
154
+
155
+ @Deprecated(" Use the other version. This one is for binary compatibility only." , level= DeprecationLevel .HIDDEN )
156
+ public fun <T > future (
157
+ context : CoroutineContext = CommonPool ,
158
+ block : suspend () -> T
159
+ ): CompletableFuture <T > = future(context= context) { block() }
0 commit comments