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