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//sampleStart 231 newSingleThreadContext("Ctx1").use { ctx1 -> 232 newSingleThreadContext("Ctx2").use { ctx2 -> 233 runBlocking(ctx1) { 234 log("Started in ctx1") 235 withContext(ctx2) { 236 log("Working in ctx2") 237 } 238 log("Back to ctx1") 239 } 240 } 241 } 242//sampleEnd 243} 244``` 245{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 246 247> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt). 248> 249{type="note"} 250 251It demonstrates several new techniques. One is using [runBlocking] with an explicitly specified context, and 252the other one is using the [withContext] function to change the context of a coroutine while still staying in the 253same coroutine, as you can see in the output below: 254 255```text 256[Ctx1 @coroutine#1] Started in ctx1 257[Ctx2 @coroutine#1] Working in ctx2 258[Ctx1 @coroutine#1] Back to ctx1 259``` 260 261<!--- TEST --> 262 263Note that this example also uses the `use` function from the Kotlin standard library to release threads 264created with [newSingleThreadContext] when they are no longer needed. 265 266## Job in the context 267 268The coroutine's [Job] is part of its context, and can be retrieved from it 269using the `coroutineContext[Job]` expression: 270 271```kotlin 272import kotlinx.coroutines.* 273 274fun main() = runBlocking<Unit> { 275//sampleStart 276 println("My job is ${coroutineContext[Job]}") 277//sampleEnd 278} 279``` 280{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 281 282> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt). 283> 284{type="note"} 285 286In the [debug mode](#debugging-coroutines-and-threads), it outputs something like this: 287 288``` 289My job is "coroutine#1":BlockingCoroutine{Active}@6d311334 290``` 291 292<!--- TEST lines.size == 1 && lines[0].startsWith("My job is \"coroutine#1\":BlockingCoroutine{Active}@") --> 293 294Note that [isActive] in [CoroutineScope] is just a convenient shortcut for 295`coroutineContext[Job]?.isActive == true`. 296 297## Children of a coroutine 298 299When a coroutine is launched in the [CoroutineScope] of another coroutine, 300it inherits its context via [CoroutineScope.coroutineContext] and 301the [Job] of the new coroutine becomes 302a _child_ of the parent coroutine's job. When the parent coroutine is cancelled, all its children 303are recursively cancelled, too. 304 305However, this parent-child relation can be explicitly overriden in one of two ways: 306 3071. When a different scope is explicitly specified when launching a coroutine (for example, `GlobalScope.launch`), 308 then it does not inherit a `Job` from the parent scope. 3092. When a different `Job` object is passed as the context for the new coroutine (as shown in the example below), 310 then it overrides the `Job` of the parent scope. 311 312In both cases, the launched coroutine is not tied to the scope it was launched from and operates independently. 313 314```kotlin 315import kotlinx.coroutines.* 316 317fun main() = runBlocking<Unit> { 318//sampleStart 319 // launch a coroutine to process some kind of incoming request 320 val request = launch { 321 // it spawns two other jobs 322 launch(Job()) { 323 println("job1: I run in my own Job and execute independently!") 324 delay(1000) 325 println("job1: I am not affected by cancellation of the request") 326 } 327 // and the other inherits the parent context 328 launch { 329 delay(100) 330 println("job2: I am a child of the request coroutine") 331 delay(1000) 332 println("job2: I will not execute this line if my parent request is cancelled") 333 } 334 } 335 delay(500) 336 request.cancel() // cancel processing of the request 337 println("main: Who has survived request cancellation?") 338 delay(1000) // delay the main thread for a second to see what happens 339//sampleEnd 340} 341``` 342{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 343 344> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt). 345> 346{type="note"} 347 348The output of this code is: 349 350```text 351job1: I run in my own Job and execute independently! 352job2: I am a child of the request coroutine 353main: Who has survived request cancellation? 354job1: I am not affected by cancellation of the request 355``` 356 357<!--- TEST --> 358 359## Parental responsibilities 360 361A parent coroutine always waits for completion of all its children. A parent does not have to explicitly track 362all the children it launches, and it does not have to use [Job.join] to wait for them at the end: 363 364```kotlin 365import kotlinx.coroutines.* 366 367fun main() = runBlocking<Unit> { 368//sampleStart 369 // launch a coroutine to process some kind of incoming request 370 val request = launch { 371 repeat(3) { i -> // launch a few children jobs 372 launch { 373 delay((i + 1) * 200L) // variable delay 200ms, 400ms, 600ms 374 println("Coroutine $i is done") 375 } 376 } 377 println("request: I'm done and I don't explicitly join my children that are still active") 378 } 379 request.join() // wait for completion of the request, including all its children 380 println("Now processing of the request is complete") 381//sampleEnd 382} 383``` 384{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 385 386> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt). 387> 388{type="note"} 389 390The result is going to be: 391 392```text 393request: I'm done and I don't explicitly join my children that are still active 394Coroutine 0 is done 395Coroutine 1 is done 396Coroutine 2 is done 397Now processing of the request is complete 398``` 399 400<!--- TEST --> 401 402## Naming coroutines for debugging 403 404Automatically assigned ids are good when coroutines log often and you just need to correlate log records 405coming from the same coroutine. However, when a coroutine is tied to the processing of a specific request 406or doing some specific background task, it is better to name it explicitly for debugging purposes. 407The [CoroutineName] context element serves the same purpose as the thread name. It is included in the thread name that 408is executing this coroutine when the [debugging mode](#debugging-coroutines-and-threads) is turned on. 409 410The following example demonstrates this concept: 411 412```kotlin 413import kotlinx.coroutines.* 414 415fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") 416 417fun main() = runBlocking(CoroutineName("main")) { 418//sampleStart 419 log("Started main coroutine") 420 // run two background value computations 421 val v1 = async(CoroutineName("v1coroutine")) { 422 delay(500) 423 log("Computing v1") 424 252 425 } 426 val v2 = async(CoroutineName("v2coroutine")) { 427 delay(1000) 428 log("Computing v2") 429 6 430 } 431 log("The answer for v1 / v2 = ${v1.await() / v2.await()}") 432//sampleEnd 433} 434``` 435{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 436 437> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt). 438> 439{type="note"} 440 441The output it produces with `-Dkotlinx.coroutines.debug` JVM option is similar to: 442 443```text 444[main @main#1] Started main coroutine 445[main @v1coroutine#2] Computing v1 446[main @v2coroutine#3] Computing v2 447[main @main#1] The answer for v1 / v2 = 42 448``` 449 450<!--- TEST FLEXIBLE_THREAD --> 451 452## Combining context elements 453 454Sometimes we need to define multiple elements for a coroutine context. We can use the `+` operator for that. 455For example, we can launch a coroutine with an explicitly specified dispatcher and an explicitly specified 456name at the same time: 457 458```kotlin 459import kotlinx.coroutines.* 460 461fun main() = runBlocking<Unit> { 462//sampleStart 463 launch(Dispatchers.Default + CoroutineName("test")) { 464 println("I'm working in thread ${Thread.currentThread().name}") 465 } 466//sampleEnd 467} 468``` 469{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 470 471> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt). 472> 473{type="note"} 474 475The output of this code with the `-Dkotlinx.coroutines.debug` JVM option is: 476 477```text 478I'm working in thread DefaultDispatcher-worker-1 @test#2 479``` 480 481<!--- TEST FLEXIBLE_THREAD --> 482 483## Coroutine scope 484 485Let us put our knowledge about contexts, children and jobs together. Assume that our application has 486an object with a lifecycle, but that object is not a coroutine. For example, we are writing an Android application 487and launch various coroutines in the context of an Android activity to perform asynchronous operations to fetch 488and update data, do animations, etc. All of these coroutines must be cancelled when the activity is destroyed 489to avoid memory leaks. We, of course, can manipulate contexts and jobs manually to tie the lifecycles of the activity 490and its coroutines, but `kotlinx.coroutines` provides an abstraction encapsulating that: [CoroutineScope]. 491You should be already familiar with the coroutine scope as all coroutine builders are declared as extensions on it. 492 493We manage the lifecycles of our coroutines by creating an instance of [CoroutineScope] tied to 494the lifecycle of our activity. A `CoroutineScope` instance can be created by the [CoroutineScope()] or [MainScope()] 495factory functions. The former creates a general-purpose scope, while the latter creates a scope for UI applications and uses 496[Dispatchers.Main] as the default dispatcher: 497 498```kotlin 499class Activity { 500 private val mainScope = MainScope() 501 502 fun destroy() { 503 mainScope.cancel() 504 } 505 // to be continued ... 506``` 507 508Now, we can launch coroutines in the scope of this `Activity` using the defined `scope`. 509For the demo, we launch ten coroutines that delay for a different time: 510 511```kotlin 512 // class Activity continues 513 fun doSomething() { 514 // launch ten coroutines for a demo, each working for a different time 515 repeat(10) { i -> 516 mainScope.launch { 517 delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc 518 println("Coroutine $i is done") 519 } 520 } 521 } 522} // class Activity ends 523``` 524 525In our main function we create the activity, call our test `doSomething` function, and destroy the activity after 500ms. 526This cancels all the coroutines that were launched from `doSomething`. We can see that because after the destruction 527of the activity no more messages are printed, even if we wait a little longer. 528 529<!--- CLEAR --> 530 531```kotlin 532import kotlinx.coroutines.* 533 534class Activity { 535 private val mainScope = CoroutineScope(Dispatchers.Default) // use Default for test purposes 536 537 fun destroy() { 538 mainScope.cancel() 539 } 540 541 fun doSomething() { 542 // launch ten coroutines for a demo, each working for a different time 543 repeat(10) { i -> 544 mainScope.launch { 545 delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc 546 println("Coroutine $i is done") 547 } 548 } 549 } 550} // class Activity ends 551 552fun main() = runBlocking<Unit> { 553//sampleStart 554 val activity = Activity() 555 activity.doSomething() // run test function 556 println("Launched coroutines") 557 delay(500L) // delay for half a second 558 println("Destroying activity!") 559 activity.destroy() // cancels all coroutines 560 delay(1000) // visually confirm that they don't work 561//sampleEnd 562} 563``` 564{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 565 566> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt). 567> 568{type="note"} 569 570The output of this example is: 571 572```text 573Launched coroutines 574Coroutine 0 is done 575Coroutine 1 is done 576Destroying activity! 577``` 578 579<!--- TEST --> 580 581As you can see, only the first two coroutines print a message and the others are cancelled 582by a single invocation of `job.cancel()` in `Activity.destroy()`. 583 584> Note, that Android has first-party support for coroutine scope in all entities with the lifecycle. 585> See [the corresponding documentation](https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope). 586> 587{type="note"} 588 589### Thread-local data 590 591Sometimes it is convenient to have an ability to pass some thread-local data to or between coroutines. 592However, since they are not bound to any particular thread, this will likely lead to boilerplate if done manually. 593 594For [`ThreadLocal`](https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html), 595the [asContextElement] extension function is here for the rescue. It creates an additional context element 596which keeps the value of the given `ThreadLocal` and restores it every time the coroutine switches its context. 597 598It is easy to demonstrate it in action: 599 600```kotlin 601import kotlinx.coroutines.* 602 603val threadLocal = ThreadLocal<String?>() // declare thread-local variable 604 605fun main() = runBlocking<Unit> { 606//sampleStart 607 threadLocal.set("main") 608 println("Pre-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 609 val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "launch")) { 610 println("Launch start, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 611 yield() 612 println("After yield, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 613 } 614 job.join() 615 println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") 616//sampleEnd 617} 618``` 619{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 620 621> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt). 622> 623{type="note"} 624 625In this example we launch a new coroutine in a background thread pool using [Dispatchers.Default], so 626it works on a different thread from the thread pool, but it still has the value of the thread local variable 627that we specified using `threadLocal.asContextElement(value = "launch")`, 628no matter which thread the coroutine is executed on. 629Thus, the output (with [debug](#debugging-coroutines-and-threads)) is: 630 631```text 632Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main' 633Launch start, current thread: Thread[DefaultDispatcher-worker-1 @coroutine#2,5,main], thread local value: 'launch' 634After yield, current thread: Thread[DefaultDispatcher-worker-2 @coroutine#2,5,main], thread local value: 'launch' 635Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main' 636``` 637 638<!--- TEST FLEXIBLE_THREAD --> 639 640It's easy to forget to set the corresponding context element. The thread-local variable accessed from the coroutine may 641then have an unexpected value, if the thread running the coroutine is different. 642To avoid such situations, it is recommended to use the [ensurePresent] method 643and fail-fast on improper usages. 644 645`ThreadLocal` has first-class support and can be used with any primitive `kotlinx.coroutines` provides. 646It has one key limitation, though: when a thread-local is mutated, a new value is not propagated to the coroutine caller 647(because a context element cannot track all `ThreadLocal` object accesses), and the updated value is lost on the next suspension. 648Use [withContext] to update the value of the thread-local in a coroutine, see [asContextElement] for more details. 649 650Alternatively, a value can be stored in a mutable box like `class Counter(var i: Int)`, which is, in turn, 651stored in a thread-local variable. However, in this case you are fully responsible to synchronize 652potentially concurrent modifications to the variable in this mutable box. 653 654For advanced usage, for example for integration with logging MDC, transactional contexts or any other libraries 655which internally use thread-locals for passing data, see the documentation of the [ThreadContextElement] interface 656that should be implemented. 657 658<!--- MODULE kotlinx-coroutines-core --> 659<!--- INDEX kotlinx.coroutines --> 660 661[Job]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html 662[CoroutineDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html 663[launch]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html 664[async]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html 665[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html 666[Dispatchers.Unconfined]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html 667[Dispatchers.Default]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html 668[newSingleThreadContext]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/new-single-thread-context.html 669[ExecutorCoroutineDispatcher.close]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-executor-coroutine-dispatcher/close.html 670[runBlocking]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html 671[delay]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html 672[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 673[withContext]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html 674[isActive]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/is-active.html 675[CoroutineScope.coroutineContext]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/coroutine-context.html 676[Job.join]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html 677[CoroutineName]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-name/index.html 678[CoroutineScope()]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html 679[MainScope()]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html 680[Dispatchers.Main]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html 681[asContextElement]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/as-context-element.html 682[ensurePresent]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-present.html 683[ThreadContextElement]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html 684 685<!--- END --> 686