Skip to content

Commit f38b9c2

Browse files
committed
Improve the documentation and tests
1 parent ab181d2 commit f38b9c2

8 files changed

+256
-183
lines changed

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

+69-47
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@ import kotlin.coroutines.*
66
* Defines elements in [CoroutineContext] that are installed into thread context
77
* every time the coroutine with this element in the context is resumed on a thread.
88
*
9-
* Implementations of this interface define a type [S] of the thread-local state that they need to store on
10-
* resume of a coroutine and restore later on suspend. The infrastructure provides the corresponding storage.
9+
* In this context, by a "thread" we mean an environment where coroutines are executed in parallel to coroutines
10+
* other threads.
11+
* On JVM and Native, this is the same as an operating system thread.
12+
* On JS, Wasm/JS, and Wasm/WASI, because coroutines can not actually execute in parallel,
13+
* we say that there is a single thread running all coroutines.
14+
*
15+
* Implementations of this interface define a type [S] of the thread-local state that they need to store
16+
* when the coroutine is resumed and restore later on when it suspends.
17+
* The coroutines infrastructure provides the corresponding storage.
1118
*
1219
* Example usage looks like this:
1320
*
1421
* ```
15-
* // Appends "name" of a coroutine to a current thread name when coroutine is executed
22+
* // Appends "name" of a coroutine to the current thread name when a coroutine is executed
1623
* class CoroutineName(val name: String) : ThreadContextElement<String> {
1724
* // declare companion object for a key of this element in coroutine context
1825
* companion object Key : CoroutineContext.Key<CoroutineName>
@@ -21,14 +28,14 @@ import kotlin.coroutines.*
2128
* override val key: CoroutineContext.Key<CoroutineName>
2229
* get() = Key
2330
*
24-
* // this is invoked before coroutine is resumed on current thread
31+
* // this is invoked before a coroutine is resumed on the current thread
2532
* override fun updateThreadContext(context: CoroutineContext): String {
2633
* val previousName = Thread.currentThread().name
2734
* Thread.currentThread().name = "$previousName # $name"
2835
* return previousName
2936
* }
3037
*
31-
* // this is invoked after coroutine has suspended on current thread
38+
* // this is invoked after a coroutine has suspended on the current thread
3239
* override fun restoreThreadContext(context: CoroutineContext, oldState: String) {
3340
* Thread.currentThread().name = oldState
3441
* }
@@ -38,13 +45,13 @@ import kotlin.coroutines.*
3845
* launch(Dispatchers.Main + CoroutineName("Progress bar coroutine")) { ... }
3946
* ```
4047
*
41-
* Every time this coroutine is resumed on a thread, UI thread name is updated to
42-
* "UI thread original name # Progress bar coroutine" and the thread name is restored to the original one when
48+
* Every time this coroutine is resumed on a thread, the name of the thread backing [Dispatchers.Main] is updated to
49+
* "UI thread original name # Progress bar coroutine", and the thread name is restored to the original one when
4350
* this coroutine suspends.
4451
*
45-
* To use [ThreadLocal] variable within the coroutine use [ThreadLocal.asContextElement][asContextElement] function.
52+
* On JVM, to use a `ThreadLocal` variable within the coroutine, use the `ThreadLocal.asContextElement` function.
4653
*
47-
* ### Reentrancy and thread-safety
54+
* ### Reentrancy and thread safety
4855
*
4956
* Correct implementations of this interface must expect that calls to [restoreThreadContext]
5057
* may happen in parallel to the subsequent [updateThreadContext] and [restoreThreadContext] operations.
@@ -55,50 +62,65 @@ import kotlin.coroutines.*
5562
*/
5663
public interface ThreadContextElement<S> : CoroutineContext.Element {
5764
/**
58-
* Updates context of the current thread.
59-
* This function is invoked before the coroutine in the specified [context] is resumed in the current thread
60-
* when the context of the coroutine this element.
61-
* The result of this function is the old value of the thread-local state that will be passed to [restoreThreadContext].
62-
* This method should handle its own exceptions and do not rethrow it. Thrown exceptions will leave coroutine which
63-
* context is updated in an undefined state and may crash an application.
65+
* Updates the context of the current thread.
66+
*
67+
* This function is invoked before the coroutine in the specified [context] is started or resumed
68+
* in the current thread when this element is present in the context of the coroutine.
69+
* The result of this function is the old value of the thread-local state
70+
* that will be passed to [restoreThreadContext] when the coroutine eventually suspends or completes.
71+
* This method should handle its own exceptions and not rethrow them.
72+
* Thrown exceptions will leave the coroutine whose context is updated in an undefined state
73+
* and may crash the application.
6474
*
65-
* @param context the coroutine context.
75+
* @param context the context of the coroutine that's being started or resumed.
6676
*/
6777
public fun updateThreadContext(context: CoroutineContext): S
6878

6979
/**
70-
* Restores context of the current thread.
71-
* This function is invoked after the coroutine in the specified [context] is suspended in the current thread
72-
* if [updateThreadContext] was previously invoked on resume of this coroutine.
73-
* The value of [oldState] is the result of the previous invocation of [updateThreadContext] and it should
74-
* be restored in the thread-local state by this function.
75-
* This method should handle its own exceptions and do not rethrow it. Thrown exceptions will leave coroutine which
76-
* context is updated in an undefined state and may crash an application.
80+
* Restores the context of the current thread.
7781
*
78-
* @param context the coroutine context.
79-
* @param oldState the value returned by the previous invocation of [updateThreadContext].
82+
* This function is invoked after the coroutine in the specified [context] has suspended or finished
83+
* in the current thread if [updateThreadContext] was previously invoked when this coroutine was started or resumed.
84+
* [oldState] is the result of the preceding invocation of [updateThreadContext],
85+
* and this value should be restored in the thread-local state by this function.
86+
* This method should handle its own exceptions and not rethrow them.
87+
* Thrown exceptions will leave the coroutine whose context is updated in an undefined state
88+
* and may crash the application.
89+
*
90+
* @param context the context of the coroutine that has suspended or finished.
91+
* @param oldState the value returned by the preceding invocation of [updateThreadContext].
8092
*/
8193
public fun restoreThreadContext(context: CoroutineContext, oldState: S)
8294
}
8395

