aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/images/new-mvn-project-jvm.pngbin378558 -> 0 bytes
-rw-r--r--docs/kc.tree5
-rw-r--r--docs/project.ihp2
-rw-r--r--docs/topics/cancellation-and-timeouts.md6
-rw-r--r--docs/topics/channels.md4
-rw-r--r--docs/topics/compatibility.md4
-rw-r--r--docs/topics/composing-suspending-functions.md24
-rw-r--r--docs/topics/coroutine-context-and-dispatchers.md30
-rw-r--r--docs/topics/coroutines-basic-jvm.md267
-rw-r--r--docs/topics/coroutines-basics.md371
-rw-r--r--docs/topics/coroutines-guide.md5
-rw-r--r--docs/topics/debug-coroutines-with-idea.md4
-rw-r--r--docs/topics/debugging.md5
-rw-r--r--docs/topics/exception-handling.md15
-rw-r--r--docs/topics/flow.md4
-rw-r--r--docs/topics/select-expression.md69
-rw-r--r--docs/topics/shared-mutable-state-and-concurrency.md6
17 files changed, 247 insertions, 574 deletions
diff --git a/docs/images/new-mvn-project-jvm.png b/docs/images/new-mvn-project-jvm.png
deleted file mode 100644
index 2154a3df..00000000
--- a/docs/images/new-mvn-project-jvm.png
+++ /dev/null
Binary files differ
diff --git a/docs/kc.tree b/docs/kc.tree
index 9cc0a28c..bd37d87f 100644
--- a/docs/kc.tree
+++ b/docs/kc.tree
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE product-profile
- SYSTEM "http://helpserver.labs.intellij.net/help/product-profile.dtd">
+ SYSTEM "https://resources.jetbrains.com/stardust/product-profile.dtd">
<product-profile id="kc"
name="Kotlin coroutines"
@@ -9,8 +9,7 @@
<toc-element id="coroutines-guide.md"/>
<toc-element id="async-programming.md"/>
- <toc-element id="coroutines-basics.md"/>
- <toc-element id="coroutines-basic-jvm.md"/>
+ <toc-element id="coroutines-basics.md" accepts-web-file-names="basics.html,coroutines-basic-jvm.html"/>
<toc-element toc-title="Intro to coroutines and channels – hands-on tutorial" href="https://play.kotlinlang.org/hands-on/Introduction%20to%20Coroutines%20and%20Channels/"/>
<toc-element id="cancellation-and-timeouts.md"/>
<toc-element id="composing-suspending-functions.md"/>
diff --git a/docs/project.ihp b/docs/project.ihp
index 80859c4b..20e44ec8 100644
--- a/docs/project.ihp
+++ b/docs/project.ihp
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE ihp SYSTEM "http://helpserver.labs.intellij.net/help/ihp.dtd">
+<!DOCTYPE ihp SYSTEM "https://resources.jetbrains.com/stardust/ihp.dtd">
<ihp version="2.0">
<categories src="c.list"/>
diff --git a/docs/topics/cancellation-and-timeouts.md b/docs/topics/cancellation-and-timeouts.md
index 7aa51e26..5221db92 100644
--- a/docs/topics/cancellation-and-timeouts.md
+++ b/docs/topics/cancellation-and-timeouts.md
@@ -447,15 +447,15 @@ This example always prints zero. Resources do not leak.
[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
[cancelAndJoin]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel-and-join.html
-[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
+[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html
[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html
[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html
[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html
[isActive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/is-active.html
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
-[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable.html
+[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable/index.html
[withTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout.html
[withTimeoutOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout-or-null.html
-<!--- END --> \ No newline at end of file
+<!--- END -->
diff --git a/docs/topics/channels.md b/docs/topics/channels.md
index 9380b2bd..7f41eaec 100644
--- a/docs/topics/channels.md
+++ b/docs/topics/channels.md
@@ -626,7 +626,7 @@ delay between elements.
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
-[kotlin.coroutines.CoroutineContext.cancelChildren]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/kotlin.coroutines.-coroutine-context/cancel-children.html
+[kotlin.coroutines.CoroutineContext.cancelChildren]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel-children.html
[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
<!--- INDEX kotlinx.coroutines.channels -->
@@ -640,7 +640,7 @@ delay between elements.
[Channel()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel.html
[ticker]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/ticker.html
[ReceiveChannel.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/cancel.html
-[TickerMode.FIXED_DELAY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-ticker-mode/-f-i-x-e-d_-d-e-l-a-y.html
+[TickerMode.FIXED_DELAY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-ticker-mode/-f-i-x-e-d_-d-e-l-a-y/index.html
<!--- INDEX kotlinx.coroutines.selects -->
diff --git a/docs/topics/compatibility.md b/docs/topics/compatibility.md
index a9414432..97448116 100644
--- a/docs/topics/compatibility.md
+++ b/docs/topics/compatibility.md
@@ -80,9 +80,9 @@ It is done in order to produce compilation warning about using experimental or o
Warnings can be disabled either programmatically for a specific call site or globally for the whole module.
### Programmatically
-For a specific call-site, warning can be disabled by using [UseExperimental](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-use-experimental/index.html) annotation:
+For a specific call-site, warning can be disabled by using [OptIn](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-opt-in/) annotation:
```kotlin
-@UseExperimental(ExperimentalCoroutinesApi::class) // Disables warning about experimental coroutines API
+@OptIn(ExperimentalCoroutinesApi::class) // Disables warning about experimental coroutines API
fun experimentalApiUsage() {
someKotlinxCoroutinesExperimentalMethod()
}
diff --git a/docs/topics/composing-suspending-functions.md b/docs/topics/composing-suspending-functions.md
index 0af60d00..e244d8c2 100644
--- a/docs/topics/composing-suspending-functions.md
+++ b/docs/topics/composing-suspending-functions.md
@@ -24,7 +24,7 @@ suspend fun doSomethingUsefulTwo(): Int {
What do we do if we need them to be invoked _sequentially_ &mdash; first `doSomethingUsefulOne` _and then_
`doSomethingUsefulTwo`, and compute the sum of their results?
-In practice we do this if we use the result of the first function to make a decision on whether we need
+In practice, we do this if we use the result of the first function to make a decision on whether we need
to invoke the second one or to decide on how to invoke it.
We use a normal sequential invocation, because the code in the coroutine, just like in the regular
@@ -190,18 +190,26 @@ standard `lazy` function in cases when computation of the value involves suspend
## Async-style functions
We can define async-style functions that invoke `doSomethingUsefulOne` and `doSomethingUsefulTwo`
-_asynchronously_ using the [async] coroutine builder with an explicit [GlobalScope] reference.
+_asynchronously_ using the [async] coroutine builder using a [GlobalScope] reference to
+opt-out of the structured concurrency.
We name such functions with the
"...Async" suffix to highlight the fact that they only start asynchronous computation and one needs
to use the resulting deferred value to get the result.
+> [GlobalScope] is a delicate API that can backfire in non-trivial ways, one of which will be explained
+> below, so you must explicitly opt-in into using `GlobalScope` with `@OptIn(DelicateCoroutinesApi::class)`.
+>
+{type="note"}
+
```kotlin
// The result type of somethingUsefulOneAsync is Deferred<Int>
+@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
doSomethingUsefulOne()
}
// The result type of somethingUsefulTwoAsync is Deferred<Int>
+@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulTwoAsync() = GlobalScope.async {
doSomethingUsefulTwo()
}
@@ -236,10 +244,12 @@ fun main() {
}
//sampleEnd
+@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
doSomethingUsefulOne()
}
+@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulTwoAsync() = GlobalScope.async {
doSomethingUsefulTwo()
}
@@ -272,9 +282,9 @@ Completed in 1085 ms
{type="note"}
Consider what happens if between the `val one = somethingUsefulOneAsync()` line and `one.await()` expression there is some logic
-error in the code and the program throws an exception and the operation that was being performed by the program aborts.
+error in the code, and the program throws an exception, and the operation that was being performed by the program aborts.
Normally, a global error-handler could catch this exception, log and report the error for developers, but the program
-could otherwise continue doing other operations. But here we have `somethingUsefulOneAsync` still running in the background,
+could otherwise continue doing other operations. However, here we have `somethingUsefulOneAsync` still running in the background,
even though the operation that initiated it was aborted. This problem does not happen with structured
concurrency, as shown in the section below.
@@ -293,7 +303,7 @@ suspend fun concurrentSum(): Int = coroutineScope {
}
```
-This way, if something goes wrong inside the code of the `concurrentSum` function and it throws an exception,
+This way, if something goes wrong inside the code of the `concurrentSum` function, and it throws an exception,
all the coroutines that were launched in its scope will be cancelled.
<!--- CLEAR -->
@@ -396,11 +406,11 @@ Computation failed with ArithmeticException
[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html
-[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-l-a-z-y.html
+[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-l-a-z-y/index.html
[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html
[Job.start]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/start.html
[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
-<!--- END --> \ No newline at end of file
+<!--- END -->
diff --git a/docs/topics/coroutine-context-and-dispatchers.md b/docs/topics/coroutine-context-and-dispatchers.md
index 9aae1a7a..c35de0e0 100644
--- a/docs/topics/coroutine-context-and-dispatchers.md
+++ b/docs/topics/coroutine-context-and-dispatchers.md
@@ -65,9 +65,8 @@ context of the main `runBlocking` coroutine which runs in the `main` thread.
[Dispatchers.Unconfined] is a special dispatcher that also appears to run in the `main` thread, but it is,
in fact, a different mechanism that is explained later.
-The default dispatcher that is used when coroutines are launched in [GlobalScope]
-is represented by [Dispatchers.Default] and uses a shared background pool of threads,
-so `launch(Dispatchers.Default) { ... }` uses the same dispatcher as `GlobalScope.launch { ... }`.
+The default dispatcher that is used when no other dispatcher is explicitly specified in the scope.
+It is represented by [Dispatchers.Default] and uses a shared background pool of threads.
[newSingleThreadContext] creates a thread for the coroutine to run.
A dedicated thread is a very expensive resource.
@@ -303,8 +302,14 @@ the [Job] of the new coroutine becomes
a _child_ of the parent coroutine's job. When the parent coroutine is cancelled, all its children
are recursively cancelled, too.
-However, when [GlobalScope] is used to launch a coroutine, there is no parent for the job of the new coroutine.
-It is therefore not tied to the scope it was launched from and operates independently.
+However, this parent-child relation can be explicitly overriden in one of two ways:
+
+1. When a different scope is explicitly specified when launching a coroutine (for example, `GlobalScope.launch`),
+ then it does not inherit a `Job` from the parent scope.
+2. When a different `Job` object is passed as the context for the new coroutine (as show in the example below),
+ then it overrides the `Job` of the parent scope.
+
+In both cases, the launched coroutine is not tied to the scope it was launched from and operates independently.
```kotlin
import kotlinx.coroutines.*
@@ -313,9 +318,9 @@ fun main() = runBlocking<Unit> {
//sampleStart
// launch a coroutine to process some kind of incoming request
val request = launch {
- // it spawns two other jobs, one with GlobalScope
- GlobalScope.launch {
- println("job1: I run in GlobalScope and execute independently!")
+ // it spawns two other jobs
+ launch(Job()) {
+ println("job1: I run in my own Job and execute independently!")
delay(1000)
println("job1: I am not affected by cancellation of the request")
}
@@ -343,7 +348,7 @@ fun main() = runBlocking<Unit> {
The output of this code is:
```text
-job1: I run in GlobalScope and execute independently!
+job1: I run in my own Job and execute independently!
job2: I am a child of the request coroutine
job1: I am not affected by cancellation of the request
main: Who has survived request cancellation?
@@ -659,7 +664,6 @@ that should be implemented.
[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
[Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html
-[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
[newSingleThreadContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/new-single-thread-context.html
[ExecutorCoroutineDispatcher.close]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-executor-coroutine-dispatcher/close.html
@@ -674,8 +678,8 @@ that should be implemented.
[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html
[MainScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html
[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
-[asContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/as-context-element.html
-[ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/ensure-present.html
+[asContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/as-context-element.html
+[ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-present.html
[ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html
-<!--- END --> \ No newline at end of file
+<!--- END -->
diff --git a/docs/topics/coroutines-basic-jvm.md b/docs/topics/coroutines-basic-jvm.md
deleted file mode 100644
index 3b067ea8..00000000
--- a/docs/topics/coroutines-basic-jvm.md
+++ /dev/null
@@ -1,267 +0,0 @@
-[//]: # (title: Create a basic coroutine – tutorial)
-
-Kotlin 1.1 introduced coroutines, a new way of writing asynchronous, non-blocking code (and much more). In this tutorial you will go through some basics of using Kotlin coroutines with the help of the `kotlinx.coroutines` library, which is a collection of helpers and wrappers for existing Java libraries.
-
-## Set up a project
-
-### Gradle
-
-In IntelliJ IDEA go to **File** \| **New** \| **Project**.:
-
-![Create a new project](new-gradle-project-jvm.png)
-
-Then follow the wizard steps. You'll have a `build.gradle` file created with Kotlin configured according to [this document](gradle.md).
-Make sure it's configured for Kotlin 1.3 or higher.
-
-Since we'll be using the [`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines), let's add its recent version to our dependencies:
-
-<tabs>
-
-```groovy
-dependencies {
- ...
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%'
-}
-```
-
-```kotlin
-dependencies {
- ...
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%")
-}
-```
-</tabs>
-
-This library is published to the [Maven Central repository](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core), so add the following:
-
-```groovy
-repositories {
- mavenCentral()
-}
-```
-
-That's it, we are good to go and write code under `src/main/kotlin`.
-
-### Maven
-
-In IntelliJ IDEA go to **File** \| **New** \| **Project** and check the **Create from archetype** box:
-
-![Create a new project](new-mvn-project-jvm.png)
-
-Then follow the wizard steps. You'll have a `pom.xml` file created with Kotlin configured according to [this document](maven.md).
-Make sure it's configured for Kotlin 1.3 or higher.
-
-```xml
-<plugin>
- <groupId>org.jetbrains.kotlin</groupId>
- <artifactId>kotlin-maven-plugin</artifactId>
- ...
- <configuration>
- <args>
- <arg>-Xcoroutines=enable</arg>
- </args>
- </configuration>
-</plugin>
-```
-
-Since we'll be using the [`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines), let's add its recent version to our dependencies:
-
-```xml
-<dependencies>
- ...
- <dependency>
- <groupId>org.jetbrains.kotlinx</groupId>
- <artifactId>kotlinx-coroutines-core</artifactId>
- <version>%coroutinesVersion%</version>
- </dependency>
-</dependencies>
-```
-
-This library is published to the [Maven Central repository](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core), which Maven will download from by default.
-
-That's it, we are good to go and write code under `src/main/kotlin`.
-
-## My first coroutine
-
-One can think of a coroutine as a light-weight thread. Like threads, coroutines can run in parallel, wait for each other and communicate.
-The biggest difference is that coroutines are very cheap, almost free: we can create thousands of them, and pay very little in terms of performance.
-True threads, on the other hand, are expensive to start and keep around. A thousand threads can be a serious challenge for a modern machine.
-
-So, how do we start a coroutine? Let's use the `launch {}` function:
-
-```kotlin
-launch {
- ...
-}
-```
-
-This starts a new coroutine. By default, coroutines are run on a shared pool of threads.
-Threads still exist in a program based on coroutines, but one thread can run many coroutines, so there's no need for
-too many threads.
-
-Let's look at a full program that uses `launch`:
-
-```kotlin
-import kotlinx.coroutines.*
-
-fun main(args: Array<String>) {
-//sampleStart
- println("Start")
-
- // Start a coroutine
- GlobalScope.launch {
- delay(1000)
- println("Hello")
- }
-
- Thread.sleep(2000) // wait for 2 seconds
- println("Stop")
-//sampleEnd
-}
-```
-{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
-
-Here we start a coroutine that waits for 1 second and prints `Hello`.
-
-We are using the `delay()` function that's like `Thread.sleep()`, but better: it _doesn't block a thread_, but only suspends the coroutine itself.
-The thread is returned to the pool while the coroutine is waiting, and when the waiting is done, the coroutine resumes on a free thread in the pool.
-
-The main thread (that runs the `main()` function) must wait until our coroutine completes, otherwise the program ends before `Hello` is printed.
-
-_Exercise: try removing the `sleep()` from the program above and see the result._
-
- If we try to use the same non-blocking `delay()` function directly inside `main()`, we'll get a compiler error:
-
-> Suspend functions are only allowed to be called from a coroutine or another suspend function.
->
-{type="note"}
-
-This is because we are not inside any coroutine. We can use delay if we wrap it into `runBlocking {}` that starts a coroutine and waits until it's done:
-
-```kotlin
-runBlocking {
- delay(2000)
-}
-```
-
-So, first the resulting program prints `Start`, then it runs a coroutine through `launch {}`, then it runs another one through `runBlocking {}` and blocks until it's done, then prints `Stop`. Meanwhile the first coroutine completes and prints `Hello`. Just like threads, we told you :)
-
-## Let's run a lot of them
-
-Now, let's make sure that coroutines are really cheaper than threads. How about starting a million of them? Let's try starting a million threads first:
-
-```kotlin
-val c = AtomicLong()
-
-for (i in 1..1_000_000L)
- thread(start = true) {
- c.addAndGet(i)
- }
-
-println(c.get())
-```
-
-This runs a 1'000'000 threads each of which adds to a common counter. My patience runs out before this program completes on my machine (definitely over a minute).
-
-Let's try the same with coroutines:
-
-```kotlin
-val c = AtomicLong()
-
-for (i in 1..1_000_000L)
- GlobalScope.launch {
- c.addAndGet(i)
- }
-
-println(c.get())
-```
-
-This example completes in less than a second for me, but it prints some arbitrary number, because some coroutines don't finish before `main()` prints the result. Let's fix that.
-
-We could use the same means of synchronization that are applicable to threads (a `CountDownLatch` is what crosses my mind in this case), but let's take a safer and cleaner path.
-
-## Async: returning a value from a coroutine
-
-Another way of starting a coroutine is `async {}`. It is like `launch {}`, but returns an instance of `Deferred<T>`, which has an `await()` function that returns the result of the coroutine. `Deferred<T>` is a very basic [future](https://en.wikipedia.org/wiki/Futures_and_promises) (fully-fledged JDK futures are also supported, but here we'll confine ourselves to `Deferred` for now).
-
-Let's create a million coroutines again, keeping their `Deferred` objects. Now there's no need in the atomic counter, as we can just return the numbers to be added from our coroutines:
-
-```kotlin
-val deferred = (1..1_000_000).map { n ->
- GlobalScope.async {
- n
- }
-}
-```
-
-All these have already started, all we need is collect the results:
-
-```kotlin
-val sum = deferred.sumOf { it.await().toLong() }
-```
-
-We simply take every coroutine and await its result here, then all results are added together by the standard library function `sumOf()`. But the compiler rightfully complains:
-
-> Suspend functions are only allowed to be called from a coroutine or another suspend function.
->
-{type="note"}
-
-`await()` can not be called outside a coroutine, because it needs to suspend until the computation finishes, and only coroutines can suspend in a non-blocking way. So, let's put this inside a coroutine:
-
-```kotlin
-runBlocking {
- val sum = deferred.sumOf { it.await().toLong() }
- println("Sum: $sum")
-}
-```
-
-Now it prints something sensible: `500000500000`, because all coroutines complete.
-
-Let's also make sure that our coroutines actually run in parallel. If we add a 1-second `delay()` to each of the `async`'s, the resulting program won't run for 1'000'000 seconds (over 11,5 days):
-
-```kotlin
-val deferred = (1..1_000_000).map { n ->
- GlobalScope.async {
- delay(1000)
- n
- }
-}
-```
-
-This takes about 10 seconds on my machine, so yes, coroutines do run in parallel.
-
-## Suspending functions
-
-Now, let's say we want to extract our _workload_ (which is "wait 1 second and return a number") into a separate function:
-
-```kotlin
-fun workload(n: Int): Int {
- delay(1000)
- return n
-}
-```
-
-A familiar error pops up:
-
-> Suspend functions are only allowed to be called from a coroutine or another suspend function.
->
-{type="note"}
-
-Let's dig a little into what it means. The biggest merit of coroutines is that they can _suspend_ without blocking a thread. The compiler has to emit some special code to make this possible, so we have to mark functions that _may suspend_ explicitly in the code. We use the `suspend` modifier for it:
-
-```kotlin
-suspend fun workload(n: Int): Int {
- delay(1000)
- return n
-}
-```
-
-Now when we call `workload()` from a coroutine, the compiler knows that it may suspend and will prepare accordingly:
-
-```kotlin
-GlobalScope.async {
- workload(n)
-}
-```
-
-Our `workload()` function can be called from a coroutine (or another suspending function), but _cannot_ be called from outside a coroutine. Naturally, `delay()` and `await()` that we used above are themselves declared as `suspend`, and this is why we had to put them inside `runBlocking {}`, `launch {}` or `async {}`.
diff --git a/docs/topics/coroutines-basics.md b/docs/topics/coroutines-basics.md
index c1c17581..5d9d0e6d 100644
--- a/docs/topics/coroutines-basics.md
+++ b/docs/topics/coroutines-basics.md
@@ -6,19 +6,27 @@ This section covers basic coroutine concepts.
## Your first coroutine
-Run the following code:
+A _coroutine_ is an instance of suspendable computation. It is conceptually similar to a thread, in the sense that it
+takes a block of code to run that works concurrently with the rest of the code.
+However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one.
+
+Coroutines can be thought of as light-weight threads, but there is a number
+of important differences that make their real-life usage very different from threads.
+
+Run the following code to get to your first working coroutine:
```kotlin
import kotlinx.coroutines.*
-fun main() {
- GlobalScope.launch { // launch a new coroutine in background and continue
+//sampleStart
+fun main() = runBlocking { // this: CoroutineScope
+ launch { // launch a new coroutine and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
- println("Hello,") // main thread continues while coroutine is delayed
- Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
+ println("Hello") // main coroutine continues while a previous one is delayed
}
+//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
@@ -29,49 +37,71 @@ fun main() {
You will see the following result:
```text
-Hello,
+Hello
World!
```
<!--- TEST -->
-Essentially, coroutines are light-weight threads.
-They are launched with [launch] _coroutine builder_ in a context of some [CoroutineScope].
-Here we are launching a new coroutine in the [GlobalScope], meaning that the lifetime of the new
-coroutine is limited only by the lifetime of the whole application.
+Let's dissect what this code does.
+
+[launch] is a _coroutine builder_. It launches a new coroutine concurrently with
+the rest of the code, which continues to work independently. That's why `Hello` has been printed first.
-You can achieve the same result by replacing
-`GlobalScope.launch { ... }` with `thread { ... }`, and `delay(...)` with `Thread.sleep(...)`.
-Try it (don't forget to import `kotlin.concurrent.thread`).
+[delay] is a special _suspending function_. It _suspends_ the coroutine for a specific time. Suspending a coroutine
+does not _block_ the underlying thread, but allows other coroutines to run and use the underlying thread for
+their code.
-If you start by replacing `GlobalScope.launch` with `thread`, the compiler produces the following error:
+[runBlocking] is also a coroutine builder that bridges the non-coroutine world of a regular `fun main()` and
+the code with coroutines inside of `runBlocking { ... }` curly braces. This is highlighted in an IDE by
+`this: CoroutineScope` hint right after the `runBlocking` opening curly brace.
+
+If you remove or forget `runBlocking` in this code, you'll get an error on the [launch] call, since `launch`
+is declared only in the [CoroutineScope]:
```Plain Text
-Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function
+Unresolved reference: launch
```
-That is because [delay] is a special _suspending function_ that does not block a thread, but _suspends_ the
-coroutine, and it can be only used from a coroutine.
+The name of `runBlocking` means that the thread that runs it (in this case &mdash; the main thread) gets _blocked_ for
+the duration of the call, until all the coroutines inside `runBlocking { ... }` complete their execution. You will
+often see `runBlocking` used like that at the very top-level of the application and quite rarely inside the real code,
+as threads are expensive resources and blocking them is inefficient and is often not desired.
+
+### Structured concurrency
-## Bridging blocking and non-blocking worlds
+Coroutines follow a principle of
+**structured concurrency** which means that new coroutines can be only launched in a specific [CoroutineScope]
+which delimits the lifetime of the coroutine. The above example shows that [runBlocking] establishes the corresponding
+scope and that is why the previous example waits until `World!` is printed after a second's delay and only then exits.
-The first example mixes _non-blocking_ `delay(...)` and _blocking_ `Thread.sleep(...)` in the same code.
-It is easy to lose track of which one is blocking and which one is not.
-Let's be explicit about blocking using the [runBlocking] coroutine builder:
+In the real application, you will be launching a lot of coroutines. Structured concurrency ensures that they are not
+lost and do not leak. An outer scope cannot complete until all its children coroutines complete.
+Structured concurrency also ensures that any errors in the code are properly reported and are never lost.
+
+## Extract function refactoring
+
+Let's extract the block of code inside `launch { ... }` into a separate function. When you
+perform "Extract function" refactoring on this code, you get a new function with the `suspend` modifier.
+This is your first _suspending function_. Suspending functions can be used inside coroutines
+just like regular functions, but their additional feature is that they can, in turn,
+use other suspending functions (like `delay` in this example) to _suspend_ execution of a coroutine.
```kotlin
import kotlinx.coroutines.*
-fun main() {
- GlobalScope.launch { // launch a new coroutine in background and continue
- delay(1000L)
- println("World!")
- }
- println("Hello,") // main thread continues here immediately
- runBlocking { // but this expression blocks the main thread
- delay(2000L) // ... while we delay for 2 seconds to keep JVM alive
- }
+//sampleStart
+fun main() = runBlocking { // this: CoroutineScope
+ launch { doWorld() }
+ println("Hello")
}
+
+// this is your first suspending function
+suspend fun doWorld() {
+ delay(1000L)
+ println("World!")
+}
+//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
@@ -80,27 +110,39 @@ fun main() {
{type="note"}
<!--- TEST
-Hello,
+Hello
World!
-->
-The result is the same, but this code uses only non-blocking [delay].
-The main thread invoking `runBlocking` _blocks_ until the coroutine inside `runBlocking` completes.
+## Scope builder
+
+In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the
+[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete.
+
+[runBlocking] and [coroutineScope][_coroutineScope] builders may look similar because they both wait for their body and all its children to complete.
+The main difference is that the [runBlocking] method _blocks_ the current thread for waiting,
+while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages.
+Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function.
-This example can be also rewritten in a more idiomatic way, using `runBlocking` to wrap
-the execution of the main function:
+You can use `coroutineScope` from any suspending function.
+For example, you can move the concurrent printing of `Hello` and `World` into a `suspend fun doWorld()` function:
```kotlin
import kotlinx.coroutines.*
-fun main() = runBlocking<Unit> { // start main coroutine
- GlobalScope.launch { // launch a new coroutine in background and continue
+//sampleStart
+fun main() = runBlocking {
+ doWorld()
+}
+
+suspend fun doWorld() = coroutineScope { // this: CoroutineScope
+ launch {
delay(1000L)
println("World!")
}
- println("Hello,") // main coroutine continues here immediately
- delay(2000L) // delaying for 2 seconds to keep JVM alive
+ println("Hello")
}
+//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
@@ -108,49 +150,43 @@ fun main() = runBlocking<Unit> { // start main coroutine
>
{type="note"}
-<!--- TEST
-Hello,
-World!
--->
-
-Here `runBlocking<Unit> { ... }` works as an adaptor that is used to start the top-level main coroutine.
-We explicitly specify its `Unit` return type, because a well-formed `main` function in Kotlin has to return `Unit`.
+This code also prints:
-This is also a way to write unit tests for suspending functions:
-
-<!--- INCLUDE
-import kotlinx.coroutines.*
--->
-
-```kotlin
-class MyTest {
- @Test
- fun testMySuspendingFunction() = runBlocking<Unit> {
- // here we can use suspending functions using any assertion style that we like
- }
-}
+```text
+Hello
+World!
```
-<!--- CLEAR -->
+<!--- TEST -->
-## Waiting for a job
+## Scope builder and concurrency
-Delaying for a time while another coroutine is working is not a good approach. Let's explicitly
-wait (in a non-blocking way) until the background [Job] that we have launched is complete:
+A [coroutineScope][_coroutineScope] builder can be used inside any suspending function to perform multiple concurrent operations.
+Let's launch two concurrent coroutines inside a `doWorld` suspending function:
```kotlin
import kotlinx.coroutines.*
-fun main() = runBlocking {
//sampleStart
- val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job
+// Sequentially executes doWorld followed by "Done"
+fun main() = runBlocking {
+ doWorld()
+ println("Done")
+}
+
+// Concurrently executes both sections
+suspend fun doWorld() = coroutineScope { // this: CoroutineScope
+ launch {
+ delay(2000L)
+ println("World 2")
+ }
+ launch {
delay(1000L)
- println("World!")
+ println("World 1")
}
- println("Hello,")
- job.join() // wait until child coroutine completes
-//sampleEnd
+ println("Hello")
}
+//sampleEnd
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
@@ -158,42 +194,39 @@ fun main() = runBlocking {
>
{type="note"}
-<!--- TEST
-Hello,
-World!
--->
-
-Now the result is still the same, but the code of the main coroutine is not tied to the duration of
-the background job in any way. Much better.
+Both pieces of code inside `launch { ... }` blocks execute _concurrently_, with
+`World 1` printed first, after a second from start, and `World 2` printed next, after two seconds from start.
+A [coroutineScope][_coroutineScope] in `doWorld` completes only after both are complete, so `doWorld` returns and
+allows `Done` string to be printed only after that:
-## Structured concurrency
+```text
+Hello
+World 1
+World 2
+Done
+```
-There is still something to be desired for practical usage of coroutines.
-When we use `GlobalScope.launch`, we create a top-level coroutine. Even though it is light-weight, it still
-consumes some memory resources while it runs. If we forget to keep a reference to the newly launched
-coroutine, it still runs. What if the code in the coroutine hangs (for example, we erroneously
-delay for too long), what if we launched too many coroutines and ran out of memory?
-Having to manually keep references to all the launched coroutines and [join][Job.join] them is error-prone.
+<!--- TEST -->
-There is a better solution. We can use structured concurrency in our code.
-Instead of launching coroutines in the [GlobalScope], just like we usually do with threads (threads are always global),
-we can launch coroutines in the specific scope of the operation we are performing.
+## An explicit job
-In our example, we have a `main` function that is turned into a coroutine using the [runBlocking] coroutine builder.
-Every coroutine builder, including `runBlocking`, adds an instance of [CoroutineScope] to the scope of its code block.
-We can launch coroutines in this scope without having to `join` them explicitly, because
-an outer coroutine (`runBlocking` in our example) does not complete until all the coroutines launched
-in its scope complete. Thus, we can make our example simpler:
+A [launch] coroutine builder returns a [Job] object that is a handle to the launched coroutine and can be
+used to explicitly wait for its completion. For example, you can wait for completion of the child coroutine
+and then print "Done" string:
```kotlin
import kotlinx.coroutines.*
-fun main() = runBlocking { // this: CoroutineScope
- launch { // launch a new coroutine in the scope of runBlocking
+fun main() = runBlocking {
+//sampleStart
+ val job = launch { // launch a new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
- println("Hello,")
+ println("Hello")
+ job.join() // wait until child coroutine completes
+ println("Done")
+//sampleEnd
}
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
@@ -202,101 +235,15 @@ fun main() = runBlocking { // this: CoroutineScope
>
{type="note"}
-<!--- TEST
-Hello,
-World!
--->
-
-## Scope builder
-
-In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the
-[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete.
-
-[runBlocking] and [coroutineScope][_coroutineScope] may look similar because they both wait for their body and all its children to complete.
-The main difference is that the [runBlocking] method _blocks_ the current thread for waiting,
-while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages.
-Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function.
-
-It can be demonstrated by the following example:
-
-```kotlin
-import kotlinx.coroutines.*
-
-fun main() = runBlocking { // this: CoroutineScope
- launch {
- delay(200L)
- println("Task from runBlocking")
- }
-
- coroutineScope { // Creates a coroutine scope
- launch {
- delay(500L)
- println("Task from nested launch")
- }
-
- delay(100L)
- println("Task from coroutine scope") // This line will be printed before the nested launch
- }
-
- println("Coroutine scope is over") // This line is not printed until the nested launch completes
-}
-```
-{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
-
-> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt).
->
-{type="note"}
-
-<!--- TEST
-Task from coroutine scope
-Task from runBlocking
-Task from nested launch
-Coroutine scope is over
--->
+This code produces:
-Note that right after the "Task from coroutine scope" message (while waiting for nested launch)
- "Task from runBlocking" is executed and printed — even though the [coroutineScope][_coroutineScope] is not completed yet.
-
-## Extract function refactoring
-
-Let's extract the block of code inside `launch { ... }` into a separate function. When you
-perform "Extract function" refactoring on this code, you get a new function with the `suspend` modifier.
-This is your first _suspending function_. Suspending functions can be used inside coroutines
-just like regular functions, but their additional feature is that they can, in turn,
-use other suspending functions (like `delay` in this example) to _suspend_ execution of a coroutine.
-
-```kotlin
-import kotlinx.coroutines.*
-
-fun main() = runBlocking {
- launch { doWorld() }
- println("Hello,")
-}
-
-// this is your first suspending function
-suspend fun doWorld() {
- delay(1000L)
- println("World!")
-}
-```
-{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
-
-> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt).
->
-{type="note"}
-
-<!--- TEST
-Hello,
+```text
+Hello
World!
--->
+Done
+```
-But what if the extracted function contains a coroutine builder which is invoked on the current scope?
-In this case, the `suspend` modifier on the extracted function is not enough. Making `doWorld` an extension
-method on `CoroutineScope` is one of the solutions, but it may not always be applicable as it does not make the API clearer.
-The idiomatic solution is to have either an explicit `CoroutineScope` as a field in a class containing the target function
-or an implicit one when the outer class implements `CoroutineScope`.
-As a last resort, [CoroutineScope(coroutineContext)][CoroutineScope()] can be used, but such an approach is structurally unsafe
-because you no longer have control on the scope of execution of this method. Only private APIs can use this builder.
+<!--- TEST -->
## Coroutines ARE light-weight
@@ -305,6 +252,7 @@ Run the following code:
```kotlin
import kotlinx.coroutines.*
+//sampleStart
fun main() = runBlocking {
repeat(100_000) { // launch a lot of coroutines
launch {
@@ -313,9 +261,10 @@ fun main() = runBlocking {
}
}
}
+//sampleEnd
```
-> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt).
+> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt).
>
{type="note"}
@@ -323,57 +272,17 @@ fun main() = runBlocking {
It launches 100K coroutines and, after 5 seconds, each coroutine prints a dot.
-Now, try that with threads. What would happen? (Most likely your code will produce some sort of out-of-memory error)
-
-## Global coroutines are like daemon threads
-
-The following code launches a long-running coroutine in [GlobalScope] that prints "I'm sleeping" twice a second and then
-returns from the main function after some delay:
-
-```kotlin
-import kotlinx.coroutines.*
-
-fun main() = runBlocking {
-//sampleStart
- GlobalScope.launch {
- repeat(1000) { i ->
- println("I'm sleeping $i ...")
- delay(500L)
- }
- }
- delay(1300L) // just quit after delay
-//sampleEnd
-}
-```
-{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}
-
-> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt).
->
-{type="note"}
-
-You can run and see that it prints three lines and terminates:
-
-```text
-I'm sleeping 0 ...
-I'm sleeping 1 ...
-I'm sleeping 2 ...
-```
-
-<!--- TEST -->
-
-Active coroutines that were launched in [GlobalScope] do not keep the process alive. They are like daemon threads.
+Now, try that with threads (remove `runBlocking`, replace `launch` with `thread`, and replace `delay` with `Thread.sleep`).
+What would happen? (Most likely your code will produce some sort of out-of-memory error)
<!--- MODULE kotlinx-coroutines-core -->
<!--- INDEX kotlinx.coroutines -->
[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
-[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
-[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
-[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
-[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html
+[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
-[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html
+[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
<!--- END -->
diff --git a/docs/topics/coroutines-guide.md b/docs/topics/coroutines-guide.md
index 239b2a45..3d857991 100644
--- a/docs/topics/coroutines-guide.md
+++ b/docs/topics/coroutines-guide.md
@@ -17,7 +17,6 @@ In order to use coroutines as well as follow the examples in this guide, you nee
## Table of contents
* [Coroutines basics](coroutines-basics.md)
-* [Tutorial: Create a basic coroutine](coroutines-basic-jvm.md)
* [Hands-on: Intro to coroutines and channels](https://play.kotlinlang.org/hands-on/Introduction%20to%20Coroutines%20and%20Channels)
* [Cancellation and timeouts](cancellation-and-timeouts.md)
* [Composing suspending functions](composing-suspending-functions.md)
@@ -32,6 +31,6 @@ In order to use coroutines as well as follow the examples in this guide, you nee
## Additional references
-* [Guide to UI programming with coroutines](../../ui/coroutines-guide-ui.md)
-* [Coroutines design document (KEEP)](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md)
+* [Guide to UI programming with coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md)
+* [Coroutines design document (KEEP)](https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md)
* [Full kotlinx.coroutines API reference](https://kotlin.github.io/kotlinx.coroutines)
diff --git a/docs/topics/debug-coroutines-with-idea.md b/docs/topics/debug-coroutines-with-idea.md
index c44bafc1..e59075e0 100644
--- a/docs/topics/debug-coroutines-with-idea.md
+++ b/docs/topics/debug-coroutines-with-idea.md
@@ -21,7 +21,7 @@ The tutorial assumes you have prior knowledge of the [coroutines](coroutines-gui
* Use the [`runBlocking()`](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html) block to wrap a coroutine.
* Use the [`async()`](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html) function to create coroutines that compute deferred values `a` and `b`.
* Use the [`await()`](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html) function to await the computation result.
- * Use the [`println()`](https://kotlinlang.org//api/latest/jvm/stdlib/stdlib/kotlin.io/println.html) function to print computing status and the result of multiplication to the output.
+ * Use the [`println()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/println.html) function to print computing status and the result of multiplication to the output.
```kotlin
import kotlinx.coroutines.*
@@ -79,4 +79,4 @@ The tutorial assumes you have prior knowledge of the [coroutines](coroutines-gui
* The second coroutine has computed its value and disappeared.
* The third coroutine is calculating the value of `b` – it has the **RUNNING** status.
-Using IntelliJ IDEA debugger, you can dig deeper into each coroutine to debug your code. \ No newline at end of file
+Using IntelliJ IDEA debugger, you can dig deeper into each coroutine to debug your code.
diff --git a/docs/topics/debugging.md b/docs/topics/debugging.md
index d18df7f4..5ff4d549 100644
--- a/docs/topics/debugging.md
+++ b/docs/topics/debugging.md
@@ -63,7 +63,10 @@ Exception copy logic is straightforward:
1) If the exception class implements [CopyableThrowable], [CopyableThrowable.createCopy] is used.
`null` can be returned from `createCopy` to opt-out specific exception from being recovered.
2) If the exception class has class-specific fields not inherited from Throwable, the exception is not copied.
- 3) Otherwise, one of the public exception's constructor is invoked reflectively with an optional `initCause` call.
+ 3) Otherwise, one of the public exception's constructor is invoked reflectively with an optional `initCause` call.
+ 4) If the reflective copy has a changed message (exception constructor passed a modified `message` parameter to the superclass),
+ the exception is not copied in order to preserve a human-readable message. [CopyableThrowable] does not have such a limitation
+ and allows the copy to have a `message` different from that of the original.
## Debug agent
diff --git a/docs/topics/exception-handling.md b/docs/topics/exception-handling.md
index fbd11313..3facd51a 100644
--- a/docs/topics/exception-handling.md
+++ b/docs/topics/exception-handling.md
@@ -19,9 +19,16 @@ exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.r
It can be demonstrated by a simple example that creates root coroutines using the [GlobalScope]:
+> [GlobalScope] is a delicate API that can backfire in non-trivial ways. Creating a root coroutine for the
+> whole application is one of the rare legitimate uses for `GlobalScope`, so you must explicitly opt-in into
+> using `GlobalScope` with `@OptIn(DelicateCoroutinesApi::class)`.
+>
+{type="note"}
+
```kotlin
import kotlinx.coroutines.*
+@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
val job = GlobalScope.launch { // root coroutine with launch
println("Throwing exception from launch")
@@ -90,6 +97,7 @@ so its `CoroutineExceptionHandler` has no effect either.
```kotlin
import kotlinx.coroutines.*
+@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
//sampleStart
val handler = CoroutineExceptionHandler { _, exception ->
@@ -184,6 +192,7 @@ which is demonstrated by the following example.
```kotlin
import kotlinx.coroutines.*
+@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
//sampleStart
val handler = CoroutineExceptionHandler { _, exception ->
@@ -242,6 +251,7 @@ import kotlinx.coroutines.exceptions.*
import kotlinx.coroutines.*
import java.io.*
+@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}")
@@ -292,6 +302,7 @@ Cancellation exceptions are transparent and are unwrapped by default:
import kotlinx.coroutines.*
import java.io.*
+@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
//sampleStart
val handler = CoroutineExceptionHandler { _, exception ->
@@ -497,7 +508,7 @@ The scope is completed
[CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html
-[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
+[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
[SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html
[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html
@@ -510,4 +521,4 @@ The scope is completed
[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
-<!--- END --> \ No newline at end of file
+<!--- END -->
diff --git a/docs/topics/flow.md b/docs/topics/flow.md
index 9dd84f3f..7acc4f99 100644
--- a/docs/topics/flow.md
+++ b/docs/topics/flow.md
@@ -1852,7 +1852,7 @@ Integration modules include conversions from and to `Flow`, integration with Rea
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
-[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
+[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel.html
[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html
[ensureActive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-active.html
[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html
@@ -1890,7 +1890,7 @@ Integration modules include conversions from and to `Flow`, integration with Rea
[catch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html
[onCompletion]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/on-completion.html
[launchIn]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/launch-in.html
-[IntRange.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/kotlin.ranges.-int-range/as-flow.html
+[IntRange.asFlow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/as-flow.html
[cancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/cancellable.html
<!--- END -->
diff --git a/docs/topics/select-expression.md b/docs/topics/select-expression.md
index 082a50d6..3d20ff39 100644
--- a/docs/topics/select-expression.md
+++ b/docs/topics/select-expression.md
@@ -120,31 +120,32 @@ buzz -> 'Buzz!'
## Selecting on close
The [onReceive][ReceiveChannel.onReceive] clause in `select` fails when the channel is closed causing the corresponding
-`select` to throw an exception. We can use [onReceiveOrNull][onReceiveOrNull] clause to perform a
+`select` to throw an exception. We can use [onReceiveCatching][ReceiveChannel.onReceiveCatching] clause to perform a
specific action when the channel is closed. The following example also shows that `select` is an expression that returns
the result of its selected clause:
```kotlin
suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
select<String> {
- a.onReceiveOrNull { value ->
- if (value == null)
- "Channel 'a' is closed"
- else
+ a.onReceiveCatching { it ->
+ val value = it.getOrNull()
+ if (value != null) {
"a -> '$value'"
+ } else {
+ "Channel 'a' is closed"
+ }
}
- b.onReceiveOrNull { value ->
- if (value == null)
- "Channel 'b' is closed"
- else
+ b.onReceiveCatching { it ->
+ val value = it.getOrNull()
+ if (value != null) {
"b -> '$value'"
+ } else {
+ "Channel 'b' is closed"
+ }
}
}
```
-Note that [onReceiveOrNull][onReceiveOrNull] is an extension function defined only
-for channels with non-nullable elements so that there is no accidental confusion between a closed channel
-and a null value.
Let's use it with channel `a` that produces "Hello" string four times and
channel `b` that produces "World" four times:
@@ -158,17 +159,21 @@ import kotlinx.coroutines.selects.*
suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
select<String> {
- a.onReceiveOrNull { value ->
- if (value == null)
- "Channel 'a' is closed"
- else
+ a.onReceiveCatching { it ->
+ val value = it.getOrNull()
+ if (value != null) {
"a -> '$value'"
+ } else {
+ "Channel 'a' is closed"
+ }
}
- b.onReceiveOrNull { value ->
- if (value == null)
- "Channel 'b' is closed"
- else
+ b.onReceiveCatching { it ->
+ val value = it.getOrNull()
+ if (value != null) {
"b -> '$value'"
+ } else {
+ "Channel 'b' is closed"
+ }
}
}
@@ -215,7 +220,7 @@ the first one among them gets selected. Here, both channels are constantly produ
being the first clause in select, wins. However, because we are using unbuffered channel, the `a` gets suspended from
time to time on its [send][SendChannel.send] invocation and gives a chance for `b` to send, too.
-The second observation, is that [onReceiveOrNull][onReceiveOrNull] gets immediately selected when the
+The second observation, is that [onReceiveCatching][ReceiveChannel.onReceiveCatching] gets immediately selected when the
channel is already closed.
## Selecting to send
@@ -375,19 +380,19 @@ Deferred 4 produced answer 'Waited for 128 ms'
Let us write a channel producer function that consumes a channel of deferred string values, waits for each received
deferred value, but only until the next deferred value comes over or the channel is closed. This example puts together
-[onReceiveOrNull][onReceiveOrNull] and [onAwait][Deferred.onAwait] clauses in the same `select`:
+[onReceiveCatching][ReceiveChannel.onReceiveCatching] and [onAwait][Deferred.onAwait] clauses in the same `select`:
```kotlin
fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce<String> {
var current = input.receive() // start with first received deferred value
while (isActive) { // loop while not cancelled/closed
val next = select<Deferred<String>?> { // return next deferred value from this select or null
- input.onReceiveOrNull { update ->
- update // replaces next value to wait
+ input.onReceiveCatching { update ->
+ update.getOrNull()
}
- current.onAwait { value ->
+ current.onAwait { value ->
send(value) // send value that current deferred has produced
- input.receiveOrNull() // and use the next deferred from the input channel
+ input.receiveCatching().getOrNull() // and use the next deferred from the input channel
}
}
if (next == null) {
@@ -423,12 +428,12 @@ fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) =
var current = input.receive() // start with first received deferred value
while (isActive) { // loop while not cancelled/closed
val next = select<Deferred<String>?> { // return next deferred value from this select or null
- input.onReceiveOrNull { update ->
- update // replaces next value to wait
+ input.onReceiveCatching { update ->
+ update.getOrNull()
}
- current.onAwait { value ->
+ current.onAwait { value ->
send(value) // send value that current deferred has produced
- input.receiveOrNull() // and use the next deferred from the input channel
+ input.receiveCatching().getOrNull() // and use the next deferred from the input channel
}
}
if (next == null) {
@@ -491,7 +496,7 @@ Channel was closed
[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
[ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
-[onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html
+[ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html
[SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/send.html
[SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html
@@ -499,4 +504,4 @@ Channel was closed
[select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html
-<!--- END --> \ No newline at end of file
+<!--- END -->
diff --git a/docs/topics/shared-mutable-state-and-concurrency.md b/docs/topics/shared-mutable-state-and-concurrency.md
index b4b68b30..40b0134a 100644
--- a/docs/topics/shared-mutable-state-and-concurrency.md
+++ b/docs/topics/shared-mutable-state-and-concurrency.md
@@ -2,8 +2,8 @@
[//]: # (title: Shared mutable state and concurrency)
-Coroutines can be executed concurrently using a multi-threaded dispatcher like the [Dispatchers.Default]. It presents
-all the usual concurrency problems. The main problem being synchronization of access to **shared mutable state**.
+Coroutines can be executed parallelly using a multi-threaded dispatcher like the [Dispatchers.Default]. It presents
+all the usual parallelism problems. The main problem being synchronization of access to **shared mutable state**.
Some solutions to this problem in the land of coroutines are similar to the solutions in the multi-threaded world,
but others are unique.
@@ -510,4 +510,4 @@ have to switch to a different context at all.
[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
-<!--- END --> \ No newline at end of file
+<!--- END -->