Repository: Kotlin/kotlinx.coroutines
File: integration/kotlinx-coroutines-guava/src/ListenableFuture.kt (Kotlin)

Code before:
 * it to return an error after it is successfully cancelled.
 *
 * By calling [asListenableFuture] on a [Deferred], any error that occurs after successfully
 * cancelling the [ListenableFuture] representation of the [Deferred] will _not_ be passed to
 * the [CoroutineExceptionHandler]. Cancelling a [Deferred] places that [Deferred] in the
 * cancelling/cancelled states defined by [Job], which _can_ show the error. It's assumed that
 * the [Deferred] pointing to the task will be used to observe any error outcome occurring after
 * cancellation.
 *
 * This may be counterintuitive, but it maintains the error and cancellation contracts of both
 * the [Deferred] and [ListenableFuture] types, while permitting both kinds of promise to point
 * to the same running task.
 */
private class ListenableFutureCoroutine<T>(
    context: CoroutineContext
) : AbstractCoroutine<T>(context) {

    // JobListenableFuture propagates external cancellation to `this` coroutine. See JobListenableFuture.
    val future = JobListenableFuture<T>(this)

    override fun onCompleted(value: T) {
        future.complete(value)
    }

    override fun onCancelled(cause: Throwable, handled: Boolean) {
        if (!future.completeExceptionally(cause) && !handled) {
            // prevents loss of exception that was not handled by parent & could not be set to JobListenableFuture
            handleCoroutineException(context, cause)
        }
    }
}

/**
 * A [ListenableFuture] that delegates to an internal [SettableFuture], collaborating with it.
 *
 * This setup allows the returned [ListenableFuture] to maintain the following properties:
 *
 * - Correct implementation of [Future]'s happens-after semantics documented for [get], [isDone]
 *   and [isCancelled] methods
 * - Cancellation propagation both to and from [Deferred]
 * - Correct cancellation and completion semantics even when this [ListenableFuture] is combined
 *   with different concrete implementations of [ListenableFuture]
 *   - Fully correct cancellation and listener happens-after obeying [Future] and
 *     [ListenableFuture]'s documented and implicit contracts is surprisingly difficult to achieve.
 *     The best way to be correct, especially given the fun corner cases from
 *     [AbstractFuture.setFuture], is to just use an [AbstractFuture].
 *   - To maintain sanity, this class implements [ListenableFuture] and uses an auxiliary [SettableFuture]
 *     around coroutine's result as a state engine to establish happens-after-completion. This
 *     could probably be compressed into one subclass of [AbstractFuture] to save an allocation, at the
 *     cost of the implementation's readability.
 */

Review comment (refactor):
It seems like (a long-existing) problem here: if `future {}` is cancelled **normally** (e.g. its parent got cancelled) and meanwhile someone completes the future from the outside, then `JobCancellationException` got reported into an exception handler, while we guarantee that no `CancellationException` will ever reach it.

This problem seems to exist in most of our `AbstractCoroutine` implementors, unfortunately. It would be nice if you could write a test that reproduces the problem and a fix for that (see: `MonoCoroutine.onCancelled`), I'll take care of the rest of the builders

Code after:
 * it to return an error after it is successfully cancelled.
 *
 * By calling [asListenableFuture] on a [Deferred], any error that occurs after successfully
 * cancelling the [ListenableFuture] representation of the [Deferred] will _not_ be passed to
 * the [CoroutineExceptionHandler]. Cancelling a [Deferred] places that [Deferred] in the
 * cancelling/cancelled states defined by [Job], which _can_ show the error. It's assumed that
 * the [Deferred] pointing to the task will be used to observe any error outcome occurring after
 * cancellation.
 *
 * This may be counterintuitive, but it maintains the error and cancellation contracts of both
 * the [Deferred] and [ListenableFuture] types, while permitting both kinds of promise to point
 * to the same running task.
 */
private class ListenableFutureCoroutine<T>(
    context: CoroutineContext
) : AbstractCoroutine<T>(context) {

    // JobListenableFuture propagates external cancellation to `this` coroutine. See JobListenableFuture.
    @JvmField val future = JobListenableFuture<T>(this)

    override fun onCompleted(value: T) {
        future.complete(value)
    }

    override fun onCancelled(cause: Throwable, handled: Boolean) {
        if (!future.completeExceptionallyOrCancel(cause) && !handled) {
            // prevents loss of exception that was not handled by parent & could not be set to JobListenableFuture
            handleCoroutineException(context, cause)
        }
    }
}

/**
 * A [ListenableFuture] that delegates to an internal [SettableFuture], collaborating with it.
 *
 * This setup allows the returned [ListenableFuture] to maintain the following properties:
 *
 * - Correct implementation of [Future]'s happens-after semantics documented for [get], [isDone]
 *   and [isCancelled] methods
 * - Cancellation propagation both to and from [Deferred]
 * - Correct cancellation and completion semantics even when this [ListenableFuture] is combined
 *   with different concrete implementations of [ListenableFuture]
 *   - Fully correct cancellation and listener happens-after obeying [Future] and
 *     [ListenableFuture]'s documented and implicit contracts is surprisingly difficult to achieve.
 *     The best way to be correct, especially given the fun corner cases from
 *     [AbstractFuture.setFuture], is to just use an [AbstractFuture].
 *   - To maintain sanity, this class implements [ListenableFuture] and uses an auxiliary [SettableFuture]
 *     around coroutine's result as a state engine to establish happens-after-completion. This
 *     could probably be compressed into one subclass of [AbstractFuture] to save an allocation, at the
 *     cost of the implementation's readability.
 */