8496
/**
85-
* A [ThreadContextElement] copied whenever a child coroutine inherits a context containing it.
86-
*
87-
* When an API uses a _mutable_ [ThreadLocal] for consistency, a [CopyableThreadContextElement]
88-
* can give coroutines "coroutine-safe" write access to that `ThreadLocal`.
89-
*
90-
* A write made to a `ThreadLocal` with a matching [CopyableThreadContextElement] by a coroutine
91-
* will be visible to _itself_ and any child coroutine launched _after_ that write.
92-
*
93-
* Writes will not be visible to the parent coroutine, peer coroutines, or coroutines that happen
94-
* to use the same thread. Writes made to the `ThreadLocal` by the parent coroutine _after_
95-
* launching a child coroutine will not be visible to that child coroutine.
96-
*
97-
* This can be used to allow a coroutine to use a mutable ThreadLocal API transparently and
97+
* A [ThreadContextElement] that is copied whenever a child coroutine inherits a context containing it.
98+
*
99+
* [ThreadContextElement] can be used to ensure that when several coroutines share the same thread,
100+
* they can each have their personal (though immutable) thread-local state without affecting the other coroutines.
101+
* Often, however, it is desirable to propagate the thread-local state across coroutine suspensions
102+
* and to child coroutines.
103+
* A [CopyableThreadContextElement] is an instrument for implementing exactly this kind of
104+
* hierarchical mutable thread-local state.
105+
*
106+
* A change made to a thread-local value with a matching [CopyableThreadContextElement] by a coroutine
107+
* will be visible to _itself_ (even after the coroutine suspends and subsequently resumes)
108+
* and any child coroutine launched _after_ that write.
109+
* Changes introduced to the thread-local value by the parent coroutine _after_ launching a child coroutine
110+
* will not be visible to that child coroutine.
111+
* Changes will not be visible to the parent coroutine, peer coroutines, or coroutines that also have
112+
* this [CopyableThreadContextElement] in their context and simply happen to use the same thread.
113+
*
114+
* This can be used to allow a coroutine to use a mutable-thread-local-value-based API transparently and
98115
* correctly, regardless of the coroutine's structured concurrency.
99116
*
100-
* This example adapts a `ThreadLocal` method trace to be "coroutine local" while the method trace
101-
* is in a coroutine:
117+
* The changes *may* be visible to unrelated coroutines that are launched on the same thread if those coroutines
118+
* do not have a [CopyableThreadContextElement] with the same key in their context.
119+
* Because of this, it is an error to access a thread-local value from a coroutine without the corresponding
120+
* [CopyableThreadContextElement] when other coroutines may have modified it.
121+
*
122+
* This example adapts thread-local-value-based method tracing to follow coroutine switches and child coroutine creation.
123+
* when the tracing happens inside a coroutine:
102124
*
103125
* ```
104126
* class TraceContextElement(private val traceData: TraceData?) : CopyableThreadContextElement<TraceData?> {
@@ -117,14 +139,14 @@ public interface ThreadContextElement<S> : CoroutineContext.Element {
117139
* }
118140
*
119141
* override fun copyForChild(): TraceContextElement {
120-
* // Copy from the ThreadLocal source of truth at child coroutine launch time. This makes
121-
* // ThreadLocal writes between resumption of the parent coroutine and the launch of the
142+
* // Copy from the ThreadLocal source of truth at the child coroutine launch time. This makes
143+
* // ThreadLocal writes between the resumption of the parent coroutine and the launch of the
122144
* // child coroutine visible to the child.
123145
* return TraceContextElement(traceThreadLocal.get()?.copy())
124146
* }
125147
*
126148
* override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext {
127-
* // Merge operation defines how to handle situations when both
149+
* // The merge operation defines how to handle situations when both
128150
* // the parent coroutine has an element in the context and
129151
* // an element with the same key was also
130152
* // explicitly passed to the child coroutine.
@@ -135,8 +157,8 @@ public interface ThreadContextElement<S> : CoroutineContext.Element {
135157
* }
136158
* ```
137159
*
138-
* A coroutine using this mechanism can safely call Java code that assumes the corresponding thread local element's
139-
* value is installed into the target thread local.
160+
* A coroutine using this mechanism can safely call coroutine-oblivious code that assumes
161+
* a specific thread local element's value is installed into the target thread local.
140162
*
141163
* ### Reentrancy and thread-safety
142164
*
@@ -164,7 +186,7 @@ public interface ThreadContextElement<S> : CoroutineContext.Element {
164186
public interface CopyableThreadContextElement<S> : ThreadContextElement<S> {
165187

166188
/**
167-
* Returns a [CopyableThreadContextElement] to replace `this` `CopyableThreadContextElement` in the child
189+
* Returns the [CopyableThreadContextElement] to replace `this` `CopyableThreadContextElement` in the child
168190
* coroutine's context that is under construction if the added context does not contain an element with the same [key].
169191
*
170192
* This function is called on the element each time a new coroutine inherits a context containing it,
@@ -176,7 +198,7 @@ public interface CopyableThreadContextElement<S> : ThreadContextElement<S> {
176198
public fun copyForChild(): CopyableThreadContextElement<S>
177199

178200
/**
179-
* Returns a [CopyableThreadContextElement] to replace `this` `CopyableThreadContextElement` in the child
201+
* Returns the [CopyableThreadContextElement] to replace `this` `CopyableThreadContextElement` in the child
180202
* coroutine's context that is under construction if the added context does contain an element with the same [key].
181203
*
182204
* This method is invoked on the original element, accepting as the parameter

0 commit comments

Comments
 (0)