• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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//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