Skip to content

Commit c5a579e

Browse files
authored
Merge pull request #4042 from Kotlin/dk-doc-improvements
Small documentation fixes
2 parents d0dabb9 + 83fa0b4 commit c5a579e

File tree

29 files changed

+371
-180
lines changed

29 files changed

+371
-180
lines changed

integration/kotlinx-coroutines-guava/src/ListenableFuture.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public fun <T> Deferred<T>.asListenableFuture(): ListenableFuture<T> {
216216
*
217217
* This suspend function is cancellable.
218218
*
219-
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
219+
* If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function
220220
* stops waiting for the future and immediately resumes with [CancellationException][kotlinx.coroutines.CancellationException].
221221
*
222222
* This method is intended to be used with one-shot Futures, so on coroutine cancellation, the Future is cancelled as well.

integration/kotlinx-coroutines-play-services/src/Tasks.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ private fun <T> Task<T>.asDeferredImpl(cancellationTokenSource: CancellationToke
9494
* Awaits the completion of the task without blocking a thread.
9595
*
9696
* This suspending function is cancellable.
97-
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
97+
* If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function
9898
* stops waiting for the completion stage and immediately resumes with [CancellationException].
9999
*
100100
* For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used.
@@ -105,7 +105,7 @@ public suspend fun <T> Task<T>.await(): T = awaitImpl(null)
105105
* Awaits the completion of the task that is linked to the given [CancellationTokenSource] to control cancellation.
106106
*
107107
* This suspending function is cancellable and cancellation is bi-directional:
108-
* - If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
108+
* - If the [Job] of the current coroutine is cancelled while this suspending function is waiting, this function
109109
* cancels the [cancellationTokenSource] and throws a [CancellationException].
110110
* - If the task is cancelled, then this function will throw a [CancellationException].
111111
*

integration/kotlinx-coroutines-slf4j/src/MDCContext.kt

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,47 @@ public typealias MDCContextMap = Map<String, String>?
2828
* using [MDC.put]. These updates are going to be lost on the next suspension and
2929
* reinstalled to the MDC context that was captured or explicitly specified in
3030
* [contextMap] when this object was created on the next resumption.
31-
* Use `withContext(MDCContext()) { ... }` to capture updated map of MDC keys and values
32-
* for the specified block of code.
31+
*
32+
* For example, the following code will not work as expected:
33+
*
34+
* ```
35+
* launch(MDCContext()) {
36+
* MDC.put("key", "value") // This update will be lost
37+
* delay(100)
38+
* println(MDC.get("key")) // This will print null
39+
* }
40+
* ```
41+
*
42+
* Instead, you should use [withContext] to capture the updated MDC context:
43+
*
44+
* ```
45+
* launch(MDCContext()) {
46+
* MDC.put("key", "value") // This update will be captured
47+
* withContext(MDCContext()) {
48+
* delay(100)
49+
* println(MDC.get("key")) // This will print "value"
50+
* }
51+
* }
52+
* ```
53+
*
54+
* There is no way to implicitly propagate MDC context updates from inside the coroutine to the outer scope.
55+
* You have to capture the updated MDC context and restore it explicitly. For example:
56+
*
57+
* ```
58+
* MDC.put("a", "b")
59+
* val contextMap = withContext(MDCContext()) {
60+
* MDC.put("key", "value")
61+
* withContext(MDCContext()) {
62+
* MDC.put("key2", "value2")
63+
* withContext(MDCContext()) {
64+
* yield()
65+
* MDC.getCopyOfContextMap()
66+
* }
67+
* }
68+
* }
69+
* // contextMap contains: {"a"="b", "key"="value", "key2"="value2"}
70+
* MDC.setContextMap(contextMap)
71+
* ```
3372
*
3473
* @param contextMap the value of [MDC] context map.
3574
* Default value is the copy of the current thread's context map that is acquired via

integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,40 @@ class MDCContextTest : TestBase() {
105105
}
106106
}
107107
}
108+
109+
/** Tests that the initially captured MDC context gets restored after suspension. */
110+
@Test
111+
fun testSuspensionsUndoingMdcContextUpdates() = runTest {
112+
MDC.put("a", "b")
113+
withContext(MDCContext()) {
114+
MDC.put("key", "value")
115+
assertEquals("b", MDC.get("a"))
116+
yield()
117+
assertNull(MDC.get("key"))
118+
assertEquals("b", MDC.get("a"))
119+
}
120+
}
121+
122+
/** Tests capturing and restoring the MDC context. */
123+
@Test
124+
fun testRestoringMdcContext() = runTest {
125+
MDC.put("a", "b")
126+
val contextMap = withContext(MDCContext()) {
127+
MDC.put("key", "value")
128+
assertEquals("b", MDC.get("a"))
129+
withContext(MDCContext()) {
130+
assertEquals("value", MDC.get("key"))
131+
MDC.put("key2", "value2")
132+
assertEquals("value2", MDC.get("key2"))
133+
withContext(MDCContext()) {
134+
yield()
135+
MDC.getCopyOfContextMap()
136+
}
137+
}
138+
}
139+
MDC.setContextMap(contextMap)
140+
assertEquals("value2", MDC.get("key2"))
141+
assertEquals("value", MDC.get("key"))
142+
assertEquals("b", MDC.get("a"))
143+
}
108144
}

