Skip to content

Commit 0776bb6

Browse files
committed
Improve the documentation and tests
1 parent 0423482 commit 0776bb6

8 files changed

+255
-183
lines changed

Diff for: kotlinx-coroutines-core/common/src/ThreadContextElement.common.kt

+68-47
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@ import kotlin.coroutines.*
66
* Defines elements in a [CoroutineContext] that are installed into the thread context
77
* every time the coroutine with this element in the context is resumed on a thread.
88
*
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+
*
915
* Implementations of this interface define a type [S] of the thread-local state that they need to store
10-
* upon resuming a coroutine and restore later upon suspension.
11-
* The infrastructure provides the corresponding storage.
16+
* when the coroutine is resumed and restore later on when it suspends.
17+
* The coroutines infrastructure provides the corresponding storage.
1218
*
1319
* Example usage looks like this:
1420
*
1521
* ```
16-
* // 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
1723
* class CoroutineName(val name: String) : ThreadContextElement<String> {
1824
* // declare companion object for a key of this element in coroutine context
1925
* companion object Key : CoroutineContext.Key<CoroutineName>
@@ -22,14 +28,14 @@ import kotlin.coroutines.*
2228
* override val key: CoroutineContext.Key<CoroutineName>
2329
* get() = Key
2430
*
25-
* // this is invoked before coroutine is resumed on current thread
31+
* // this is invoked before a coroutine is resumed on the current thread
2632
* override fun updateThreadContext(context: CoroutineContext): String {
2733
* val previousName = Thread.currentThread().name
2834
* Thread.currentThread().name = "$previousName # $name"
2935
* return previousName
3036
* }
3137
*
32-
* // this is invoked after coroutine has suspended on current thread
38+
* // this is invoked after a coroutine has suspended on the current thread
3339
* override fun restoreThreadContext(context: CoroutineContext, oldState: String) {
3440
* Thread.currentThread().name = oldState
3541
* }
@@ -39,13 +45,13 @@ import kotlin.coroutines.*
3945
* launch(Dispatchers.Main + CoroutineName("Progress bar coroutine")) { ... }
4046
* ```
4147
*
42-
* Every time this coroutine is resumed on a thread, UI thread name is updated to
43-
* "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
4450
* this coroutine suspends.
4551
*
46-
* 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.
4753
*
48-
* ### Reentrancy and thread-safety
54+
* ### Reentrancy and thread safety
4955
*
5056
* Correct implementations of this interface must expect that calls to [restoreThreadContext]
5157
* may happen in parallel to the subsequent [updateThreadContext] and [restoreThreadContext] operations.
@@ -56,50 +62,65 @@ import kotlin.coroutines.*
5662
*/
5763
public interface ThreadContextElement<S> : CoroutineContext.Element {
5864
/**
59-
* Updates context of the current thread.
60-
* This function is invoked before the coroutine in the specified [context] is resumed in the current thread
61-
* when the context of the coroutine this element.
62-
* The result of this function is the old value of the thread-local state that will be passed to [restoreThreadContext].
63-
* This method should handle its own exceptions and do not rethrow it. Thrown exceptions will leave coroutine which
64-
* 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.
6574
*
66-
* @param context the coroutine context.
75+
* @param context the context of the coroutine that's being started or resumed.
6776
*/
6877
public fun updateThreadContext(context: CoroutineContext): S
6978

7079
/**
71-
* Restores context of the current thread.
72-
* This function is invoked after the coroutine in the specified [context] is suspended in the current thread
73-
* if [updateThreadContext] was previously invoked on resume of this coroutine.
74-
* The value of [oldState] is the result of the previous invocation of [updateThreadContext] and it should
75-
* be restored in the thread-local state by this function.
76-
* This method should handle its own exceptions and do not rethrow it. Thrown exceptions will leave coroutine which
77-
* context is updated in an undefined state and may crash an application.
80+
* Restores the context of the current thread.
7881
*
79-
* @param context the coroutine context.
80-
* @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].
8192
*/
8293
public fun restoreThreadContext(context: CoroutineContext, oldState: S)
8394
}
8495

8596
/**
86-
* A [ThreadContextElement] copied whenever a child coroutine inherits a context containing it.
87-
*
88-
* When an API uses a _mutable_ [ThreadLocal] for consistency, a [CopyableThreadContextElement]
89-
* can give coroutines "coroutine-safe" write access to that `ThreadLocal`.
90-
*
91-
* A write made to a `ThreadLocal` with a matching [CopyableThreadContextElement] by a coroutine
92-
* will be visible to _itself_ and any child coroutine launched _after_ that write.
93-
*
94-
* Writes will not be visible to the parent coroutine, peer coroutines, or coroutines that happen
95-
* to use the same thread. Writes made to the `ThreadLocal` by the parent coroutine _after_
96-
* launching a child coroutine will not be visible to that child coroutine.
97-
*
98-
* 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
99115
* correctly, regardless of the coroutine's structured concurrency.
100116
*
101-
* This example adapts a `ThreadLocal` method trace to be "coroutine local" while the method trace
102-
* 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:
103124
*
104125
* ```
105126
* class TraceContextElement(private val traceData: TraceData?) : CopyableThreadContextElement<TraceData?> {
@@ -118,14 +139,14 @@ public interface ThreadContextElement<S> : CoroutineContext.Element {
118139
* }
119140
*
120141
* override fun copyForChild(): TraceContextElement {
121-
* // Copy from the ThreadLocal source of truth at child coroutine launch time. This makes
122-
* // 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
123144
* // child coroutine visible to the child.
124145
* return TraceContextElement(traceThreadLocal.get()?.copy())
125146
* }
126147
*
127148
* override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext {
128-
* // Merge operation defines how to handle situations when both
149+
* // The merge operation defines how to handle situations when both
129150
* // the parent coroutine has an element in the context and
130151
* // an element with the same key was also
131152
* // explicitly passed to the child coroutine.
@@ -136,8 +157,8 @@ public interface ThreadContextElement<S> : CoroutineContext.Element {
136157
* }
137158
* ```
138159
*
139-
* A coroutine using this mechanism can safely call Java code that assumes the corresponding thread local element's
140-
* 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.
141162
*
142163
* ### Reentrancy and thread-safety
143164
*
@@ -165,7 +186,7 @@ public interface ThreadContextElement<S> : CoroutineContext.Element {
165186
public interface CopyableThreadContextElement<S> : ThreadContextElement<S> {
166187

167188
/**
168-
* Returns a [CopyableThreadContextElement] to replace `this` `CopyableThreadContextElement` in the child
189+
* Returns the [CopyableThreadContextElement] to replace `this` `CopyableThreadContextElement` in the child
169190
* coroutine's context that is under construction if the added context does not contain an element with the same [key].
170191
*
171192
* This function is called on the element each time a new coroutine inherits a context containing it,
@@ -177,7 +198,7 @@ public interface CopyableThreadContextElement<S> : ThreadContextElement<S> {
177198
public fun copyForChild(): CopyableThreadContextElement<S>
178199

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

0 commit comments

Comments
 (0)