xref: /aosp_15_r20/external/kotlinx.coroutines/docs/topics/coroutine-context-and-dispatchers.md (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)
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![Debugging coroutines](coroutine-idea-debugging-1.png){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