kotlinx-coroutines-core/common/src/Await.kt

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ import kotlin.coroutines.*
1111
* This function is **not** equivalent to `deferreds.map { it.await() }` which fails only when it sequentially
1212
* gets to wait for the failing deferred, while this `awaitAll` fails immediately as soon as any of the deferreds fail.
1313
*
14-
* This suspending function is cancellable.
15-
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting,
16-
* this function immediately resumes with [CancellationException].
17-
* There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
18-
* suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
14+
* This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
15+
* suspending function is waiting, this function immediately resumes with [CancellationException].
16+
* There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled
17+
* while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
1918
*/
2019
public suspend fun <T> awaitAll(vararg deferreds: Deferred<T>): List<T> =
2120
if (deferreds.isEmpty()) emptyList() else AwaitAll(deferreds).await()
@@ -28,11 +27,10 @@ public suspend fun <T> awaitAll(vararg deferreds: Deferred<T>): List<T> =
2827
* This function is **not** equivalent to `this.map { it.await() }` which fails only when it sequentially
2928
* gets to wait for the failing deferred, while this `awaitAll` fails immediately as soon as any of the deferreds fail.
3029
*
31-
* This suspending function is cancellable.
32-
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting,
33-
* this function immediately resumes with [CancellationException].
34-
* There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
35-
* suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
30+
* This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
31+
* suspending function is waiting, this function immediately resumes with [CancellationException].
32+
* There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled
33+
* while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
3634
*/
3735
public suspend fun <T> Collection<Deferred<T>>.awaitAll(): List<T> =
3836
if (isEmpty()) emptyList() else AwaitAll(toTypedArray()).await()
@@ -41,23 +39,21 @@ public suspend fun <T> Collection<Deferred<T>>.awaitAll(): List<T> =
4139
* Suspends current coroutine until all given jobs are complete.
4240
* This method is semantically equivalent to joining all given jobs one by one with `jobs.forEach { it.join() }`.
4341
*
44-
* This suspending function is cancellable.
45-
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting,
46-
* this function immediately resumes with [CancellationException].
47-
* There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
48-
* suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
42+
* This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
43+
* suspending function is waiting, this function immediately resumes with [CancellationException].
44+
* There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled
45+
* while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
4946
*/
5047
public suspend fun joinAll(vararg jobs: Job): Unit = jobs.forEach { it.join() }
5148

