1<!--- TEST_NAME DispatcherGuideTest --> 2 3**Table of contents** 4 5<!--- TOC --> 6 7* [Coroutine Context and Dispatchers](#coroutine-context-and-dispatchers) 8 * [Dispatchers and threads](#dispatchers-and-threads) 9 * [Unconfined vs confined dispatcher](#unconfined-vs-confined-dispatcher) 10 * [Debugging coroutines and threads](#debugging-coroutines-and-threads) 11 * [Debugging with IDEA](#debugging-with-idea) 12 * [Debugging using logging](#debugging-using-logging) 13 * [Jumping between threads](#jumping-between-threads) 14 * [Job in the context](#job-in-the-context) 15 * [Children of a coroutine](#children-of-a-coroutine) 16 * [Parental responsibilities](#parental-responsibilities) 17 * [Naming coroutines for debugging](#naming-coroutines-for-debugging) 18 * [Combining context elements](#combining-context-elements) 19 * [Coroutine scope](#coroutine-scope) 20 * [Thread-local data](#thread-local-data) 21 22<!--- END --> 23 24## Coroutine Context and Dispatchers 25 26Coroutines always execute in some context represented by a value of the 27[CoroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-coroutine-context/) 28type, defined in the Kotlin standard library. 29 30The coroutine context is a set of various elements. The main elements are the [Job] of the coroutine, 31which we've seen before, and its dispatcher, which is covered in this section. 32 33### Dispatchers and threads 34 35The coroutine context includes a _coroutine dispatcher_ (see [CoroutineDispatcher]) that determines what thread or threads 36the corresponding coroutine uses for its execution. The coroutine dispatcher can confine coroutine execution 37to a specific thread, dispatch it to a thread pool, or let it run unconfined. 38 39All coroutine builders like [launch] and [async] accept an optional 40[CoroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-coroutine-context/) 41parameter that can be used to explicitly specify the dispatcher for the new coroutine and other context elements. 42 43Try the following example: 44 45<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 46 47```kotlin 48import kotlinx.coroutines.* 49 50fun main() = runBlocking<Unit> { 51//sampleStart 52 launch { // context of the parent, main runBlocking coroutine 53 println("main runBlocking : I'm working in thread ${Thread.currentThread().name}") 54 } 55 launch(Dispatchers.Unconfined) { // not confined -- will work with main thread 56 println("Unconfined : I'm working in thread ${Thread.currentThread().name}") 57 } 58 launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher 59 println("Default : I'm working in thread ${Thread.currentThread().name}") 60 } 61 launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread 62 println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}") 63 } 64//sampleEnd 65} 66``` 67 68</div> 69 70> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt). 71 72It produces the following output (maybe in different order): 73 74```text 75Unconfined : I'm working in thread main 76Default : I'm working in thread DefaultDispatcher-worker-1 77newSingleThreadContext: I'm working in thread MyOwnThread 78main runBlocking : I'm working in thread main 79``` 80 81<!--- TEST LINES_START_UNORDERED --> 82 83When `launch { ... }` is used without parameters, it inherits the context (and thus dispatcher) 84from the [CoroutineScope] it is being launched from. In this case, it inherits the 85context of the main `runBlocking` coroutine which runs in the `main` thread. 86 87[Dispatchers.Unconfined] is a special dispatcher that also appears to run in the `main` thread, but it is, 88in fact, a different mechanism that is explained later. 89 90The default dispatcher that is used when coroutines are launched in [GlobalScope] 91is represented by [Dispatchers.Default] and uses a shared background pool of threads, 92so `launch(Dispatchers.Default) { ... }` uses the same dispatcher as `GlobalScope.launch { ... }`. 93 94[newSingleThreadContext] creates a thread for the coroutine to run. 95A dedicated thread is a very expensive resource. 96In a real application it must be either released, when no longer needed, using the [close][ExecutorCoroutineDispatcher.close] 97function, or stored in a top-level variable and reused throughout the application. 98 99### Unconfined vs confined dispatcher 100 101The [Dispatchers.Unconfined] coroutine dispatcher starts a coroutine in the caller thread, but only until the 102first suspension point. After suspension it resumes the coroutine in the thread that is fully determined by the 103suspending function that was invoked. The unconfined dispatcher is appropriate for coroutines which neither 104consume CPU time nor update any shared data (like UI) confined to a specific thread. 105 106On the other side, the dispatcher is inherited from the outer [CoroutineScope] by default. 107The default dispatcher for the [runBlocking] coroutine, in particular, 108is confined to the invoker thread, so inheriting it has the effect of confining execution to 109this thread with predictable FIFO scheduling. 110 111<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 112 113```kotlin 114import kotlinx.coroutines.* 115 116fun main() = runBlocking<Unit> { 117//sampleStart 118 launch(Dispatchers.Unconfined) { // not confined -- will work with main thread 119 println("Unconfined : I'm working in thread ${Thread.currentThread().name}") 120 delay(500) 121 println("Unconfined : After delay in thread ${Thread.currentThread().name}") 122 } 123 launch { // context of the parent, main runBlocking coroutine 124 println("main runBlocking: I'm working in thread ${Thread.currentThread().name}") 125 delay(1000) 126 println("main runBlocking: After delay in thread ${Thread.currentThread().name}") 127 } 128//sampleEnd 129} 130``` 131 132</div> 133 134> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt). 135 136Produces the output: 137 138```text 139Unconfined : I'm working in thread main 140main runBlocking: I'm working in thread main 141Unconfined : After delay in thread kotlinx.coroutines.DefaultExecutor 142main runBlocking: After delay in thread main 143``` 144 145<!--- TEST LINES_START --> 146 147So, the coroutine with the context inherited from `runBlocking {...}` continues to execute 148in the `main` thread, while the unconfined one resumes in the default executor thread that the [delay] 149function is using. 150 151> The unconfined dispatcher is an advanced mechanism that can be helpful in certain corner cases where 152dispatching of a coroutine for its execution later is not needed or produces undesirable side-effects, 153because some operation in a coroutine must be performed right away. 154The unconfined dispatcher should not be used in general code. 155 156### Debugging coroutines and threads 157 158Coroutines can suspend on one thread and resume on another thread. 159Even with a single-threaded dispatcher it might be hard to 160figure out what the coroutine was doing, where, and when if you don't have special tooling. 161 162#### Debugging with IDEA 163 164The Coroutine Debugger of the Kotlin plugin simplifies debugging coroutines in IntelliJ IDEA. 165 166> Debugging works for versions 1.3.8 or later of `kotlinx-coroutines-core`. 167 168The **Debug** tool window contains the **Coroutines** tab. In this tab, you can find information about both currently running and suspended coroutines. 169The coroutines are grouped by the dispatcher they are running on. 170 171![Debugging coroutines](images/coroutine-idea-debugging-1.png) 172 173With the coroutine debugger, you can: 174* Check the state of each coroutine. 175* See the values of local and captured variables for both running and suspended coroutines. 176* See a full coroutine creation stack, as well as a call stack inside the coroutine. The stack includes all frames with 177variable values, even those that would be lost during standard debugging. 178* Get a full report that contains the state of each coroutine and its stack. To obtain it, right-click inside the **Coroutines** tab, and then click **Get Coroutines Dump**. 179 180To start coroutine debugging, you just need to set breakpoints and run the application in debug mode. 181 182Learn more about coroutines debugging in the [tutorial](https://kotlinlang.org/docs/tutorials/coroutines/debug-coroutines-with-idea.html). 183 184#### Debugging using logging 185 186Another approach to debugging applications with 187threads without Coroutine Debugger is to print the thread name in the log file on each log statement. This feature is universally supported 188by logging frameworks. When using coroutines, the thread name alone does not give much of a context, so 189`kotlinx.coroutines` includes debugging facilities to make it easier. 190 191Run the following code with `-Dkotlinx.coroutines.debug` JVM option: 192 193<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 194 195```kotlin 196import kotlinx.coroutines.* 197 198fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") 199 200fun main() = runBlocking<Unit> { 201//sampleStart 202 val a = async { 203 log("I'm computing a piece of the answer") 204 6 205 } 206 val b = async { 207 log("I'm computing another piece of the answer") 208 7 209 } 210 log("The answer is ${a.await() * b.await()}") 211//sampleEnd 212} 213``` 214 215</div> 216 217> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt). 218 219There are three coroutines. The main coroutine (#1) inside `runBlocking` 220and two coroutines computing the deferred values `a` (#2) and `b` (#3). 221They are all executing in the context of `runBlocking` and are confined to the main thread. 222The output of this code is: 223 224```text 225[main @coroutine#2] I'm computing a piece of the answer 226[main @coroutine#3] I'm computing another piece of the answer 227[main @coroutine#1] The answer is 42 228``` 229 230<!--- TEST FLEXIBLE_THREAD --> 231 232The `log` function prints the name of the thread in square brackets, and you can see that it is the `main` 233thread with the identifier of the currently executing coroutine appended to it. This identifier 234is consecutively assigned to all created coroutines when the debugging mode is on. 235 236> Debugging mode is also turned on when JVM is run with `-ea` option. 237You can read more about debugging facilities in the documentation of the [DEBUG_PROPERTY_NAME] property. 238 239### Jumping between threads 240 241Run the following code with the `-Dkotlinx.coroutines.debug` JVM option (see [debug](#debugging-coroutines-and-threads)): 242 243<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 244 245```kotlin 246import kotlinx.coroutines.* 247 248fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") 249 250fun main() { 251//sampleStart 252 newSingleThreadContext("Ctx1").use { ctx1 -> 253 newSingleThreadContext("Ctx2").use { ctx2 -> 254 runBlocking(ctx1) { 255 log("Started in ctx1") 256 withContext(ctx2) { 257 log("Working in ctx2") 258 } 259 log("Back to ctx1") 260 } 261 } 262 } 263//sampleEnd 264} 265``` 266 267</div> 268 269> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt). 270 271It demonstrates several new techniques. One is using [runBlocking] with an explicitly specified context, and 272the other one is using the [withContext] function to change the context of a coroutine while still staying in the 273same coroutine, as you can see in the output below: 274 275```text 276[Ctx1 @coroutine#1] Started in ctx1 277[Ctx2 @coroutine#1] Working in ctx2 278[Ctx1 @coroutine#1] Back to ctx1 279``` 280 281<!--- TEST --> 282 283Note that this example also uses the `use` function from the Kotlin standard library to release threads 284created with [newSingleThreadContext] when they are no longer needed. 285 286### Job in the context 287 288The coroutine's [Job] is part of its context, and can be retrieved from it 289using the `coroutineContext[Job]` expression: 290 291<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 292 293```kotlin 294import kotlinx.coroutines.* 295 296fun main() = runBlocking<Unit> { 297//sampleStart 298 println("My job is ${coroutineContext[Job]}") 299//sampleEnd 300} 301``` 302 303</div> 304 305> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt). 306 307In the [debug mode](#debugging-coroutines-and-threads), it outputs something like this: 308 309``` 310My job is "coroutine#1":BlockingCoroutine{Active}@6d311334 311``` 312 313<!--- TEST lines.size == 1 && lines[0].startsWith("My job is \"coroutine#1\":BlockingCoroutine{Active}@") --> 314 315Note that [isActive] in [CoroutineScope] is just a convenient shortcut for 316`coroutineContext[Job]?.isActive == true`. 317 318### Children of a coroutine 319 320When a coroutine is launched in the [CoroutineScope] of another coroutine, 321it inherits its context via [CoroutineScope.coroutineContext] and 322the [Job] of the new coroutine becomes 323a _child_ of the parent coroutine's job. When the parent coroutine is cancelled, all its children 324are recursively cancelled, too. 325 326However, when [GlobalScope] is used to launch a coroutine, there is no parent for the job of the new coroutine. 327It is therefore not tied to the scope it was launched from and operates independently. 328 329 330<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 331 332```kotlin 333import kotlinx.coroutines.* 334 335fun main() = runBlocking<Unit> { 336//sampleStart 337 // launch a coroutine to process some kind of incoming request 338 val request = launch { 339 // it spawns two other jobs, one with GlobalScope 340 GlobalScope.launch { 341 println("job1: I run in GlobalScope and execute independently!") 342 delay(1000) 343 println("job1: I am not affected by cancellation of the request") 344 } 345 // and the other inherits the parent context 346 launch { 347 delay(100) 348 println("job2: I am a child of the request coroutine") 349 delay(1000) 350 println("job2: I will not execute this line if my parent request is cancelled") 351 } 352 } 353 delay(500) 354 request.cancel() // cancel processing of the request 355 delay(1000) // delay a second to see what happens 356 println("main: Who has survived request cancellation?") 357//sampleEnd 358} 359``` 360 361</div> 362 363> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt). 364 365The output of this code is: 366 367```text 368job1: I run in GlobalScope and execute independently! 369job2: I am a child of the request coroutine 370job1: I am not affected by cancellation of the request 371main: Who has survived request cancellation? 372``` 373 374<!--- TEST --> 375 376### Parental responsibilities 377 378A parent coroutine always waits for completion of all its children. A parent does not have to explicitly track 379all the children it launches, and it does not have to use [Job.join] to wait for them at the end: 380 381<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 382 383```kotlin 384import kotlinx.coroutines.* 385 386fun main() = runBlocking<Unit> { 387//sampleStart 388 // launch a coroutine to process some kind of incoming request 389 val request = launch { 390 repeat(3) { i -> // launch a few children jobs 391 launch { 392 delay((i + 1) * 200L) // variable delay 200ms, 400ms, 600ms 393 println("Coroutine $i is done") 394 } 395 } 396 println("request: I'm done and I don't explicitly join my children that are still active") 397 } 398 request.join() // wait for completion of the request, including all its children 399 println("Now processing of the request is complete") 400//sampleEnd 401} 402``` 403 404</div> 405 406> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt). 407 408The result is going to be: 409 410```text 411request: I'm done and I don't explicitly join my children that are still active 412Coroutine 0 is done 413Coroutine 1 is done 414Coroutine 2 is done 415Now processing of the request is complete 416``` 417 418<!--- TEST --> 419 420### Naming coroutines for debugging 421 422Automatically assigned ids are good when coroutines log often and you just need to correlate log records 423coming from the same coroutine. However, when a coroutine is tied to the processing of a specific request 424or doing some specific background task, it is better to name it explicitly for debugging purposes. 425The [CoroutineName] context element serves the same purpose as the thread name. It is included in the thread name that 426is executing this coroutine when the [debugging mode](#debugging-coroutines-and-threads) is turned on. 427 428The following example demonstrates this concept: 429 430<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 431 432```kotlin 433import kotlinx.coroutines.* 434 435fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") 436 437fun main() = runBlocking(CoroutineName("main")) { 438//sampleStart 439 log("Started main coroutine") 440 // run two background value computations 441 val v1 = async(CoroutineName("v1coroutine")) { 442 delay(500) 443 log("Computing v1") 444 252 445 } 446 val v2 = async(CoroutineName("v2coroutine")) { 447 delay(1000) 448 log("Computing v2") 449 6 450 } 451 log("The answer for v1 / v2 = ${v1.await() / v2.await()}") 452//sampleEnd 453} 454``` 455 456</div> 457 458> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt). 459 460The output it produces with `-Dkotlinx.coroutines.debug` JVM option is similar to: 461 462```text 463[main @main#1] Started main coroutine 464[main @v1coroutine#2] Computing v1 465[main @v2coroutine#3] Computing v2 466[main @main#1] The answer for v1 / v2 = 42 467``` 468 469<!--- TEST FLEXIBLE_THREAD --> 470 471### Combining context elements 472 473Sometimes we need to define multiple elements for a coroutine context. We can use the `+` operator for that. 474For example, we can launch a coroutine with an explicitly specified dispatcher and an explicitly specified 475name at the same time: 476 477<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 478 479```kotlin 480import kotlinx.coroutines.* 481 482fun main() = runBlocking<Unit> { 483//sampleStart 484 launch(Dispatchers.Default + CoroutineName("test")) { 485 println("I'm working in thread ${Thread.currentThread().name}") 486 } 487//sampleEnd 488} 489``` 490 491</div> 492 493> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt). 494 495The output of this code with the `-Dkotlinx.coroutines.debug` JVM option is: 496 497```text 498I'm working in thread DefaultDispatcher-worker-1 @test#2 499``` 500 501<!--- TEST FLEXIBLE_THREAD --> 502 503### Coroutine scope 504 505Let us put our knowledge about contexts, children and jobs together. Assume that our application has 506an object with a lifecycle, but that object is not a coroutine. For example, we are writing an Android application 507and launch various coroutines in the context of an Android activity to perform asynchronous operations to fetch 508and update data, do animations, etc. All of these coroutines must be cancelled when the activity is destroyed 509to avoid memory leaks. We, of course, can manipulate contexts and jobs manually to tie the lifecycles of the activity 510and its coroutines, but `kotlinx.coroutines` provides an abstraction encapsulating that: [CoroutineScope]. 511You should be already familiar with the coroutine scope as all coroutine builders are declared as extensions on it. 512 513We manage the lifecycles of our coroutines by creating an instance of [CoroutineScope] tied to 514the lifecycle of our activity. A `CoroutineScope` instance can be created by the [CoroutineScope()] or [MainScope()] 515factory functions. The former creates a general-purpose scope, while the latter creates a scope for UI applications and uses 516[Dispatchers.Main] as the default dispatcher: 517 518<div class="sample" markdown="1" theme="idea" data-highlight-only> 519 520```kotlin 521class Activity { 522 private val mainScope = MainScope() 523 524 fun destroy() { 525 mainScope.cancel() 526 } 527 // to be continued ... 528``` 529 530</div> 531 532Now, we can launch coroutines in the scope of this `Activity` using the defined `scope`. 533For the demo, we launch ten coroutines that delay for a different time: 534 535<div class="sample" markdown="1" theme="idea" data-highlight-only> 536 537```kotlin 538 // class Activity continues 539 fun doSomething() { 540 // launch ten coroutines for a demo, each working for a different time 541 repeat(10) { i -> 542 mainScope.launch { 543 delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc 544 println("Coroutine $i is done") 545 } 546 } 547 } 548} // class Activity ends 549``` 550 551</div> 552 553In our main function we create the activity, call our test `doSomething` function, and destroy the activity after 500ms. 554This cancels all the coroutines that were launched from `doSomething`. We can see that because after the destruction 555of the activity no more messages are printed, even if we wait a little longer. 556 557<!--- CLEAR --> 558 559<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 560 561```kotlin 562import kotlinx.coroutines.* 563 564class Activity { 565 private val mainScope = CoroutineScope(Dispatchers.Default) // use Default for test purposes 566 567 fun destroy() { 568 mainScope.cancel() 569 } 570 571 fun doSomething() { 572 // launch ten coroutines for a demo, each working for a different time 573 repeat(10) { i -> 574 mainScope.launch { 575 delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc 576 println("Coroutine $i is done") 577 } 578 } 579 } 580} // class Activity ends 581 582fun main() = runBlocking<Unit> { 583//sampleStart 584 val activity = Activity() 585 activity.doSomething() // run test function 586 println("Launched coroutines") 587 delay(500L) // delay for half a second 588 println("Destroying activity!") 589 activity.destroy() // cancels all coroutines 590 delay(1000) // visually confirm that they don't work 591//sampleEnd 592} 593``` 594 595</div> 596 597> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt). 598 599The output of this example is: 600 601```text 602Launched coroutines 603Coroutine 0 is done 604Coroutine 1 is done 605Destroying activity! 606``` 607 608<!--- TEST --> 609 610As you can see, only the first two coroutines print a message and the others are cancelled 611by a single invocation of `job.cancel()` in `Activity.destroy()`. 612 613> Note, that Android has first-party support for coroutine scope in all entities with the lifecycle. 614See [the corresponding documentation](https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope). 615 616### Thread-local data 617 618Sometimes it is convenient to have an ability to pass some thread-local data to or between coroutines. 619However, since they are not bound to any particular thread, this will likely lead to boilerplate if done manually. 620 621For [`ThreadLocal`](https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html), 622the [asContextElement] extension function is here for the rescue. It creates an additional context element 623which keeps the value of the given `ThreadLocal` and restores it every time the coroutine switches its context. 624 625It is easy to demonstrate it in action: 626 627<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 628 629```kotlin 630import kotlinx.coroutines.* 631 632val threadLocal = ThreadLocal<String?>() // declare thread-local variable 633 634fun main() = runBlocking<Unit> { 635//sampleStart 636 threadLocal.set("main") 637 println("Pre-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 638 val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "launch")) { 639 println("Launch start, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 640 yield() 641 println("After yield, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 642 } 643 job.join() 644 println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 645//sampleEnd 646} 647``` 648 649</div> 650 651> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt). 652 653In this example we launch a new coroutine in a background thread pool using [Dispatchers.Default], so 654it works on a different thread from the thread pool, but it still has the value of the thread local variable 655that we specified using `threadLocal.asContextElement(value = "launch")`, 656no matter which thread the coroutine is executed on. 657Thus, the output (with [debug](#debugging-coroutines-and-threads)) is: 658 659```text 660Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main' 661Launch start, current thread: Thread[DefaultDispatcher-worker-1 @coroutine#2,5,main], thread local value: 'launch' 662After yield, current thread: Thread[DefaultDispatcher-worker-2 @coroutine#2,5,main], thread local value: 'launch' 663Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main' 664``` 665 666<!--- TEST FLEXIBLE_THREAD --> 667 668It's easy to forget to set the corresponding context element. The thread-local variable accessed from the coroutine may 669then have an unexpected value, if the thread running the coroutine is different. 670To avoid such situations, it is recommended to use the [ensurePresent] method 671and fail-fast on improper usages. 672 673`ThreadLocal` has first-class support and can be used with any primitive `kotlinx.coroutines` provides. 674It has one key limitation, though: when a thread-local is mutated, a new value is not propagated to the coroutine caller 675(because a context element cannot track all `ThreadLocal` object accesses), and the updated value is lost on the next suspension. 676Use [withContext] to update the value of the thread-local in a coroutine, see [asContextElement] for more details. 677 678Alternatively, a value can be stored in a mutable box like `class Counter(var i: Int)`, which is, in turn, 679stored in a thread-local variable. However, in this case you are fully responsible to synchronize 680potentially concurrent modifications to the variable in this mutable box. 681 682For advanced usage, for example for integration with logging MDC, transactional contexts or any other libraries 683which internally use thread-locals for passing data, see the documentation of the [ThreadContextElement] interface 684that should be implemented. 685 686<!--- MODULE kotlinx-coroutines-core --> 687<!--- INDEX kotlinx.coroutines --> 688[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html 689[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html 690[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html 691[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html 692[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html 693[Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html 694[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html 695[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html 696[newSingleThreadContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/new-single-thread-context.html 697[ExecutorCoroutineDispatcher.close]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-executor-coroutine-dispatcher/close.html 698[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html 699[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html 700[DEBUG_PROPERTY_NAME]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-d-e-b-u-g_-p-r-o-p-e-r-t-y_-n-a-m-e.html 701[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html 702[isActive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/is-active.html 703[CoroutineScope.coroutineContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/coroutine-context.html 704[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html 705[CoroutineName]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-name/index.html 706[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html 707[MainScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html 708[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html 709[asContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/as-context-element.html 710[ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/ensure-present.html 711[ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html 712<!--- END --> 713