diff --git a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt index fce7217aa8..fdf24bc2a2 100644 --- a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt +++ b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt @@ -216,7 +216,7 @@ public fun Deferred.asListenableFuture(): ListenableFuture { * * This suspend function is cancellable. * - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function * stops waiting for the future and immediately resumes with [CancellationException][kotlinx.coroutines.CancellationException]. * * This method is intended to be used with one-shot Futures, so on coroutine cancellation, the Future is cancelled as well. diff --git a/integration/kotlinx-coroutines-play-services/src/Tasks.kt b/integration/kotlinx-coroutines-play-services/src/Tasks.kt index 2035bc749b..be4069d2fb 100644 --- a/integration/kotlinx-coroutines-play-services/src/Tasks.kt +++ b/integration/kotlinx-coroutines-play-services/src/Tasks.kt @@ -94,7 +94,7 @@ private fun Task.asDeferredImpl(cancellationTokenSource: CancellationToke * Awaits the completion of the task without blocking a thread. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * 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]. * * For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used. @@ -105,7 +105,7 @@ public suspend fun Task.await(): T = awaitImpl(null) * Awaits the completion of the task that is linked to the given [CancellationTokenSource] to control cancellation. * * This suspending function is cancellable and cancellation is bi-directional: - * - If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * - If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function * cancels the [cancellationTokenSource] and throws a [CancellationException]. * - If the task is cancelled, then this function will throw a [CancellationException]. * diff --git a/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt b/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt index 59ded36b4c..fb4bdfe4ae 100644 --- a/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt +++ b/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt @@ -28,8 +28,47 @@ public typealias MDCContextMap = Map? * using [MDC.put]. These updates are going to be lost on the next suspension and * reinstalled to the MDC context that was captured or explicitly specified in * [contextMap] when this object was created on the next resumption. - * Use `withContext(MDCContext()) { ... }` to capture updated map of MDC keys and values - * for the specified block of code. + * + * For example, the following code will not work as expected: + * + * ``` + * launch(MDCContext()) { + * MDC.put("key", "value") // This update will be lost + * delay(100) + * println(MDC.get("key")) // This will print null + * } + * ``` + * + * Instead, you should use [withContext] to capture the updated MDC context: + * + * ``` + * launch(MDCContext()) { + * MDC.put("key", "value") // This update will be captured + * withContext(MDCContext()) { + * delay(100) + * println(MDC.get("key")) // This will print "value" + * } + * } + * ``` + * + * There is no way to implicitly propagate MDC context updates from inside the coroutine to the outer scope. + * You have to capture the updated MDC context and restore it explicitly. For example: + * + * ``` + * MDC.put("a", "b") + * val contextMap = withContext(MDCContext()) { + * MDC.put("key", "value") + * withContext(MDCContext()) { + * MDC.put("key2", "value2") + * withContext(MDCContext()) { + * yield() + * MDC.getCopyOfContextMap() + * } + * } + * } + * // contextMap contains: {"a"="b", "key"="value", "key2"="value2"} + * MDC.setContextMap(contextMap) + * ``` * * @param contextMap the value of [MDC] context map. * Default value is the copy of the current thread's context map that is acquired via diff --git a/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt b/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt index e191530301..dd3b34cdee 100644 --- a/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt +++ b/integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt @@ -105,4 +105,40 @@ class MDCContextTest : TestBase() { } } } + + /** Tests that the initially captured MDC context gets restored after suspension. */ + @Test + fun testSuspensionsUndoingMdcContextUpdates() = runTest { + MDC.put("a", "b") + withContext(MDCContext()) { + MDC.put("key", "value") + assertEquals("b", MDC.get("a")) + yield() + assertNull(MDC.get("key")) + assertEquals("b", MDC.get("a")) + } + } + + /** Tests capturing and restoring the MDC context. */ + @Test + fun testRestoringMdcContext() = runTest { + MDC.put("a", "b") + val contextMap = withContext(MDCContext()) { + MDC.put("key", "value") + assertEquals("b", MDC.get("a")) + withContext(MDCContext()) { + assertEquals("value", MDC.get("key")) + MDC.put("key2", "value2") + assertEquals("value2", MDC.get("key2")) + withContext(MDCContext()) { + yield() + MDC.getCopyOfContextMap() + } + } + } + MDC.setContextMap(contextMap) + assertEquals("value2", MDC.get("key2")) + assertEquals("value", MDC.get("key")) + assertEquals("b", MDC.get("a")) + } } diff --git a/kotlinx-coroutines-core/common/src/Await.kt b/kotlinx-coroutines-core/common/src/Await.kt index fd78899834..1cc197fc65 100644 --- a/kotlinx-coroutines-core/common/src/Await.kt +++ b/kotlinx-coroutines-core/common/src/Await.kt @@ -11,11 +11,10 @@ import kotlin.coroutines.* * This function is **not** equivalent to `deferreds.map { it.await() }` which fails only when it sequentially * gets to wait for the failing deferred, while this `awaitAll` fails immediately as soon as any of the deferreds fail. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, - * this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. */ public suspend fun awaitAll(vararg deferreds: Deferred): List = if (deferreds.isEmpty()) emptyList() else AwaitAll(deferreds).await() @@ -28,11 +27,10 @@ public suspend fun awaitAll(vararg deferreds: Deferred): List = * This function is **not** equivalent to `this.map { it.await() }` which fails only when it sequentially * gets to wait for the failing deferred, while this `awaitAll` fails immediately as soon as any of the deferreds fail. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, - * this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. */ public suspend fun Collection>.awaitAll(): List = if (isEmpty()) emptyList() else AwaitAll(toTypedArray()).await() @@ -41,11 +39,10 @@ public suspend fun Collection>.awaitAll(): List = * Suspends current coroutine until all given jobs are complete. * This method is semantically equivalent to joining all given jobs one by one with `jobs.forEach { it.join() }`. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, - * this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. */ public suspend fun joinAll(vararg jobs: Job): Unit = jobs.forEach { it.join() } @@ -53,11 +50,10 @@ public suspend fun joinAll(vararg jobs: Job): Unit = jobs.forEach { it.join() } * Suspends current coroutine until all given jobs are complete. * This method is semantically equivalent to joining all given jobs one by one with `forEach { it.join() }`. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, - * this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. */ public suspend fun Collection.joinAll(): Unit = forEach { it.join() } diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt index 4b31966ee9..e4bca096ab 100644 --- a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt +++ b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt @@ -238,25 +238,25 @@ public interface CancellableContinuation : Continuation { * * This function provides **prompt cancellation guarantee**. * If the [Job] of the current coroutine was cancelled while this function was suspended it will not resume - * successfully. + * successfully, even if [CancellableContinuation.resume] was already invoked. * * The cancellation of the coroutine's job is generally asynchronous with respect to the suspended coroutine. - * The suspended coroutine is resumed with the call it to its [Continuation.resumeWith] member function or to + * The suspended coroutine is resumed with a call to its [Continuation.resumeWith] member function or to the * [resume][Continuation.resume] extension function. * However, when coroutine is resumed, it does not immediately start executing, but is passed to its * [CoroutineDispatcher] to schedule its execution when dispatcher's resources become available for execution. - * The job's cancellation can happen both before, after, and concurrently with the call to `resume`. In any - * case, prompt cancellation guarantees that the the coroutine will not resume its code successfully. + * The job's cancellation can happen before, after, and concurrently with the call to `resume`. In any + * case, prompt cancellation guarantees that the coroutine will not resume its code successfully. * * If the coroutine was resumed with an exception (for example, using [Continuation.resumeWithException] extension - * function) and cancelled, then the resulting exception of the `suspendCancellableCoroutine` function is determined - * by whichever action (exceptional resume or cancellation) that happened first. + * function) and cancelled, then the exception thrown by the `suspendCancellableCoroutine` function is determined + * by what happened first: exceptional resume or cancellation. * * ### Returning resources from a suspended coroutine * - * As a result of a prompt cancellation guarantee, when a closeable resource - * (like open file or a handle to another native resource) is returned from a suspended coroutine as a value - * it can be lost when the coroutine is cancelled. In order to ensure that the resource can be properly closed + * As a result of the prompt cancellation guarantee, when a closeable resource + * (like open file or a handle to another native resource) is returned from a suspended coroutine as a value, + * it can be lost when the coroutine is cancelled. To ensure that the resource can be properly closed * in this case, the [CancellableContinuation] interface provides two functions. * * - [invokeOnCancellation][CancellableContinuation.invokeOnCancellation] installs a handler that is called diff --git a/kotlinx-coroutines-core/common/src/CoroutineScope.kt b/kotlinx-coroutines-core/common/src/CoroutineScope.kt index 6b045d20b4..2d37d15bb7 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineScope.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineScope.kt @@ -220,13 +220,13 @@ public object GlobalScope : CoroutineScope { /** * Creates a [CoroutineScope] and calls the specified suspend block with this scope. - * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides - * the context's [Job]. + * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, using the + * [Job] from that context as the parent for a new [Job]. * * This function is designed for _concurrent decomposition_ of work. When any child coroutine in this scope fails, - * this scope fails and all the rest of the children are cancelled (for a different behavior see [supervisorScope]). - * This function returns as soon as the given block and all its children coroutines are completed. - * A usage example of a scope looks like this: + * this scope fails, cancelling all the other children (for a different behavior, see [supervisorScope]). + * This function returns as soon as the given block and all its child coroutines are completed. + * A usage of a scope looks like this: * * ``` * suspend fun showSomeData() = coroutineScope { @@ -248,8 +248,8 @@ public object GlobalScope : CoroutineScope { * 3) If the outer scope of `showSomeData` is cancelled, both started `async` and `withContext` blocks are cancelled. * 4) If the `async` block fails, `withContext` will be cancelled. * - * The method may throw a [CancellationException] if the current job was cancelled externally - * or may throw a corresponding unhandled [Throwable] if there is any unhandled exception in this scope + * The method may throw a [CancellationException] if the current job was cancelled externally, + * rethrow the exception thrown by [block], or throw an unhandled [Throwable] if there is one * (for example, from a crashed coroutine that was started with [launch][CoroutineScope.launch] in this scope). */ public suspend fun coroutineScope(block: suspend CoroutineScope.() -> R): R { diff --git a/kotlinx-coroutines-core/common/src/Deferred.kt b/kotlinx-coroutines-core/common/src/Deferred.kt index 9445f23902..afec2cc4ef 100644 --- a/kotlinx-coroutines-core/common/src/Deferred.kt +++ b/kotlinx-coroutines-core/common/src/Deferred.kt @@ -33,24 +33,46 @@ import kotlinx.coroutines.selects.* public interface Deferred : Job { /** - * Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete, - * returning the resulting value or throwing the corresponding exception if the deferred was cancelled. + * Awaits for completion of this value without blocking the thread and returns the resulting value or throws + * the exception if the deferred was cancelled. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * Unless the calling coroutine is cancelled, [await] will return the same result on each invocation: + * if the [Deferred] completed successfully, [await] will return the same value every time; + * if the [Deferred] completed exceptionally, [await] will rethrow the same exception. * - * This function can be used in [select] invocation with [onAwait] clause. - * Use [isCompleted] to check for completion of this deferred value without waiting. + * This suspending function is itself cancellable: if the [Job] of the current coroutine is cancelled or completed + * while this suspending function is waiting, this function immediately resumes with [CancellationException]. + * + * This means that [await] can throw [CancellationException] in two cases: + * - if the coroutine in which [await] was called got cancelled, + * - or if the [Deferred] itself got completed with a [CancellationException]. + * + * In both cases, the [CancellationException] will cancel the coroutine calling [await], unless it's caught. + * The following idiom may be helpful to avoid this: + * ``` + * try { + * deferred.await() + * } catch (e: CancellationException) { + * currentCoroutineContext().ensureActive() // throws if the current coroutine was cancelled + * processException(e) // if this line executes, the exception is the result of `await` itself + * } + * ``` + * + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. + * + * This function can be used in [select] invocations with an [onAwait] clause. + * Use [isCompleted] to check for completion of this deferred value without waiting, and + * [join] to wait for completion without returning the result. */ public suspend fun await(): T /** - * Clause for [select] expression of [await] suspending function that selects with the deferred value when it is - * resolved. The [select] invocation fails if the deferred value completes exceptionally (either fails or - * it cancelled). + * Clause using the [await] suspending function as a [select] clause. + * It selects with the deferred value when the [Deferred] completes. + * If [Deferred] completes with an exception, the whole the [select] invocation fails with the same exception. + * Note that, if [Deferred] completed with a [CancellationException], throwing it may have unintended + * consequences. See [await] for details. */ public val onAwait: SelectClause1 diff --git a/kotlinx-coroutines-core/common/src/Delay.kt b/kotlinx-coroutines-core/common/src/Delay.kt index 48f78b4ea0..67d3d16bb1 100644 --- a/kotlinx-coroutines-core/common/src/Delay.kt +++ b/kotlinx-coroutines-core/common/src/Delay.kt @@ -106,11 +106,10 @@ public suspend fun awaitCancellation(): Nothing = suspendCancellableCoroutine {} * Delays coroutine for at least the given time without blocking a thread and resumes it after a specified time. * If the given [timeMillis] is non-positive, this function returns immediately. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. * * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead. * @@ -133,11 +132,10 @@ public suspend fun delay(timeMillis: Long) { * Delays coroutine for at least the given [duration] without blocking a thread and resumes it after the specified time. * If the given [duration] is non-positive, this function returns immediately. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. * * If you want to delay forever (until cancellation), consider using [awaitCancellation] instead. * diff --git a/kotlinx-coroutines-core/common/src/Supervisor.kt b/kotlinx-coroutines-core/common/src/Supervisor.kt index 8036b1741e..730050b5ab 100644 --- a/kotlinx-coroutines-core/common/src/Supervisor.kt +++ b/kotlinx-coroutines-core/common/src/Supervisor.kt @@ -20,11 +20,8 @@ import kotlin.jvm.* * - A failure of a child job that was created using [launch][CoroutineScope.launch] can be handled via [CoroutineExceptionHandler] in the context. * - A failure of a child job that was created using [async][CoroutineScope.async] can be handled via [Deferred.await] on the resulting deferred value. * - * If [parent] job is specified, then this supervisor job becomes a child job of its parent and is cancelled when its - * parent fails or is cancelled. All this supervisor's children are cancelled in this case, too. The invocation of - * [cancel][Job.cancel] with exception (other than [CancellationException]) on this supervisor job also cancels parent. - * - * @param parent an optional parent job. + * If a [parent] job is specified, then this supervisor job becomes a child job of [parent] and is cancelled when the + * parent fails or is cancelled. All this supervisor's children are cancelled in this case, too. */ @Suppress("FunctionName") public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent) @@ -36,15 +33,16 @@ public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobIm public fun SupervisorJob0(parent: Job? = null) : Job = SupervisorJob(parent) /** - * Creates a [CoroutineScope] with [SupervisorJob] and calls the specified suspend block with this scope. - * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides - * context's [Job] with [SupervisorJob]. + * Creates a [CoroutineScope] with [SupervisorJob] and calls the specified suspend [block] with this scope. + * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, using the + * [Job] from that context as the parent for the new [SupervisorJob]. * This function returns as soon as the given block and all its child coroutines are completed. * * Unlike [coroutineScope], a failure of a child does not cause this scope to fail and does not affect its other children, * so a custom policy for handling failures of its children can be implemented. See [SupervisorJob] for additional details. - * A failure of the scope itself (exception thrown in the [block] or external cancellation) fails the scope with all its children, - * but does not cancel parent job. + * + * If an exception happened in [block], then the supervisor job is failed and all its children are cancelled. + * If the current coroutine was cancelled, then both the supervisor job itself and all its children are cancelled. * * The method may throw a [CancellationException] if the current job was cancelled externally, * or rethrow an exception thrown by the given [block]. diff --git a/kotlinx-coroutines-core/common/src/Yield.kt b/kotlinx-coroutines-core/common/src/Yield.kt index c6a157f10e..0598228640 100644 --- a/kotlinx-coroutines-core/common/src/Yield.kt +++ b/kotlinx-coroutines-core/common/src/Yield.kt @@ -6,12 +6,11 @@ import kotlin.coroutines.intrinsics.* /** * Yields the thread (or thread pool) of the current coroutine dispatcher * to other coroutines on the same dispatcher to run if possible. - * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed when this suspending function is invoked or while - * this function is waiting for dispatch, it resumes with a [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while + * [yield] is invoked or while waiting for dispatch, it immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. * * **Note**: This function always [checks for cancellation][ensureActive] even when it does not suspend. * diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 9523634995..af906ef691 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -41,11 +41,12 @@ public interface SendChannel { * All elements sent over the channel are delivered in first-in first-out order. The sent element * will be delivered to receivers before the close token. * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with a [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. The `send` call can send the element to the channel, - * but then throw [CancellationException], thus an exception should not be treated as a failure to deliver the element. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if [send] managed to send the element, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. + * + * Because of the prompt cancellation guarantee, an exception does not always mean a failure to deliver the element. * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. * * Note that this function does not check for cancellation when it is not suspended. @@ -211,13 +212,22 @@ public interface ReceiveChannel { * If the channel was closed because of an exception, it is called a _failed_ channel and this function * will throw the original [close][SendChannel.close] cause exception. * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this + * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled while this * function is suspended, this function immediately resumes with a [CancellationException]. * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was * suspended, it will not resume successfully. The `receive` call can retrieve the element from the channel, * but then throw [CancellationException], thus failing to deliver the element. * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. * + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if [receive] managed to retrieve the element from the channel, + * but was cancelled while suspended, [CancellationException] will be thrown. + * See [suspendCancellableCoroutine] for low-level details. + * + * Because of the prompt cancellation guarantee, some values retrieved from the channel can become lost. + * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. + * * Note that this function does not check for cancellation when it is not suspended. * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. * @@ -240,11 +250,13 @@ public interface ReceiveChannel { * or the close cause if the channel was closed. Closed cause may be `null` if the channel was closed normally. * The result cannot be [failed][ChannelResult.isFailure] without being [closed][ChannelResult.isClosed]. * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with a [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. The `receiveCatching` call can retrieve the element from the channel, - * but then throw [CancellationException], thus failing to deliver the element. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if [receiveCatching] managed to retrieve the element from the + * channel, but was cancelled while suspended, [CancellationException] will be thrown. + * See [suspendCancellableCoroutine] for low-level details. + * + * Because of the prompt cancellation guarantee, some values retrieved from the channel can become lost. * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. * * Note that this function does not check for cancellation when it is not suspended. @@ -561,11 +573,13 @@ public interface ChannelIterator { * This function retrieves and removes an element from this channel for the subsequent invocation * of [next]. * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with a [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. The `hasNext` call can retrieve the element from the channel, - * but then throw [CancellationException], thus failing to deliver the element. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if [hasNext] retrieves the element from the channel during + * its operation, but was cancelled while suspended, [CancellationException] will be thrown. + * See [suspendCancellableCoroutine] for low-level details. + * + * Because of the prompt cancellation guarantee, some values retrieved from the channel can become lost. * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. * * Note that this function does not check for cancellation when it is not suspended. @@ -651,11 +665,10 @@ public interface ChannelIterator { * ### Prompt cancellation guarantee * * All suspending functions with channels provide **prompt cancellation guarantee**. - * If the job was cancelled while send or receive function was suspended, it will not resume successfully, - * but throws a [CancellationException]. - * With a single-threaded [dispatcher][CoroutineDispatcher] like [Dispatchers.Main] this gives a - * guarantee that if a piece code running in this thread cancels a [Job], then a coroutine running this job cannot - * resume successfully and continue to run, ensuring a prompt response to its cancellation. + * If the job was cancelled while send or receive function was suspended, it will not resume successfully, even if it + * already changed the channel's state, but throws a [CancellationException]. + * With a single-threaded [dispatcher][CoroutineDispatcher] like [Dispatchers.Main], this gives a + * guarantee that the coroutine promptly reacts to the cancellation of its [Job] and does not resume its execution. * * > **Prompt cancellation guarantee** for channel operations was added since `kotlinx.coroutines` version `1.4.0` * > and had replaced a channel-specific atomic-cancellation that was not consistent with other suspending functions. @@ -663,9 +676,9 @@ public interface ChannelIterator { * * ### Undelivered elements * - * As a result of a prompt cancellation guarantee, when a closeable resource - * (like open file or a handle to another native resource) is transferred via channel from one coroutine to another - * it can fail to be delivered and will be lost if either send or receive operations are cancelled in transit. + * As a result of the prompt cancellation guarantee, when a closeable resource + * (like open file or a handle to another native resource) is transferred via a channel from one coroutine to another, + * it can fail to be delivered and will be lost if the receiving operation is cancelled in transit. * * A `Channel()` constructor function has an `onUndeliveredElement` optional parameter. * When `onUndeliveredElement` parameter is set, the corresponding function is called once for each element diff --git a/kotlinx-coroutines-core/common/src/channels/Produce.kt b/kotlinx-coroutines-core/common/src/channels/Produce.kt index 85f1df5057..3dd0bb4ff9 100644 --- a/kotlinx-coroutines-core/common/src/channels/Produce.kt +++ b/kotlinx-coroutines-core/common/src/channels/Produce.kt @@ -22,9 +22,10 @@ public interface ProducerScope : CoroutineScope, SendChannel { * Suspends the current coroutine until the channel is either [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel] * and invokes the given [block] before resuming the coroutine. * - * This suspending function is cancellable. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. * * Note that when the producer channel is cancelled, this function resumes with a cancellation exception. * Therefore, in case of cancellation, no code after the call to this function will be executed. diff --git a/kotlinx-coroutines-core/common/src/selects/Select.kt b/kotlinx-coroutines-core/common/src/selects/Select.kt index 638cc312ed..92bfb3f522 100644 --- a/kotlinx-coroutines-core/common/src/selects/Select.kt +++ b/kotlinx-coroutines-core/common/src/selects/Select.kt @@ -38,10 +38,10 @@ import kotlin.jvm.* * | [ReceiveChannel] | [receiveCatching][ReceiveChannel.receiveCatching] | [onReceiveCatching][ReceiveChannel.onReceiveCatching] * | none | [delay] | [onTimeout][SelectBuilder.onTimeout] * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. * * Note that this function does not check for cancellation when it is not suspended. * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. diff --git a/kotlinx-coroutines-core/common/src/sync/Mutex.kt b/kotlinx-coroutines-core/common/src/sync/Mutex.kt index 2bdad8c1a5..45bd383857 100644 --- a/kotlinx-coroutines-core/common/src/sync/Mutex.kt +++ b/kotlinx-coroutines-core/common/src/sync/Mutex.kt @@ -40,10 +40,10 @@ public interface Mutex { /** * Locks this mutex, suspending caller until the lock is acquired (in other words, while the lock is held elsewhere). * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. * This function releases the lock if it was already acquired by this function before the [CancellationException] * was thrown. * diff --git a/kotlinx-coroutines-core/common/src/sync/Semaphore.kt b/kotlinx-coroutines-core/common/src/sync/Semaphore.kt index 54d1efab31..bf568f3d43 100644 --- a/kotlinx-coroutines-core/common/src/sync/Semaphore.kt +++ b/kotlinx-coroutines-core/common/src/sync/Semaphore.kt @@ -28,14 +28,14 @@ public interface Semaphore { * Acquires a permit from this semaphore, suspending until one is available. * All suspending acquirers are processed in first-in-first-out (FIFO) order. * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. * This function releases the semaphore if it was already acquired by this function before the [CancellationException] * was thrown. * - * Note, that this function does not check for cancellation when it does not suspend. + * Note that this function does not check for cancellation when it does not suspend. * Use [CoroutineScope.isActive] or [CoroutineScope.ensureActive] to periodically * check for cancellation in tight loops if needed. * diff --git a/kotlinx-coroutines-core/common/test/SupervisorTest.kt b/kotlinx-coroutines-core/common/test/SupervisorTest.kt index 8866f80432..1fb0ff9a06 100644 --- a/kotlinx-coroutines-core/common/test/SupervisorTest.kt +++ b/kotlinx-coroutines-core/common/test/SupervisorTest.kt @@ -83,10 +83,12 @@ class SupervisorTest : TestBase() { @Test fun testThrowingSupervisorScope() = runTest { + var childJob: Job? = null + var supervisorJob: Job? = null try { expect(1) supervisorScope { - async { + childJob = async { try { delay(Long.MAX_VALUE) } finally { @@ -96,9 +98,13 @@ class SupervisorTest : TestBase() { expect(2) yield() + supervisorJob = coroutineContext.job throw TestException2() } } catch (e: Throwable) { + assertIs(e) + assertTrue(childJob!!.isCancelled) + assertTrue(supervisorJob!!.isCancelled) finish(4) } } @@ -155,6 +161,31 @@ class SupervisorTest : TestBase() { } } + /** + * Tests that [supervisorScope] cancels all its children when the current coroutine is cancelled. + */ + @Test + fun testSupervisorScopeExternalCancellation() = runTest { + var childJob: Job? = null + val job = launch { + supervisorScope { + childJob = launch(start = CoroutineStart.UNDISPATCHED) { + try { + delay(Long.MAX_VALUE) + } finally { + expect(2) + } + } + } + } + while (childJob == null) yield() + expect(1) + job.cancel() + assertTrue(childJob!!.isCancelled) + job.join() + finish(3) + } + @Test fun testAsyncCancellation() = runTest { val parent = SupervisorJob() diff --git a/kotlinx-coroutines-core/common/test/selects/SelectDeferredTest.kt b/kotlinx-coroutines-core/common/test/selects/SelectDeferredTest.kt index 0fc1b3be3b..5b977d992b 100644 --- a/kotlinx-coroutines-core/common/test/selects/SelectDeferredTest.kt +++ b/kotlinx-coroutines-core/common/test/selects/SelectDeferredTest.kt @@ -5,6 +5,7 @@ package kotlinx.coroutines.selects import kotlinx.coroutines.testing.* import kotlinx.coroutines.* import kotlin.test.* +import kotlin.time.Duration.Companion.seconds class SelectDeferredTest : TestBase() { @Test @@ -114,6 +115,23 @@ class SelectDeferredTest : TestBase() { finish(9) } + /** + * Tests that completing a [Deferred] with an exception will cause the [select] that uses [Deferred.onAwait] + * to throw the same exception. + */ + @Test + fun testSelectFailure() = runTest { + val d = CompletableDeferred() + d.completeExceptionally(TestException()) + val d2 = CompletableDeferred(42) + assertFailsWith { + select { + d.onAwait { expectUnreached() } + d2.onAwait { 4 } + } + } + } + @Test fun testSelectCancel() = runTest( expected = { it is CancellationException } @@ -167,4 +185,4 @@ class SelectDeferredTest : TestBase() { override val list: NodeList? get() = error("") } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/concurrent/src/Builders.concurrent.kt b/kotlinx-coroutines-core/concurrent/src/Builders.concurrent.kt index 525fc2ee75..7c0581b9d9 100644 --- a/kotlinx-coroutines-core/concurrent/src/Builders.concurrent.kt +++ b/kotlinx-coroutines-core/concurrent/src/Builders.concurrent.kt @@ -4,7 +4,21 @@ import kotlin.coroutines.* /** * Runs a new coroutine and **blocks** the current thread until its completion. - * This function should not be used from a coroutine. It is designed to bridge regular blocking code - * to libraries that are written in suspending style, to be used in `main` functions and in tests. + * + * It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in + * `main` functions and in tests. + * + * Calling [runBlocking] from a suspend function is redundant. + * For example, the following code is incorrect: + * ``` + * suspend fun loadConfiguration() { + * // DO NOT DO THIS: + * val data = runBlocking { // <- redundant and blocks the thread, do not do that + * fetchConfigurationData() // suspending function + * } + * ``` + * + * Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will + * block, potentially leading to thread starvation issues. */ -public expect fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T \ No newline at end of file +public expect fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T diff --git a/kotlinx-coroutines-core/jdk8/src/future/Future.kt b/kotlinx-coroutines-core/jdk8/src/future/Future.kt index ad2adbbe97..ac61b1e9b4 100644 --- a/kotlinx-coroutines-core/jdk8/src/future/Future.kt +++ b/kotlinx-coroutines-core/jdk8/src/future/Future.kt @@ -146,7 +146,7 @@ public fun CompletionStage.asDeferred(): Deferred { * Awaits for completion of [CompletionStage] without blocking a thread. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * 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 diff --git a/kotlinx-coroutines-core/js/src/Promise.kt b/kotlinx-coroutines-core/js/src/Promise.kt index fe4940e87e..f84338a888 100644 --- a/kotlinx-coroutines-core/js/src/Promise.kt +++ b/kotlinx-coroutines-core/js/src/Promise.kt @@ -55,11 +55,10 @@ public fun Promise.asDeferred(): Deferred { /** * Awaits for completion of the promise without blocking. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * stops waiting for the promise and immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting on the promise, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. */ public suspend fun Promise.await(): T = suspendCancellableCoroutine { cont: CancellableContinuation -> this@await.then( diff --git a/kotlinx-coroutines-core/jvm/src/Builders.kt b/kotlinx-coroutines-core/jvm/src/Builders.kt index 268194d57a..d2249bfdd0 100644 --- a/kotlinx-coroutines-core/jvm/src/Builders.kt +++ b/kotlinx-coroutines-core/jvm/src/Builders.kt @@ -10,8 +10,22 @@ import kotlin.coroutines.* /** * Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion. - * This function should not be used from a coroutine. It is designed to bridge regular blocking code - * to libraries that are written in suspending style, to be used in `main` functions and in tests. + * + * It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in + * `main` functions and in tests. + * + * Calling [runBlocking] from a suspend function is redundant. + * For example, the following code is incorrect: + * ``` + * suspend fun loadConfiguration() { + * // DO NOT DO THIS: + * val data = runBlocking { // <- redundant and blocks the thread, do not do that + * fetchConfigurationData() // suspending function + * } + * ``` + * + * Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will + * block, potentially leading to thread starvation issues. * * The default [CoroutineDispatcher] for this builder is an internal implementation of event loop that processes continuations * in this blocked thread until the completion of this coroutine. diff --git a/kotlinx-coroutines-core/native/src/Builders.kt b/kotlinx-coroutines-core/native/src/Builders.kt index d10cb48a5d..0464747e0e 100644 --- a/kotlinx-coroutines-core/native/src/Builders.kt +++ b/kotlinx-coroutines-core/native/src/Builders.kt @@ -8,8 +8,22 @@ import kotlin.native.concurrent.* /** * Runs new coroutine and **blocks** current thread _interruptibly_ until its completion. - * This function should not be used from coroutine. It is designed to bridge regular blocking code - * to libraries that are written in suspending style, to be used in `main` functions and in tests. + * + * It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in + * `main` functions and in tests. + * + * Calling [runBlocking] from a suspend function is redundant. + * For example, the following code is incorrect: + * ``` + * suspend fun loadConfiguration() { + * // DO NOT DO THIS: + * val data = runBlocking { // <- redundant and blocks the thread, do not do that + * fetchConfigurationData() // suspending function + * } + * ``` + * + * Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will + * block, potentially leading to thread starvation issues. * * The default [CoroutineDispatcher] for this builder in an implementation of [EventLoop] that processes continuations * in this blocked thread until the completion of this coroutine. diff --git a/kotlinx-coroutines-core/wasmJs/src/Promise.kt b/kotlinx-coroutines-core/wasmJs/src/Promise.kt index 980f13867f..9099a7c3e9 100644 --- a/kotlinx-coroutines-core/wasmJs/src/Promise.kt +++ b/kotlinx-coroutines-core/wasmJs/src/Promise.kt @@ -66,11 +66,10 @@ public fun Promise.asDeferred(): Deferred { /** * Awaits for completion of the promise without blocking. * - * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * stops waiting for the promise and immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. + * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this + * suspending function is waiting on the promise, this function immediately resumes with [CancellationException]. + * There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled + * while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details. */ @Suppress("UNCHECKED_CAST") public suspend fun Promise.await(): T = suspendCancellableCoroutine { cont: CancellableContinuation -> diff --git a/reactive/kotlinx-coroutines-jdk9/src/Await.kt b/reactive/kotlinx-coroutines-jdk9/src/Await.kt index b33ba43f97..2fba53dd1f 100644 --- a/reactive/kotlinx-coroutines-jdk9/src/Await.kt +++ b/reactive/kotlinx-coroutines-jdk9/src/Await.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.reactive.* * the publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. * * @throws NoSuchElementException if the publisher does not emit any value @@ -24,7 +24,7 @@ public suspend fun Flow.Publisher.awaitFirst(): T = * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. */ public suspend fun Flow.Publisher.awaitFirstOrDefault(default: T): T = @@ -35,7 +35,7 @@ public suspend fun Flow.Publisher.awaitFirstOrDefault(default: T): T = * and returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. */ public suspend fun Flow.Publisher.awaitFirstOrNull(): T? = @@ -47,7 +47,7 @@ public suspend fun Flow.Publisher.awaitFirstOrNull(): T? = * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. */ public suspend fun Flow.Publisher.awaitFirstOrElse(defaultValue: () -> T): T = @@ -58,7 +58,7 @@ public suspend fun Flow.Publisher.awaitFirstOrElse(defaultValue: () -> T) * returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. * * @throws NoSuchElementException if the publisher does not emit any value @@ -71,7 +71,7 @@ public suspend fun Flow.Publisher.awaitLast(): T = * if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. * * @throws NoSuchElementException if the publisher does not emit any value diff --git a/reactive/kotlinx-coroutines-reactive/src/Await.kt b/reactive/kotlinx-coroutines-reactive/src/Await.kt index 8ba387d161..73f0ca220b 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Await.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Await.kt @@ -12,7 +12,7 @@ import kotlin.coroutines.* * the publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. * * @throws NoSuchElementException if the publisher does not emit any value @@ -25,7 +25,7 @@ public suspend fun Publisher.awaitFirst(): T = awaitOne(Mode.FIRST) * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. */ public suspend fun Publisher.awaitFirstOrDefault(default: T): T = awaitOne(Mode.FIRST_OR_DEFAULT, default) @@ -35,7 +35,7 @@ public suspend fun Publisher.awaitFirstOrDefault(default: T): T = awaitOn * and returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. */ public suspend fun Publisher.awaitFirstOrNull(): T? = awaitOne(Mode.FIRST_OR_DEFAULT) @@ -46,7 +46,7 @@ public suspend fun Publisher.awaitFirstOrNull(): T? = awaitOne(Mode.FIRST * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. */ public suspend fun Publisher.awaitFirstOrElse(defaultValue: () -> T): T = awaitOne(Mode.FIRST_OR_DEFAULT) ?: defaultValue() @@ -56,7 +56,7 @@ public suspend fun Publisher.awaitFirstOrElse(defaultValue: () -> T): T = * returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. * * @throws NoSuchElementException if the publisher does not emit any value @@ -68,7 +68,7 @@ public suspend fun Publisher.awaitLast(): T = awaitOne(Mode.LAST) * if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. * * @throws NoSuchElementException if the publisher does not emit any value @@ -82,7 +82,7 @@ public suspend fun Publisher.awaitSingle(): T = awaitOne(Mode.SINGLE) * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. * * ### Deprecation @@ -112,7 +112,7 @@ public suspend fun Publisher.awaitSingleOrDefault(default: T): T = awaitO * produced by the publisher, `null` is returned. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. * * ### Deprecation @@ -142,7 +142,7 @@ public suspend fun Publisher.awaitSingleOrNull(): T? = awaitOne(Mode.SING * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. * * ### Deprecation diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index f8c0752419..94b925ab8e 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -36,7 +36,7 @@ public fun mono( * `null` is returned. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. */ public suspend fun Mono.awaitSingleOrNull(): T? = suspendCancellableCoroutine { cont -> @@ -68,7 +68,7 @@ public suspend fun Mono.awaitSingleOrNull(): T? = suspendCancellableCorou * if this Mono has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately cancels its [Subscription] and resumes with [CancellationException]. * * @throws NoSuchElementException if the Mono does not emit any value diff --git a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt index f59a1fd504..cfcc95db8f 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt @@ -14,7 +14,7 @@ import kotlin.coroutines.* * Awaits for completion of this completable without blocking the thread. * Returns `Unit`, or throws the corresponding exception if this completable produces an error. * - * This suspending function is cancellable. If the [Job] of the invoking coroutine is cancelled or completed while this + * This suspending function is cancellable. If the [Job] of the invoking coroutine is cancelled while this * suspending function is suspended, this function immediately resumes with [CancellationException] and disposes of its * subscription. */ @@ -42,7 +42,7 @@ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine * [MaybeSource] produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this * function immediately resumes with [CancellationException] and disposes of its subscription. */ public suspend fun MaybeSource.awaitSingleOrNull(): T? = suspendCancellableCoroutine { cont -> @@ -70,7 +70,7 @@ public suspend fun MaybeSource.awaitSingleOrNull(): T? = suspendCancellab * Returns the resulting value, or throws if either no value is produced or this [MaybeSource] produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this * function immediately resumes with [CancellationException] and disposes of its subscription. * * @throws NoSuchElementException if no elements were produced by this [MaybeSource]. @@ -83,7 +83,7 @@ public suspend fun MaybeSource.awaitSingle(): T = awaitSingleOrNull() ?: * maybe had produced error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function * immediately resumes with [CancellationException]. * * ### Deprecation @@ -105,7 +105,7 @@ public suspend fun MaybeSource.await(): T? = awaitSingleOrNull() * maybe had produced error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function * immediately resumes with [CancellationException]. * * ### Deprecation @@ -128,7 +128,7 @@ public suspend fun MaybeSource.awaitOrDefault(default: T): T = awaitSingl * Returns the resulting value, or throws the corresponding exception if this response produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun SingleSource.await(): T = suspendCancellableCoroutine { cont -> @@ -154,7 +154,7 @@ public suspend fun SingleSource.await(): T = suspendCancellableCoroutine * if the observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. * * @throws NoSuchElementException if the observable does not emit any value @@ -167,7 +167,7 @@ public suspend fun ObservableSource.awaitFirst(): T = awaitOne(Mode.FIRST * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrDefault(default: T): T = awaitOne(Mode.FIRST_OR_DEFAULT, default) @@ -178,7 +178,7 @@ public suspend fun ObservableSource.awaitFirstOrDefault(default: T): T = * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrNull(): T? = awaitOne(Mode.FIRST_OR_DEFAULT) @@ -189,7 +189,7 @@ public suspend fun ObservableSource.awaitFirstOrNull(): T? = awaitOne(Mod * the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: () -> T): T = @@ -200,7 +200,7 @@ public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: () -> * returns the resulting value, or, if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. * * @throws NoSuchElementException if the observable does not emit any value @@ -212,7 +212,7 @@ public suspend fun ObservableSource.awaitLast(): T = awaitOne(Mode.LAST) * if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. * * @throws NoSuchElementException if the observable does not emit any value diff --git a/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt b/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt index 6e75c2b135..0353d77f12 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt @@ -14,7 +14,7 @@ import kotlin.coroutines.* * Awaits for completion of this completable without blocking the thread. * Returns `Unit`, or throws the corresponding exception if this completable produces an error. * - * This suspending function is cancellable. If the [Job] of the invoking coroutine is cancelled or completed while this + * This suspending function is cancellable. If the [Job] of the invoking coroutine is cancelled while this * suspending function is suspended, this function immediately resumes with [CancellationException] and disposes of its * subscription. */ @@ -34,7 +34,7 @@ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine * [MaybeSource] produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this * function immediately resumes with [CancellationException] and disposes of its subscription. */ public suspend fun MaybeSource.awaitSingleOrNull(): T? = suspendCancellableCoroutine { cont -> @@ -51,7 +51,7 @@ public suspend fun MaybeSource.awaitSingleOrNull(): T? = suspendCan * Returns the resulting value, or throws if either no value is produced or this [MaybeSource] produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this * function immediately resumes with [CancellationException] and disposes of its subscription. * * @throws NoSuchElementException if no elements were produced by this [MaybeSource]. @@ -64,7 +64,7 @@ public suspend fun MaybeSource.awaitSingle(): T = awaitSingleOrNull * maybe had produced error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function * immediately resumes with [CancellationException]. * * ### Deprecation @@ -87,7 +87,7 @@ public suspend fun MaybeSource.await(): T? = awaitSingleOrNull() * maybe had produced error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function + * If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function * immediately resumes with [CancellationException]. * * ### Deprecation @@ -111,7 +111,7 @@ public suspend fun MaybeSource.awaitOrDefault(default: T): T = awai * Returns the resulting value, or throws the corresponding exception if this response produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun SingleSource.await(): T = suspendCancellableCoroutine { cont -> @@ -129,7 +129,7 @@ public suspend fun SingleSource.await(): T = suspendCancellableCoro * if the observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. * * @throws NoSuchElementException if the observable does not emit any value @@ -143,7 +143,7 @@ public suspend fun ObservableSource.awaitFirst(): T = awaitOne(Mode * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ @Suppress("UNCHECKED_CAST") @@ -156,7 +156,7 @@ public suspend fun ObservableSource.awaitFirstOrDefault(default: T) * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrNull(): T? = awaitOne(Mode.FIRST_OR_DEFAULT) @@ -167,7 +167,7 @@ public suspend fun ObservableSource.awaitFirstOrNull(): T? = awaitO * the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: () -> T): T = @@ -178,7 +178,7 @@ public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: * returns the resulting value, or, if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. * * @throws NoSuchElementException if the observable does not emit any value @@ -191,7 +191,7 @@ public suspend fun ObservableSource.awaitLast(): T = awaitOne(Mode. * if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * If the [Job] of the current coroutine is cancelled while the suspending function is waiting, this * function immediately disposes of its subscription and resumes with [CancellationException]. * * @throws NoSuchElementException if the observable does not emit any value