5249
/**
5350
* Suspends current coroutine until all given jobs are complete.
5451
* This method is semantically equivalent to joining all given jobs one by one with `forEach { it.join() }`.
5552
*
56-
* This suspending function is cancellable.
57-
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting,
58-
* this function immediately resumes with [CancellationException].
59-
* There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
60-
* suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
53+
* This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
54+
* suspending function is waiting, this function immediately resumes with [CancellationException].
55+
* There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled
56+
* while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
6157
*/
6258
public suspend fun Collection<Job>.joinAll(): Unit = forEach { it.join() }
6359

kotlinx-coroutines-core/common/src/CancellableContinuation.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -238,25 +238,25 @@ public interface CancellableContinuation<in T> : Continuation<T> {
238238
*
239239
* This function provides **prompt cancellation guarantee**.
240240
* If the [Job] of the current coroutine was cancelled while this function was suspended it will not resume
241-
* successfully.
241+
* successfully, even if [CancellableContinuation.resume] was already invoked.
242242
*
243243
* The cancellation of the coroutine's job is generally asynchronous with respect to the suspended coroutine.
244-
* The suspended coroutine is resumed with the call it to its [Continuation.resumeWith] member function or to
244+
* The suspended coroutine is resumed with a call to its [Continuation.resumeWith] member function or to the
245245
* [resume][Continuation.resume] extension function.
246246
* However, when coroutine is resumed, it does not immediately start executing, but is passed to its
247247
* [CoroutineDispatcher] to schedule its execution when dispatcher's resources become available for execution.
248-
* The job's cancellation can happen both before, after, and concurrently with the call to `resume`. In any
249-
* case, prompt cancellation guarantees that the the coroutine will not resume its code successfully.
248+
* The job's cancellation can happen before, after, and concurrently with the call to `resume`. In any
249+
* case, prompt cancellation guarantees that the coroutine will not resume its code successfully.
250250
*
251251
* If the coroutine was resumed with an exception (for example, using [Continuation.resumeWithException] extension
252-
* function) and cancelled, then the resulting exception of the `suspendCancellableCoroutine` function is determined
253-
* by whichever action (exceptional resume or cancellation) that happened first.
252+
* function) and cancelled, then the exception thrown by the `suspendCancellableCoroutine` function is determined
253+
* by what happened first: exceptional resume or cancellation.
254254
*
255255
* ### Returning resources from a suspended coroutine
256256
*
257-
* As a result of a prompt cancellation guarantee, when a closeable resource
258-
* (like open file or a handle to another native resource) is returned from a suspended coroutine as a value
259-
* it can be lost when the coroutine is cancelled. In order to ensure that the resource can be properly closed
257+
* As a result of the prompt cancellation guarantee, when a closeable resource
258+
* (like open file or a handle to another native resource) is returned from a suspended coroutine as a value,
259+
* it can be lost when the coroutine is cancelled. To ensure that the resource can be properly closed
260260
* in this case, the [CancellableContinuation] interface provides two functions.
261261
*
262262
* - [invokeOnCancellation][CancellableContinuation.invokeOnCancellation] installs a handler that is called

