jdk8Main.future.Future.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-coroutines-core-jvm Show documentation
Show all versions of kotlinx-coroutines-core-jvm Show documentation
Coroutines support libraries for Kotlin
The newest version!
package kotlinx.coroutines.future
import kotlinx.coroutines.*
import kotlinx.coroutines.CancellationException
import java.util.concurrent.*
import java.util.function.*
import kotlin.coroutines.*
/**
* Starts a new coroutine and returns its result as an implementation of [CompletableFuture].
* The running coroutine is cancelled when the resulting future is cancelled or otherwise completed.
*
* The coroutine context is inherited from a [CoroutineScope], additional context elements can be specified with the [context] argument.
* If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used.
* The parent job is inherited from a [CoroutineScope] as well, but it can also be overridden
* with corresponding [context] element.
*
* By default, the coroutine is immediately scheduled for execution.
* Other options can be specified via `start` parameter. See [CoroutineStart] for details.
* A value of [CoroutineStart.LAZY] is not supported
* (since `CompletableFuture` framework does not provide the corresponding capability) and
* produces [IllegalArgumentException].
*
* See [newCoroutineContext][CoroutineScope.newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
*
* @param context additional to [CoroutineScope.coroutineContext] context of the coroutine.
* @param start coroutine start option. The default value is [CoroutineStart.DEFAULT].
* @param block the coroutine code.
*/
public fun CoroutineScope.future(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
) : CompletableFuture {
require(!start.isLazy) { "$start start is not supported" }
val newContext = this.newCoroutineContext(context)
val future = CompletableFuture()
val coroutine = CompletableFutureCoroutine(newContext, future)
future.handle(coroutine) // Cancel coroutine if future was completed externally
coroutine.start(start, coroutine, block)
return future
}
private class CompletableFutureCoroutine(
context: CoroutineContext,
private val future: CompletableFuture
) : AbstractCoroutine(context, initParentJob = true, active = true), BiFunction {
override fun apply(value: T?, exception: Throwable?) {
cancel()
}
override fun onCompleted(value: T) {
future.complete(value)
}
override fun onCancelled(cause: Throwable, handled: Boolean) {
/*
* Here we can potentially lose the cause if the failure is racing with future's
* external cancellation. We are consistent with other future implementations
* (LF, FT, CF) and give up on such exception.
*/
future.completeExceptionally(cause)
}
}
/**
* Converts this deferred value to the instance of [CompletableFuture].
* The deferred value is cancelled when the resulting future is cancelled or otherwise completed.
*/
public fun Deferred.asCompletableFuture(): CompletableFuture {
val future = CompletableFuture()
setupCancellation(future)
invokeOnCompletion {
try {
future.complete(getCompleted())
} catch (t: Throwable) {
future.completeExceptionally(t)
}
}
return future
}
/**
* Converts this job to the instance of [CompletableFuture].
* The job is cancelled when the resulting future is cancelled or otherwise completed.
*/
public fun Job.asCompletableFuture(): CompletableFuture {
val future = CompletableFuture()
setupCancellation(future)
invokeOnCompletion { cause ->
if (cause === null) future.complete(Unit)
else future.completeExceptionally(cause)
}
return future
}
private fun Job.setupCancellation(future: CompletableFuture<*>) {
future.handle { _, exception ->
cancel(exception?.let {
it as? CancellationException ?: CancellationException("CompletableFuture was completed exceptionally", it)
})
}
}
/**
* Converts this [CompletionStage] to an instance of [Deferred].
*
* The [CompletableFuture] that corresponds to this [CompletionStage] (see [CompletionStage.toCompletableFuture])
* is cancelled when the resulting deferred is cancelled.
*/
@Suppress("DeferredIsResult")
public fun CompletionStage.asDeferred(): Deferred {
val future = toCompletableFuture() // retrieve the future
// Fast path if already completed
if (future.isDone) {
return try {
@Suppress("UNCHECKED_CAST")
CompletableDeferred(future.get() as T)
} catch (e: Throwable) {
// unwrap original cause from ExecutionException
val original = (e as? ExecutionException)?.cause ?: e
CompletableDeferred().also { it.completeExceptionally(original) }
}
}
val result = CompletableDeferred()
handle { value, exception ->
try {
if (exception == null) {
// the future has completed normally
result.complete(value)
} else {
// the future has completed with an exception, unwrap it consistently with fast path
// Note: In the fast-path the implementation of CompletableFuture.get() does unwrapping
result.completeExceptionally((exception as? CompletionException)?.cause ?: exception)
}
} catch (e: Throwable) {
// We come here iff the internals of Deferred threw an exception during its completion
handleCoroutineException(EmptyCoroutineContext, e)
}
}
result.invokeOnCompletion(handler = CancelFutureOnCompletion(future))
return result
}
/**
* Awaits for completion of [CompletionStage] without blocking a thread.
*
* This suspending function is cancellable.
* If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function
* stops waiting for the completion stage and immediately resumes with [CancellationException][kotlinx.coroutines.CancellationException].
*
* This method is intended to be used with one-shot futures, so on coroutine cancellation the [CompletableFuture] that
* corresponds to this [CompletionStage] (see [CompletionStage.toCompletableFuture])
* is cancelled. If cancelling the given stage is undesired, `stage.asDeferred().await()` should be used instead.
*/
public suspend fun CompletionStage.await(): T {
val future = toCompletableFuture() // retrieve the future
// fast path when CompletableFuture is already done (does not suspend)
if (future.isDone) {
try {
@Suppress("UNCHECKED_CAST", "BlockingMethodInNonBlockingContext")
return future.get() as T
} catch (e: ExecutionException) {
throw e.cause ?: e // unwrap original cause from ExecutionException
}
}
// slow path -- suspend
return suspendCancellableCoroutine { cont: CancellableContinuation ->
val consumer = ContinuationHandler(cont)
handle(consumer)
cont.invokeOnCancellation {
future.cancel(false)
consumer.cont = null // shall clear reference to continuation to aid GC
}
}
}
private class ContinuationHandler(
@Volatile @JvmField var cont: Continuation?
) : BiFunction {
@Suppress("UNCHECKED_CAST")
override fun apply(result: T?, exception: Throwable?) {
val cont = this.cont ?: return // atomically read current value unless null
if (exception == null) {
// the future has completed normally
cont.resume(result as T)
} else {
// the future has completed with an exception, unwrap it to provide consistent view of .await() result and to propagate only original exception
cont.resumeWithException((exception as? CompletionException)?.cause ?: exception)
}
}
}
private class CancelFutureOnCompletion(
private val future: Future<*>
) : JobNode() {
override val onCancelling get() = false
override fun invoke(cause: Throwable?) {
// Don't interrupt when cancelling future on completion, because no one is going to reset this
// interruption flag and it will cause spurious failures elsewhere.
// We do not cancel the future if it's already completed in some way,
// because `cancel` on a completed future won't change the state but is not guaranteed to behave well
// on reentrancy. See https://github.com/Kotlin/kotlinx.coroutines/issues/4156
if (cause != null && !future.isDone) future.cancel(false)
}
}