kotlinx-coroutines-core/common/src/CoroutineScope.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,13 @@ public object GlobalScope : CoroutineScope {
220220

221221
/**
222222
* Creates a [CoroutineScope] and calls the specified suspend block with this scope.
223-
* The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides
224-
* the context's [Job].
223+
* The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, using the
224+
* [Job] from that context as the parent for a new [Job].
225225
*
226226
* This function is designed for _concurrent decomposition_ of work. When any child coroutine in this scope fails,
227-
* this scope fails and all the rest of the children are cancelled (for a different behavior see [supervisorScope]).
228-
* This function returns as soon as the given block and all its children coroutines are completed.
229-
* A usage example of a scope looks like this:
227+
* this scope fails, cancelling all the other children (for a different behavior, see [supervisorScope]).
228+
* This function returns as soon as the given block and all its child coroutines are completed.
229+
* A usage of a scope looks like this:
230230
*
231231
* ```
232232
* suspend fun showSomeData() = coroutineScope {
@@ -248,8 +248,8 @@ public object GlobalScope : CoroutineScope {
248248
* 3) If the outer scope of `showSomeData` is cancelled, both started `async` and `withContext` blocks are cancelled.
249249
* 4) If the `async` block fails, `withContext` will be cancelled.
250250
*
251-
* The method may throw a [CancellationException] if the current job was cancelled externally
252-
* or may throw a corresponding unhandled [Throwable] if there is any unhandled exception in this scope
251+
* The method may throw a [CancellationException] if the current job was cancelled externally,
252+
* rethrow the exception thrown by [block], or throw an unhandled [Throwable] if there is one
253253
* (for example, from a crashed coroutine that was started with [launch][CoroutineScope.launch] in this scope).
254254
*/
255255
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {

kotlinx-coroutines-core/common/src/Deferred.kt

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,46 @@ import kotlinx.coroutines.selects.*
3333
public interface Deferred<out T> : Job {
3434

3535
/**
36-
* Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete,
37-
* returning the resulting value or throwing the corresponding exception if the deferred was cancelled.
36+
* Awaits for completion of this value without blocking the thread and returns the resulting value or throws
37+
* the exception if the deferred was cancelled.
3838
*
39-
* This suspending function is cancellable.
40-
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
41-
* immediately resumes with [CancellationException].
42-
* There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
43-
* suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
39+
* Unless the calling coroutine is cancelled, [await] will return the same result on each invocation:
40+
* if the [Deferred] completed successfully, [await] will return the same value every time;
41+
* if the [Deferred] completed exceptionally, [await] will rethrow the same exception.
4442
*
45-
* This function can be used in [select] invocation with [onAwait] clause.
46-
* Use [isCompleted] to check for completion of this deferred value without waiting.
43+
* This suspending function is itself cancellable: if the [Job] of the current coroutine is cancelled or completed
44+
* while this suspending function is waiting, this function immediately resumes with [CancellationException].
45+
*
46+
* This means that [await] can throw [CancellationException] in two cases:
47+
* - if the coroutine in which [await] was called got cancelled,
48+
* - or if the [Deferred] itself got completed with a [CancellationException].
49+
*
50+
* In both cases, the [CancellationException] will cancel the coroutine calling [await], unless it's caught.
51+
* The following idiom may be helpful to avoid this:
52+
* ```
53+
* try {
54+
* deferred.await()
55+
* } catch (e: CancellationException) {
56+
* currentCoroutineContext().ensureActive() // throws if the current coroutine was cancelled
57+
* processException(e) // if this line executes, the exception is the result of `await` itself
58+
* }
59+
* ```
60+
*
61+
* There is a **prompt cancellation guarantee**: even if this function is ready to return the result, but was cancelled
62+
* while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
63+
*
64+
* This function can be used in [select] invocations with an [onAwait] clause.
65+
* Use [isCompleted] to check for completion of this deferred value without waiting, and
66+
* [join] to wait for completion without returning the result.
4767
*/
4868
public suspend fun await(): T
4969

5070
/**
51-
* Clause for [select] expression of [await] suspending function that selects with the deferred value when it is
52-
* resolved. The [select] invocation fails if the deferred value completes exceptionally (either fails or
53-
* it cancelled).
71+
* Clause using the [await] suspending function as a [select] clause.
72+
* It selects with the deferred value when the [Deferred] completes.
73+
* If [Deferred] completes with an exception, the whole the [select] invocation fails with the same exception.
74+
* Note that, if [Deferred] completed with a [CancellationException], throwing it may have unintended
75+
* consequences. See [await] for details.
5476
*/
5577
public val onAwait: SelectClause1<T>
5678

0 commit comments

Comments
 (0)