diff --git a/CHANGES.md b/CHANGES.md
index 089b1991b3..25769073e7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,37 @@
# Change log for kotlinx.coroutines
+## Version 1.5.0-RC
+
+### Channels API
+
+* Major channels API rework (#330, #974). Existing `offer`, `poll`, and `sendBlocking` methods are deprecated, internal `receiveCatching` and `onReceiveCatching` removed, `receiveOrNull` and `onReceiveOrNull` are completely deprecated. Previously deprecated `SendChannel.isFull` declaration is removed. Channel operators deprecated with `ERROR` are now `HIDDEN`.
+* New methods `receiveCatching`, `onReceiveCatching` `trySend`, `tryReceive`, and `trySendBlocking` along with the new result type `ChannelResult` are introduced. They provide better type safety, are less error-prone, and have a consistent future-proof naming scheme. The full rationale behind this change can be found [here](https://github.com/Kotlin/kotlinx.coroutines/issues/974#issuecomment-806569582).
+* `BroadcastChannel` and `ConflatedBroadcastChannel` are marked as `ObsoleteCoroutinesApi` in the favor or `SharedFlow` and `StateFlow`. The migration scheme can be found in their documentation. These classes will be deprecated in the next major release.
+* `callbackFlow` and `channelFlow` are promoted to stable API.
+
+### Reactive integrations
+
+* All existing API in modules `kotlinx-coroutines-rx2`, `kotlinx-coroutines-rx3`, `kotlinx-coroutines-reactive`, `kotlinx-coroutines-reactor`, and `kotlinx-coroutines-jdk9` were revisited and promoted to stable (#2545).
+* `publish` is no longer allowed to emit `null` values (#2646).
+* Misleading `awaitSingleOr*` functions on `Publisher` type are deprecated (#2591).
+* `MaybeSource.await` is deprecated in the favor of `awaitSingle`, additional lint functions for `Mono` are added in order to prevent ambiguous `Publisher` usages (#2628, #1587).
+* `ContextView` support in `kotlinx-coroutines-reactor` (#2575).
+* All reactive builders no longer ignore inner cancellation exceptions preventing their completion (#2262, #2646).
+* `MaybeSource.collect` and `Maybe.collect` properly finish when they are completed without a value (#2617).
+* All exceptions are now consistently handled according to reactive specification, whether they are considered 'fatal' or not by reactive frameworks (#2646).
+
+### Other improvements
+
+* `Flow.last` and `Flow.lastOrNull` operators (#2246).
+* `Flow.runningFold` operator (#2641).
+* `CoroutinesTimeout` rule for JUnit5 (#2197).
+* Internals of `Job` and `AbstractCoroutine` was reworked, resulting in smaller code size, less memory footprint, and better performance (#2513, #2512).
+* `CancellationException` from Kotlin standard library is used for cancellation on Koltin/JS and Kotlin/Native (#2638).
+* Introduced new `DelicateCoroutineApi` annotation that warns users about potential target API pitfalls and suggests studying API's documentation first. The only delicate API right now is `GlobalScope` (#2637).
+* Fixed bug introduced in `1.4.3` when `kotlinx-coroutines-core.jar` triggered IDEA debugger failure (#2619).
+* Fixed memory leak of `ChildHandlerNode` with reusable continuations (#2564).
+* Various documentation improvements (#2555, #2589, #2592, #2583, #2437, #2616, #2633, #2560).
+
## Version 1.4.3
### General changes
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7737062fa3..4628eade47 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -52,6 +52,10 @@ so do familiarize yourself with the following guidelines.
* Follow the style of writing tests that is used in this project:
name test functions as `testXxx`. Don't use backticks in test names.
* If you introduce any new public APIs:
+ * Comment on the existing issue if you want to work on it or create one beforehand.
+ Ensure that the issue not only describes a problem, but also describes a solution that had received a positive feedback. Propose a solution if there isn't any.
+ PRs with new API, but without a corresponding issue with a positive feedback about the proposed implementation are unlikely to
+ be approved or reviewed.
* All new APIs must come with documentation and tests.
* All new APIs are initially released with `@ExperimentalCoroutineApi` annotation and are graduated later.
* [Update the public API dumps](#updating-the-public-api-dump) and commit the resulting changes as well.
@@ -59,8 +63,6 @@ so do familiarize yourself with the following guidelines.
* If you plan large API additions, then please start by submitting an issue with the proposed API design
to gather community feedback.
* [Contact the maintainers](#contacting-maintainers) to coordinate any big piece of work in advance.
-* Comment on the existing issue if you want to work on it. Ensure that the issue not only describes a problem,
- but also describes a solution that had received a positive feedback. Propose a solution if there isn't any.
* Steps for contributing new integration modules are explained [here](integration/README.md#Contributing).
## Building
diff --git a/README.md b/README.md
index 506d181fd5..76adb21241 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,12 @@
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[](https://www.apache.org/licenses/LICENSE-2.0)
-[](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.4.3/pom)
-[](http://kotlinlang.org)
+[](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0-RC/pom)
+[](http://kotlinlang.org)
[](https://kotlinlang.slack.com/messages/coroutines/)
Library support for Kotlin coroutines with [multiplatform](#multiplatform) support.
-This is a companion version for the Kotlin `1.4.30` release.
+This is a companion version for the Kotlin `1.5.0` release.
```kotlin
suspend fun main() = coroutineScope {
@@ -75,10 +75,6 @@ suspend fun main() = coroutineScope {
## Using in your projects
-The libraries are published to [kotlinx](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines) Bintray repository,
-linked to [JCenter](https://bintray.com/bintray/jcenter?filterByPkgName=kotlinx.coroutines) and
-pushed to [Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.jetbrains.kotlinx%20a%3Akotlinx-coroutines*).
-
### Maven
Add dependencies (you can also add other modules that you need):
@@ -87,7 +83,7 @@ Add dependencies (you can also add other modules that you need):
org.jetbrains.kotlinx
kotlinx-coroutines-core
- 1.4.3
+ 1.5.0-RC
```
@@ -95,7 +91,7 @@ And make sure that you use the latest Kotlin version:
```xml
- 1.4.30
+ 1.5.0
```
@@ -105,7 +101,7 @@ Add dependencies (you can also add other modules that you need):
```groovy
dependencies {
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC'
}
```
@@ -113,7 +109,7 @@ And make sure that you use the latest Kotlin version:
```groovy
buildscript {
- ext.kotlin_version = '1.4.30'
+ ext.kotlin_version = '1.5.0'
}
```
@@ -131,7 +127,7 @@ Add dependencies (you can also add other modules that you need):
```groovy
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC")
}
```
@@ -139,7 +135,7 @@ And make sure that you use the latest Kotlin version:
```groovy
plugins {
- kotlin("jvm") version "1.4.30"
+ kotlin("jvm") version "1.5.0"
}
```
@@ -151,7 +147,7 @@ Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android)
module as a dependency when using `kotlinx.coroutines` on Android:
```groovy
-implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
+implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC'
```
This gives you access to the Android [Dispatchers.Main]
@@ -184,7 +180,7 @@ In common code that should get compiled for different platforms, you can add a d
```groovy
commonMain {
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC")
}
}
```
@@ -196,7 +192,7 @@ Platform-specific dependencies are recommended to be used only for non-multiplat
#### JS
Kotlin/JS version of `kotlinx.coroutines` is published as
-[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.4.3/jar)
+[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.5.0-RC/jar)
(follow the link to get the dependency declaration snippet) and as [`kotlinx-coroutines-core`](https://www.npmjs.com/package/kotlinx-coroutines-core) NPM package.
#### Native
diff --git a/build.gradle b/build.gradle
index 80124e7ea0..fc52d8cc53 100644
--- a/build.gradle
+++ b/build.gradle
@@ -53,18 +53,11 @@ buildscript {
}
repositories {
- maven {url "https://kotlin.bintray.com/kotlinx"}
- // Future replacement for kotlin-dev, with cache redirector
- maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
- maven {
- url "https://kotlin.bintray.com/kotlin-dev"
- credentials {
- username = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') ?: ""
- password = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') ?: ""
- }
- }
+ // Leftover until we migrated to Dokka 1.4.30
+ maven { url "https://kotlin.bintray.com/kotlin-dev" }
maven { url "https://jetbrains.bintray.com/kotlin-native-dependencies" }
maven { url "https://plugins.gradle.org/m2/" }
+ maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
}
dependencies {
@@ -150,9 +143,6 @@ allprojects {
*/
google()
mavenCentral()
- // Future replacement for kotlin-dev, with cache redirector
- maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
- maven { url "https://kotlin.bintray.com/kotlinx" }
}
}
@@ -163,9 +153,9 @@ configure(subprojects.findAll { !sourceless.contains(it.name) && it.name != core
apply plugin: "kotlin-${platform}-conventions"
dependencies {
// See comment below for rationale, it will be replaced with "project" dependency
- compile project(":$coreModule")
+ api project(":$coreModule")
// the only way IDEA can resolve test classes
- testCompile project(":$coreModule").kotlin.targets.jvm.compilations.test.output.allOutputs
+ testImplementation project(":$coreModule").kotlin.targets.jvm.compilations.test.output.allOutputs
}
}
@@ -243,7 +233,7 @@ configure(subprojects.findAll { !unpublished.contains(it.name) }) {
if (it.name != 'kotlinx-coroutines-bom') {
apply from: rootProject.file('gradle/dokka.gradle')
}
- apply from: rootProject.file('gradle/publish-bintray.gradle')
+ apply from: rootProject.file('gradle/publish.gradle')
}
configure(subprojects.findAll { !unpublished.contains(it.name) }) {
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 6c373a01f4..80214522b8 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -17,8 +17,10 @@ repositories {
maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev")
} else {
maven("https://plugins.gradle.org/m2")
+ // Leftover until we migrated to Dokka 1.4.30
maven("https://dl.bintray.com/kotlin/kotlin-dev")
}
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
if (buildSnapshotTrain) {
mavenLocal()
diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt
index 5b191bfa38..e0124cd966 100644
--- a/buildSrc/src/main/kotlin/Publishing.kt
+++ b/buildSrc/src/main/kotlin/Publishing.kt
@@ -62,20 +62,6 @@ fun configureMavenPublication(rh: RepositoryHandler, project: Project) {
}
}
-fun configureBintrayPublication(rh: RepositoryHandler, project: Project) {
- rh.maven {
- val user = "kotlin"
- val repo = "kotlinx"
- val name = "kotlinx.coroutines"
- url = URI("https://api.bintray.com/maven/$user/$repo/$name/;publish=0;override=0")
-
- credentials {
- username = project.findProperty("bintrayUser") as? String ?: System.getenv("BINTRAY_USER")
- password = project.findProperty("bintrayApiKey") as? String ?: System.getenv("BINTRAY_API_KEY")
- }
- }
-}
-
fun signPublicationIfKeyPresent(project: Project, publication: MavenPublication) {
val keyId = project.getSensitiveProperty("libs.sign.key.id")
val signingKey = project.getSensitiveProperty("libs.sign.key.private")
diff --git a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts
index 89007718c8..5f3193428c 100644
--- a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts
@@ -22,12 +22,12 @@ if (rootProject.extra.get("jvm_ir_enabled") as Boolean) {
}
dependencies {
- testCompile(kotlin("test"))
+ testImplementation(kotlin("test"))
// Workaround to make addSuppressed work in tests
- testCompile(kotlin("reflect"))
- testCompile(kotlin("stdlib-jdk7"))
- testCompile(kotlin("test-junit"))
- testCompile("junit:junit:${version("junit")}")
+ testImplementation(kotlin("reflect"))
+ testImplementation(kotlin("stdlib-jdk7"))
+ testImplementation(kotlin("test-junit"))
+ testImplementation("junit:junit:${version("junit")}")
}
tasks.compileKotlin {
diff --git a/docs/images/after.png b/docs/images/after.png
index 4ce15e8b4e..b1e138c682 100644
Binary files a/docs/images/after.png and b/docs/images/after.png differ
diff --git a/docs/images/before.png b/docs/images/before.png
index 31b910607b..7386ee213b 100644
Binary files a/docs/images/before.png and b/docs/images/before.png differ
diff --git a/docs/images/new-mvn-project-jvm.png b/docs/images/new-mvn-project-jvm.png
deleted file mode 100644
index 2154a3df3d..0000000000
Binary files a/docs/images/new-mvn-project-jvm.png and /dev/null differ
diff --git a/docs/topics/composing-suspending-functions.md b/docs/topics/composing-suspending-functions.md
index 0af60d00a9..9c1a26a910 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_ — 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
+@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
doSomethingUsefulOne()
}
// The result type of somethingUsefulTwoAsync is Deferred
+@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.
@@ -403,4 +413,4 @@ Computation failed with ArithmeticException
[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
-
\ No newline at end of file
+
diff --git a/docs/topics/coroutine-context-and-dispatchers.md b/docs/topics/coroutine-context-and-dispatchers.md
index 9aae1a7a4b..402db103b4 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 {
//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 {
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
@@ -678,4 +682,4 @@ that should be implemented.
[ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/ensure-present.html
[ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html
-
\ No newline at end of file
+
diff --git a/docs/topics/coroutines-basic-jvm.md b/docs/topics/coroutines-basic-jvm.md
deleted file mode 100644
index 3b067ea86b..0000000000
--- 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**.:
-
-
-
-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:
-
-
-
-```groovy
-dependencies {
- ...
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%'
-}
-```
-
-```kotlin
-dependencies {
- ...
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%")
-}
-```
-
-
-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:
-
-
-
-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
-
- org.jetbrains.kotlin
- kotlin-maven-plugin
- ...
-
-
- -Xcoroutines=enable
-
-
-
-```
-
-Since we'll be using the [`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines), let's add its recent version to our dependencies:
-
-```xml
-
- ...
-
- org.jetbrains.kotlinx
- kotlinx-coroutines-core
- %coroutinesVersion%
-
-
-```
-
-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) {
-//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`, which has an `await()` function that returns the result of the coroutine. `Deferred` 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 c1c17581c0..ab8b427685 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!
```
-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.
-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`).
+[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.
-If you start by replacing `GlobalScope.launch` with `thread`, the compiler produces the following error:
+[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.
+
+[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 — 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
+
+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.
+
+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.
-## Bridging blocking and non-blocking worlds
+## Extract function refactoring
-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:
+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"}
-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 { // 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,41 @@ fun main() = runBlocking { // start main coroutine
>
{type="note"}
+This code also prints:
+
-Here `runBlocking { ... }` 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 is also a way to write unit tests for suspending functions:
-
-
-
-```kotlin
-class MyTest {
- @Test
- fun testMySuspendingFunction() = runBlocking {
- // here we can use suspending functions using any assertion style that we like
- }
-}
-```
-
-
+## Scope builder and concurrency
-## Waiting for a job
-
-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 "Hello"
+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 +192,39 @@ fun main() = runBlocking {
>
{type="note"}
-
-
-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.
+
-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 +233,15 @@ fun main() = runBlocking { // this: CoroutineScope
>
{type="note"}
-
-
-## 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"}
-
-
-
-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"}
+This code produces:
-
+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.
+
## Coroutines ARE light-weight
@@ -305,6 +250,7 @@ Run the following code:
```kotlin
import kotlinx.coroutines.*
+//sampleStart
fun main() = runBlocking {
repeat(100_000) { // launch a lot of coroutines
launch {
@@ -313,9 +259,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 +270,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 ...
-```
-
-
-
-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)
[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
diff --git a/docs/topics/coroutines-guide.md b/docs/topics/coroutines-guide.md
index 239b2a451a..3d857991a1 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/exception-handling.md b/docs/topics/exception-handling.md
index fbd11313cc..4cff42f357 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 ->
@@ -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
-
\ No newline at end of file
+
diff --git a/docs/topics/select-expression.md b/docs/topics/select-expression.md
index 082a50d65b..3d20ff39d4 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, b: ReceiveChannel): String =
select {
- 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, b: ReceiveChannel): String =
select {
- 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>) = produce {
var current = input.receive() // start with first received deferred value
while (isActive) { // loop while not cancelled/closed
val next = select?> { // 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>) =
var current = input.receive() // start with first received deferred value
while (isActive) { // loop while not cancelled/closed
val next = select?> { // 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
-
\ No newline at end of file
+
diff --git a/gradle.properties b/gradle.properties
index db1a782ba9..be8d5fa670 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,16 +3,17 @@
#
# Kotlin
-version=1.4.3-SNAPSHOT
+version=1.5.0-RC-SNAPSHOT
group=org.jetbrains.kotlinx
-kotlin_version=1.4.30
+kotlin_version=1.5.0
# Dependencies
junit_version=4.12
-atomicfu_version=0.15.1
+junit5_version=5.7.0
+atomicfu_version=0.16.1
knit_version=0.2.3
-html_version=0.6.8
-lincheck_version=2.10
+html_version=0.7.2
+lincheck_version=2.12
dokka_version=0.9.16-rdev-2-mpp-hacks
byte_buddy_version=1.10.9
reactor_version=3.4.1
@@ -21,7 +22,7 @@ rxjava2_version=2.2.8
rxjava3_version=3.0.2
javafx_version=11.0.2
javafx_plugin_version=0.0.8
-binary_compatibility_validator_version=0.4.0
+binary_compatibility_validator_version=0.5.0
blockhound_version=1.0.2.RELEASE
jna_version=5.5.0
@@ -54,10 +55,4 @@ jekyll_version=4.0
# TODO: Remove once KT-37187 is fixed
org.gradle.jvmargs=-Xmx2g
-# Workaround for Bintray treating .sha512 files as artifacts
-# https://github.com/gradle/gradle/issues/11412
-systemProp.org.gradle.internal.publish.checksums.insecure=true
-
-# todo:KLUDGE: This is commented out, and the property is set conditionally in build.gradle, because IDEA doesn't work with it.
-#kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
diff --git a/gradle/publish-bintray.gradle b/gradle/publish.gradle
similarity index 84%
rename from gradle/publish-bintray.gradle
rename to gradle/publish.gradle
index e6f0423451..3a0a4224ab 100644
--- a/gradle/publish-bintray.gradle
+++ b/gradle/publish.gradle
@@ -4,7 +4,7 @@
import org.gradle.util.VersionNumber
-// Configures publishing of Maven artifacts to Bintray
+// Configures publishing of Maven artifacts to Maven Central
apply plugin: 'maven'
apply plugin: 'maven-publish'
@@ -37,12 +37,7 @@ if (!isMultiplatform && !isBom) {
publishing {
repositories {
- def bintrayUpload = System.getenv("libs.bintray.upload") != null
- if (bintrayUpload) {
- PublishingKt.configureBintrayPublication(delegate, project)
- } else {
- PublishingKt.configureMavenPublication(delegate, project)
- }
+ PublishingKt.configureMavenPublication(delegate, project)
}
if (!isMultiplatform && !isBom) {
@@ -61,10 +56,7 @@ publishing {
publications.all {
PublishingKt.configureMavenCentralMetadata(pom, project)
- def bintrayUpload = System.getenv("libs.bintray.upload") != null
- if (!bintrayUpload) {
- PublishingKt.signPublicationIfKeyPresent(project, it)
- }
+ PublishingKt.signPublicationIfKeyPresent(project, it)
// add empty javadocs
if (!isBom && it.name != "kotlinMultiplatform") {
it.artifact(javadocJar)
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3a9638d5b1..d7ae858e5a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -4,6 +4,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle
index c5a551a05f..6efa3a14e6 100644
--- a/integration-testing/build.gradle
+++ b/integration-testing/build.gradle
@@ -92,10 +92,10 @@ task coreAgentTest(type: Test) {
}
dependencies {
- testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
- testCompile 'junit:junit:4.12'
- npmTestCompile 'org.apache.commons:commons-compress:1.18'
- npmTestCompile 'com.google.code.gson:gson:2.8.5'
+ testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
+ testImplementation 'junit:junit:4.12'
+ npmTestImplementation 'org.apache.commons:commons-compress:1.18'
+ npmTestImplementation 'com.google.code.gson:gson:2.8.5'
debugAgentTestCompile project(':kotlinx-coroutines-core')
debugAgentTestCompile project(':kotlinx-coroutines-debug')
coreAgentTestCompile project(':kotlinx-coroutines-core')
diff --git a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt
index 53019c4bbe..35e0aeb379 100644
--- a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt
+++ b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt
@@ -299,7 +299,7 @@ private class ToContinuation(
*/
private class ListenableFutureCoroutine(
context: CoroutineContext
-) : AbstractCoroutine(context) {
+) : AbstractCoroutine(context, initParentJob = true, active = true) {
// JobListenableFuture propagates external cancellation to `this` coroutine. See JobListenableFuture.
@JvmField
diff --git a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt
index 9dca9e9b46..c463174a8d 100644
--- a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt
+++ b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.guava
@@ -747,4 +747,12 @@ class ListenableFutureTest : TestBase() {
latch.countDown()
return future
}
+
+ @Test
+ fun testCancelledParent() = runTest({ it is CancellationException }) {
+ cancel()
+ future { expectUnreached() }
+ future(start = CoroutineStart.ATOMIC) { }
+ future(start = CoroutineStart.UNDISPATCHED) { }
+ }
}
diff --git a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt
index b3b45e9dbc..7e9c349c66 100644
--- a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt
+++ b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt
@@ -48,7 +48,7 @@ public fun CoroutineScope.future(
private class CompletableFutureCoroutine(
context: CoroutineContext,
private val future: CompletableFuture
-) : AbstractCoroutine(context), BiConsumer {
+) : AbstractCoroutine(context, initParentJob = true, active = true), BiConsumer {
override fun accept(value: T?, exception: Throwable?) {
cancel()
}
diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt
index 998aaa0835..08e5cdad93 100644
--- a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt
+++ b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.future
@@ -567,4 +567,12 @@ class FutureTest : TestBase() {
assertFailsWith { stage.await() }
finish(4)
}
+
+ @Test
+ fun testCancelledParent() = runTest({ it is java.util.concurrent.CancellationException }) {
+ cancel()
+ future { expectUnreached() }
+ future(start = CoroutineStart.ATOMIC) { }
+ future(start = CoroutineStart.UNDISPATCHED) { }
+ }
}
diff --git a/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt b/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt
index 11ceb1a831..d35ee72de0 100644
--- a/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt
+++ b/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.time
@@ -12,8 +12,7 @@ import org.junit.Test
import java.time.Duration
import kotlin.test.assertEquals
-
-class SampleTest : TestBase() {
+class FlowSampleTest : TestBase() {
@Test
public fun testBasic() = withVirtualTime {
expect(1)
diff --git a/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api b/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api
index a8bf271ba4..6b565d4c1a 100644
--- a/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api
+++ b/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api
@@ -3,11 +3,7 @@ public final class kotlinx/coroutines/slf4j/MDCContext : kotlin/coroutines/Abstr
public fun ()V
public fun (Ljava/util/Map;)V
public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
- public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public final fun getContextMap ()Ljava/util/Map;
- public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
- public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
public synthetic fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)V
public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Ljava/util/Map;)V
public synthetic fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Ljava/lang/Object;
diff --git a/js/example-frontend-js/build.gradle.kts b/js/example-frontend-js/build.gradle.kts
index 5b18966fcd..a78ac3dc6d 100644
--- a/js/example-frontend-js/build.gradle.kts
+++ b/js/example-frontend-js/build.gradle.kts
@@ -21,7 +21,12 @@ kotlin {
}
}
+// For kotlinx-html
+repositories {
+ maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
+}
+
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-html-js:${version("html")}")
- implementation(devNpm("html-webpack-plugin", "3.2.0"))
+ implementation(devNpm("html-webpack-plugin", "5.3.1"))
}
diff --git a/kotlinx-coroutines-core/README.md b/kotlinx-coroutines-core/README.md
index bc5587623a..9fdf418233 100644
--- a/kotlinx-coroutines-core/README.md
+++ b/kotlinx-coroutines-core/README.md
@@ -54,9 +54,9 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio
| ---------------- | --------------------------------------------- | ------------------------------------------------ | --------------------------
| [Job] | [join][Job.join] | [onJoin][Job.onJoin] | [isCompleted][Job.isCompleted]
| [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted]
-| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [offer][kotlinx.coroutines.channels.SendChannel.offer]
-| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
-| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveOrNull][kotlinx.coroutines.channels.receiveOrNull] | [onReceiveOrNull][kotlinx.coroutines.channels.onReceiveOrNull] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
+| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [trySend][kotlinx.coroutines.channels.SendChannel.trySend]
+| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive]
+| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive]
| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock]
| none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none
@@ -133,11 +133,11 @@ Obsolete and deprecated module to test coroutines. Replaced with `kotlinx-corout
[kotlinx.coroutines.channels.ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
[kotlinx.coroutines.channels.SendChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/index.html
[kotlinx.coroutines.channels.SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html
-[kotlinx.coroutines.channels.SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html
+[kotlinx.coroutines.channels.SendChannel.trySend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html
[kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
-[kotlinx.coroutines.channels.ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/poll.html
-[kotlinx.coroutines.channels.receiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/receive-or-null.html
-[kotlinx.coroutines.channels.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html
+[kotlinx.coroutines.channels.ReceiveChannel.tryReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/try-receive.html
+[kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html
+[kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html
diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
index 1d16d31086..ec161ce28e 100644
--- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
+++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
@@ -1,7 +1,5 @@
public abstract class kotlinx/coroutines/AbstractCoroutine : kotlinx/coroutines/JobSupport, kotlin/coroutines/Continuation, kotlinx/coroutines/CoroutineScope, kotlinx/coroutines/Job {
- protected final field parentContext Lkotlin/coroutines/CoroutineContext;
- public fun (Lkotlin/coroutines/CoroutineContext;Z)V
- public synthetic fun (Lkotlin/coroutines/CoroutineContext;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Lkotlin/coroutines/CoroutineContext;ZZ)V
protected fun afterResume (Ljava/lang/Object;)V
protected fun cancellationExceptionMessage ()Ljava/lang/String;
public final fun getContext ()Lkotlin/coroutines/CoroutineContext;
@@ -10,10 +8,8 @@ public abstract class kotlinx/coroutines/AbstractCoroutine : kotlinx/coroutines/
protected fun onCancelled (Ljava/lang/Throwable;Z)V
protected fun onCompleted (Ljava/lang/Object;)V
protected final fun onCompletionInternal (Ljava/lang/Object;)V
- protected fun onStart ()V
public final fun resumeWith (Ljava/lang/Object;)V
public final fun start (Lkotlinx/coroutines/CoroutineStart;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
- public final fun start (Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function1;)V
}
public final class kotlinx/coroutines/AwaitKt {
@@ -89,6 +85,7 @@ public final class kotlinx/coroutines/CancellableContinuationKt {
public abstract interface class kotlinx/coroutines/ChildHandle : kotlinx/coroutines/DisposableHandle {
public abstract fun childCancelled (Ljava/lang/Throwable;)Z
+ public abstract fun getParent ()Lkotlinx/coroutines/Job;
}
public abstract interface class kotlinx/coroutines/ChildJob : kotlinx/coroutines/Job {
@@ -270,7 +267,10 @@ public final class kotlinx/coroutines/Delay$DefaultImpls {
public final class kotlinx/coroutines/DelayKt {
public static final fun awaitCancellation (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun delay (JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun delay-VtjQ1oo (DLkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final fun delay-VtjQ1oo (JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface annotation class kotlinx/coroutines/DelicateCoroutinesApi : java/lang/annotation/Annotation {
}
public final class kotlinx/coroutines/Dispatchers {
@@ -420,6 +420,7 @@ public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlin
public final fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
public final fun getOnJoin ()Lkotlinx/coroutines/selects/SelectClause0;
protected fun handleJobException (Ljava/lang/Throwable;)Z
+ protected final fun initParentJob (Lkotlinx/coroutines/Job;)V
public final fun invokeOnCompletion (Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
public final fun invokeOnCompletion (ZZLkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
public fun isActive ()Z
@@ -431,6 +432,7 @@ public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlin
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
protected fun onCancelling (Ljava/lang/Throwable;)V
protected fun onCompletionInternal (Ljava/lang/Object;)V
+ protected fun onStart ()V
public final fun parentCancelled (Lkotlinx/coroutines/ParentJob;)V
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
public fun plus (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job;
@@ -473,6 +475,7 @@ public final class kotlinx/coroutines/NonDisposableHandle : kotlinx/coroutines/C
public static final field INSTANCE Lkotlinx/coroutines/NonDisposableHandle;
public fun childCancelled (Ljava/lang/Throwable;)Z
public fun dispose ()V
+ public fun getParent ()Lkotlinx/coroutines/Job;
public fun toString ()Ljava/lang/String;
}
@@ -535,9 +538,9 @@ public final class kotlinx/coroutines/TimeoutCancellationException : java/util/c
public final class kotlinx/coroutines/TimeoutKt {
public static final fun withTimeout (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun withTimeout-KLykuaI (DLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final fun withTimeout-KLykuaI (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun withTimeoutOrNull (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun withTimeoutOrNull-KLykuaI (DLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final fun withTimeoutOrNull-KLykuaI (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class kotlinx/coroutines/YieldKt {
@@ -555,6 +558,9 @@ public abstract interface class kotlinx/coroutines/channels/ActorScope : kotlinx
public final class kotlinx/coroutines/channels/ActorScope$DefaultImpls {
public static synthetic fun cancel (Lkotlinx/coroutines/channels/ActorScope;)V
+ public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/ActorScope;)Lkotlinx/coroutines/selects/SelectClause1;
+ public static fun poll (Lkotlinx/coroutines/channels/ActorScope;)Ljava/lang/Object;
+ public static fun receiveOrNull (Lkotlinx/coroutines/channels/ActorScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public abstract interface class kotlinx/coroutines/channels/BroadcastChannel : kotlinx/coroutines/channels/SendChannel {
@@ -566,6 +572,7 @@ public abstract interface class kotlinx/coroutines/channels/BroadcastChannel : k
public final class kotlinx/coroutines/channels/BroadcastChannel$DefaultImpls {
public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/BroadcastChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z
public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/BroadcastChannel;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V
+ public static fun offer (Lkotlinx/coroutines/channels/BroadcastChannel;Ljava/lang/Object;)Z
}
public final class kotlinx/coroutines/channels/BroadcastChannelKt {
@@ -598,6 +605,10 @@ public abstract interface class kotlinx/coroutines/channels/Channel : kotlinx/co
public final class kotlinx/coroutines/channels/Channel$DefaultImpls {
public static synthetic fun cancel (Lkotlinx/coroutines/channels/Channel;)V
+ public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/Channel;)Lkotlinx/coroutines/selects/SelectClause1;
+ public static fun offer (Lkotlinx/coroutines/channels/Channel;Ljava/lang/Object;)Z
+ public static fun poll (Lkotlinx/coroutines/channels/Channel;)Ljava/lang/Object;
+ public static fun receiveOrNull (Lkotlinx/coroutines/channels/Channel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class kotlinx/coroutines/channels/Channel$Factory {
@@ -623,125 +634,107 @@ public final class kotlinx/coroutines/channels/ChannelKt {
public static final fun Channel (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/channels/Channel;
public static synthetic fun Channel$default (IILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel;
public static synthetic fun Channel$default (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel;
+ public static final fun getOrElse-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
+ public static final fun onClosed-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
+ public static final fun onFailure-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
+ public static final fun onSuccess-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
+}
+
+public final class kotlinx/coroutines/channels/ChannelResult {
+ public static final field Companion Lkotlinx/coroutines/channels/ChannelResult$Companion;
+ public static final synthetic fun box-impl (Ljava/lang/Object;)Lkotlinx/coroutines/channels/ChannelResult;
+ public static fun constructor-impl (Ljava/lang/Object;)Ljava/lang/Object;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Ljava/lang/Object;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Ljava/lang/Object;Ljava/lang/Object;)Z
+ public static final fun exceptionOrNull-impl (Ljava/lang/Object;)Ljava/lang/Throwable;
+ public static final fun getOrNull-impl (Ljava/lang/Object;)Ljava/lang/Object;
+ public static final fun getOrThrow-impl (Ljava/lang/Object;)Ljava/lang/Object;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Ljava/lang/Object;)I
+ public static final fun isClosed-impl (Ljava/lang/Object;)Z
+ public static final fun isFailure-impl (Ljava/lang/Object;)Z
+ public static final fun isSuccess-impl (Ljava/lang/Object;)Z
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Ljava/lang/Object;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Ljava/lang/Object;
+}
+
+public final class kotlinx/coroutines/channels/ChannelResult$Companion {
+ public final fun closed-JP2dKIU (Ljava/lang/Throwable;)Ljava/lang/Object;
+ public final fun failure-PtdJZtk ()Ljava/lang/Object;
+ public final fun success-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object;
}
public final class kotlinx/coroutines/channels/ChannelsKt {
- public static final fun all (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun associate (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun associateBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun associateBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun associateByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun associateByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun associateTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun cancelConsumed (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;)V
public static final fun consume (Lkotlinx/coroutines/channels/BroadcastChannel;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun consume (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun consumeEach (Lkotlinx/coroutines/channels/BroadcastChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun consumeEach (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun consumeEachIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun consumes (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlin/jvm/functions/Function1;
public static final fun consumesAll ([Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlin/jvm/functions/Function1;
- public static final fun count (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun count (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun distinct (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun count (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun distinct (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final fun distinctBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun distinctBy$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun drop (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun drop (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun drop$default (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun dropWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun dropWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun dropWhile$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun elementAt (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun elementAtOrElse (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun elementAtOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun elementAt (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun elementAtOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun filter (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun filter$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun filterIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun filterIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun filterIndexed$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun filterIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun filterIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun filterNot (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun filterNot (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun filterNot$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final fun filterNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun filterNotTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun filterNotTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun filterTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun filterTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun find (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun findLast (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun first (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun first (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun firstOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun firstOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun flatMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun first (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun firstOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun flatMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun flatMap$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun fold (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun foldIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun groupBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun groupBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun groupByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun groupByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun indexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun indexOfFirst (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun indexOfLast (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun last (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun last (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun lastIndexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun lastOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun lastOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun indexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun last (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun lastIndexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun lastOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun map (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun map$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final fun mapIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun mapIndexed$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun mapIndexedNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun mapIndexedNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun mapIndexedNotNull$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun mapIndexedNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun mapIndexedNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun mapIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun mapIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun mapNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun mapNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun mapNotNull$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun mapNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun mapNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun mapTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun mapTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun maxBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun maxWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun minBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun minWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun none (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun none (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun maxWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun minWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun none (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun onReceiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/selects/SelectClause1;
- public static final fun partition (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun receiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun reduce (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun reduceIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun requireNoNulls (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun requireNoNulls (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final fun sendBlocking (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Object;)V
- public static final fun single (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun single (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun singleOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun singleOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun sumBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun sumByDouble (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun take (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun single (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun singleOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun take (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun take$default (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun takeWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun takeWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun takeWhile$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final fun toChannel (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun toCollection (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun toList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun toMutableList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final synthetic fun toMutableList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun toMutableSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun toSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun withIndex (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun toSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final fun trySendBlocking (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Object;)Ljava/lang/Object;
+ public static final synthetic fun withIndex (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun withIndex$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
- public static final fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
+ public static final synthetic fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static final fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel;
public static synthetic fun zip$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel;
}
@@ -765,10 +758,10 @@ public final class kotlinx/coroutines/channels/ConflatedBroadcastChannel : kotli
public final fun getValueOrNull ()Ljava/lang/Object;
public fun invokeOnClose (Lkotlin/jvm/functions/Function1;)V
public fun isClosedForSend ()Z
- public fun isFull ()Z
public fun offer (Ljava/lang/Object;)Z
public fun openSubscription ()Lkotlinx/coroutines/channels/ReceiveChannel;
public fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public fun trySend-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object;
}
public final class kotlinx/coroutines/channels/ProduceKt {
@@ -784,26 +777,34 @@ public abstract interface class kotlinx/coroutines/channels/ProducerScope : kotl
public abstract fun getChannel ()Lkotlinx/coroutines/channels/SendChannel;
}
+public final class kotlinx/coroutines/channels/ProducerScope$DefaultImpls {
+ public static fun offer (Lkotlinx/coroutines/channels/ProducerScope;Ljava/lang/Object;)Z
+}
+
public abstract interface class kotlinx/coroutines/channels/ReceiveChannel {
public abstract synthetic fun cancel ()V
public abstract synthetic fun cancel (Ljava/lang/Throwable;)Z
public abstract fun cancel (Ljava/util/concurrent/CancellationException;)V
public abstract fun getOnReceive ()Lkotlinx/coroutines/selects/SelectClause1;
- public abstract fun getOnReceiveOrClosed ()Lkotlinx/coroutines/selects/SelectClause1;
+ public abstract fun getOnReceiveCatching ()Lkotlinx/coroutines/selects/SelectClause1;
public abstract fun getOnReceiveOrNull ()Lkotlinx/coroutines/selects/SelectClause1;
public abstract fun isClosedForReceive ()Z
public abstract fun isEmpty ()Z
public abstract fun iterator ()Lkotlinx/coroutines/channels/ChannelIterator;
public abstract fun poll ()Ljava/lang/Object;
public abstract fun receive (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public abstract fun receiveOrClosed-WVj179g (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun receiveCatching-JP2dKIU (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun receiveOrNull (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun tryReceive-PtdJZtk ()Ljava/lang/Object;
}
public final class kotlinx/coroutines/channels/ReceiveChannel$DefaultImpls {
public static synthetic fun cancel (Lkotlinx/coroutines/channels/ReceiveChannel;)V
public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z
public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V
+ public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/selects/SelectClause1;
+ public static fun poll (Lkotlinx/coroutines/channels/ReceiveChannel;)Ljava/lang/Object;
+ public static fun receiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public abstract interface class kotlinx/coroutines/channels/SendChannel {
@@ -811,13 +812,14 @@ public abstract interface class kotlinx/coroutines/channels/SendChannel {
public abstract fun getOnSend ()Lkotlinx/coroutines/selects/SelectClause2;
public abstract fun invokeOnClose (Lkotlin/jvm/functions/Function1;)V
public abstract fun isClosedForSend ()Z
- public abstract fun isFull ()Z
public abstract fun offer (Ljava/lang/Object;)Z
public abstract fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun trySend-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object;
}
public final class kotlinx/coroutines/channels/SendChannel$DefaultImpls {
public static synthetic fun close$default (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z
+ public static fun offer (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Object;)Z
}
public final class kotlinx/coroutines/channels/TickerChannelsKt {
@@ -832,23 +834,6 @@ public final class kotlinx/coroutines/channels/TickerMode : java/lang/Enum {
public static fun values ()[Lkotlinx/coroutines/channels/TickerMode;
}
-public final class kotlinx/coroutines/channels/ValueOrClosed {
- public static final field Companion Lkotlinx/coroutines/channels/ValueOrClosed$Companion;
- public static final synthetic fun box-impl (Ljava/lang/Object;)Lkotlinx/coroutines/channels/ValueOrClosed;
- public fun equals (Ljava/lang/Object;)Z
- public static fun equals-impl (Ljava/lang/Object;Ljava/lang/Object;)Z
- public static final fun equals-impl0 (Ljava/lang/Object;Ljava/lang/Object;)Z
- public static final fun getCloseCause-impl (Ljava/lang/Object;)Ljava/lang/Throwable;
- public static final fun getValue-impl (Ljava/lang/Object;)Ljava/lang/Object;
- public static final fun getValueOrNull-impl (Ljava/lang/Object;)Ljava/lang/Object;
- public fun hashCode ()I
- public static fun hashCode-impl (Ljava/lang/Object;)I
- public static final fun isClosed-impl (Ljava/lang/Object;)Z
- public fun toString ()Ljava/lang/String;
- public static fun toString-impl (Ljava/lang/Object;)Ljava/lang/String;
- public final synthetic fun unbox-impl ()Ljava/lang/Object;
-}
-
public final class kotlinx/coroutines/debug/internal/DebugCoroutineInfo {
public fun (Lkotlinx/coroutines/debug/internal/DebugCoroutineInfoImpl;Lkotlin/coroutines/CoroutineContext;)V
public final fun getContext ()Lkotlin/coroutines/CoroutineContext;
@@ -939,7 +924,7 @@ public final class kotlinx/coroutines/flow/FlowKt {
public static final fun count (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun debounce (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow;
public static final fun debounce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow;
- public static final fun debounce-HG0u8IE (Lkotlinx/coroutines/flow/Flow;D)Lkotlinx/coroutines/flow/Flow;
+ public static final fun debounce-HG0u8IE (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow;
public static final fun debounceDuration (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow;
public static final fun delayEach (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow;
public static final fun delayFlow (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow;
@@ -980,6 +965,8 @@ public final class kotlinx/coroutines/flow/FlowKt {
public static final fun fold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun forEach (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)V
public static final fun getDEFAULT_CONCURRENCY ()I
+ public static final fun last (Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public static final fun lastOrNull (Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun launchIn (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job;
public static final fun map (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
public static final fun mapLatest (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
@@ -1013,9 +1000,10 @@ public final class kotlinx/coroutines/flow/FlowKt {
public static synthetic fun retry$default (Lkotlinx/coroutines/flow/Flow;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun retry$default (Lkotlinx/coroutines/flow/Flow;JLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun retryWhen (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function4;)Lkotlinx/coroutines/flow/Flow;
+ public static final fun runningFold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
public static final fun runningReduce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
public static final fun sample (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow;
- public static final fun sample-HG0u8IE (Lkotlinx/coroutines/flow/Flow;D)Lkotlinx/coroutines/flow/Flow;
+ public static final fun sample-HG0u8IE (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow;
public static final fun scan (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
public static final fun scanFold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
public static final fun scanReduce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
@@ -1102,8 +1090,8 @@ public final class kotlinx/coroutines/flow/SharingStarted$Companion {
}
public final class kotlinx/coroutines/flow/SharingStartedKt {
- public static final fun WhileSubscribed-5qebJ5I (Lkotlinx/coroutines/flow/SharingStarted$Companion;DD)Lkotlinx/coroutines/flow/SharingStarted;
- public static synthetic fun WhileSubscribed-5qebJ5I$default (Lkotlinx/coroutines/flow/SharingStarted$Companion;DDILjava/lang/Object;)Lkotlinx/coroutines/flow/SharingStarted;
+ public static final fun WhileSubscribed-5qebJ5I (Lkotlinx/coroutines/flow/SharingStarted$Companion;JJ)Lkotlinx/coroutines/flow/SharingStarted;
+ public static synthetic fun WhileSubscribed-5qebJ5I$default (Lkotlinx/coroutines/flow/SharingStarted$Companion;JJILjava/lang/Object;)Lkotlinx/coroutines/flow/SharingStarted;
}
public abstract interface class kotlinx/coroutines/flow/StateFlow : kotlinx/coroutines/flow/SharedFlow {
@@ -1120,7 +1108,6 @@ public abstract class kotlinx/coroutines/flow/internal/ChannelFlow : kotlinx/cor
public final field onBufferOverflow Lkotlinx/coroutines/channels/BufferOverflow;
public fun (Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)V
protected fun additionalToStringProps ()Ljava/lang/String;
- public fun broadcastImpl (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/CoroutineStart;)Lkotlinx/coroutines/channels/BroadcastChannel;
public fun collect (Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected abstract fun collectTo (Lkotlinx/coroutines/channels/ProducerScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected abstract fun create (Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)Lkotlinx/coroutines/flow/internal/ChannelFlow;
@@ -1234,7 +1221,7 @@ public abstract interface class kotlinx/coroutines/selects/SelectInstance {
}
public final class kotlinx/coroutines/selects/SelectKt {
- public static final fun onTimeout-8Mi8wO0 (Lkotlinx/coroutines/selects/SelectBuilder;DLkotlin/jvm/functions/Function1;)V
+ public static final fun onTimeout-8Mi8wO0 (Lkotlinx/coroutines/selects/SelectBuilder;JLkotlin/jvm/functions/Function1;)V
public static final fun select (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle
index c2a57f9d9e..4ea81d3de3 100644
--- a/kotlinx-coroutines-core/build.gradle
+++ b/kotlinx-coroutines-core/build.gradle
@@ -208,8 +208,20 @@ jvmTest {
}
}
-jvmJar {
- manifest {
+// Setup manifest for kotlinx-coroutines-core-jvm.jar
+jvmJar { setupManifest(it) }
+
+/*
+ * Setup manifest for kotlinx-coroutines-core.jar
+ * This is convenient for users that pass -javaagent arg manually and also is a workaround #2619 and KTIJ-5659.
+ * This manifest contains reference to AgentPremain that belongs to
+ * kotlinx-coroutines-core-jvm, but our resolving machinery guarantees that
+ * any JVM project that depends on -core artifact also depends on -core-jvm one.
+ */
+metadataJar { setupManifest(it) }
+
+static def setupManifest(Jar jar) {
+ jar.manifest {
attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain"
attributes "Can-Retransform-Classes": "true"
}
diff --git a/kotlinx-coroutines-core/common/README.md b/kotlinx-coroutines-core/common/README.md
index 6712648ae8..e8503d0d16 100644
--- a/kotlinx-coroutines-core/common/README.md
+++ b/kotlinx-coroutines-core/common/README.md
@@ -59,7 +59,7 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio
| [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted]
| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [offer][kotlinx.coroutines.channels.SendChannel.offer]
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
-| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveOrNull][kotlinx.coroutines.channels.receiveOrNull] | [onReceiveOrNull][kotlinx.coroutines.channels.onReceiveOrNull] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
+| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock]
| none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none
@@ -149,8 +149,8 @@ Low-level primitives for finer-grained control of coroutines.
[kotlinx.coroutines.channels.SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html
[kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
[kotlinx.coroutines.channels.ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/poll.html
-[kotlinx.coroutines.channels.receiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/receive-or-null.html
-[kotlinx.coroutines.channels.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html
+[kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html
+[kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html
diff --git a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt
index af392b63f5..439a9ac7a5 100644
--- a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt
+++ b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt
@@ -8,7 +8,6 @@ package kotlinx.coroutines
import kotlinx.coroutines.CoroutineStart.*
import kotlinx.coroutines.intrinsics.*
import kotlin.coroutines.*
-import kotlin.jvm.*
/**
* Abstract base class for implementation of coroutines in coroutine builders.
@@ -26,6 +25,9 @@ import kotlin.jvm.*
* * [onCancelled] in invoked when the coroutine completes with an exception (cancelled).
*
* @param parentContext the context of the parent coroutine.
+ * @param initParentJob specifies whether the parent-child relationship should be instantiated directly
+ * in `AbstractCoroutine` constructor. If set to `false`, it's the responsibility of the child class
+ * to invoke [initParentJob] manually.
* @param active when `true` (by default), the coroutine is created in the _active_ state, otherwise it is created in the _new_ state.
* See [Job] for details.
*
@@ -33,13 +35,22 @@ import kotlin.jvm.*
*/
@InternalCoroutinesApi
public abstract class AbstractCoroutine(
- /**
- * The context of the parent coroutine.
- */
- @JvmField
- protected val parentContext: CoroutineContext,
- active: Boolean = true
+ parentContext: CoroutineContext,
+ initParentJob: Boolean,
+ active: Boolean
) : JobSupport(active), Job, Continuation, CoroutineScope {
+
+ init {
+ /*
+ * Setup parent-child relationship between the parent in the context and the current coroutine.
+ * It may cause this coroutine to become _cancelling_ if the parent is already cancelled.
+ * It is dangerous to install parent-child relationship here if the coroutine class
+ * operates its state from within onCancelled or onCancelling
+ * (with exceptions for rx integrations that can't have any parent)
+ */
+ if (initParentJob) initParentJob(parentContext[Job])
+ }
+
/**
* The context of this coroutine that includes this coroutine as a [Job].
*/
@@ -53,28 +64,6 @@ public abstract class AbstractCoroutine(
override val isActive: Boolean get() = super.isActive
- /**
- * Initializes the parent job from the `parentContext` of this coroutine that was passed to it during construction.
- * It shall be invoked at most once after construction after all other initialization.
- *
- * Invocation of this function may cause this coroutine to become cancelled if the parent is already cancelled,
- * in which case it synchronously invokes all the corresponding handlers.
- * @suppress **This is unstable API and it is subject to change.**
- */
- internal fun initParentJob() {
- initParentJobInternal(parentContext[Job])
- }
-
- /**
- * This function is invoked once when a non-active coroutine (constructed with `active` set to `false)
- * is [started][start].
- */
- protected open fun onStart() {}
-
- internal final override fun onStartInternal() {
- onStart()
- }
-
/**
* This function is invoked once when the job was completed normally with the specified [value],
* right before all the waiters for the coroutine's completion are notified.
@@ -127,26 +116,6 @@ public abstract class AbstractCoroutine(
/**
* Starts this coroutine with the given code [block] and [start] strategy.
* This function shall be invoked at most once on this coroutine.
- *
- * First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it
- * during construction. Second, it starts the coroutine based on [start] parameter:
- *
- * * [DEFAULT] uses [startCoroutineCancellable].
- * * [ATOMIC] uses [startCoroutine].
- * * [UNDISPATCHED] uses [startCoroutineUndispatched].
- * * [LAZY] does nothing.
- */
- public fun start(start: CoroutineStart, block: suspend () -> T) {
- initParentJob()
- start(block, this)
- }
-
- /**
- * Starts this coroutine with the given code [block] and [start] strategy.
- * This function shall be invoked at most once on this coroutine.
- *
- * First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it
- * during construction. Second, it starts the coroutine based on [start] parameter:
*
* * [DEFAULT] uses [startCoroutineCancellable].
* * [ATOMIC] uses [startCoroutine].
@@ -154,7 +123,6 @@ public abstract class AbstractCoroutine(
* * [LAZY] does nothing.
*/
public fun start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
- initParentJob()
start(block, receiver, this)
}
}
diff --git a/kotlinx-coroutines-core/common/src/Annotations.kt b/kotlinx-coroutines-core/common/src/Annotations.kt
index 70adad9b97..724cc8cb87 100644
--- a/kotlinx-coroutines-core/common/src/Annotations.kt
+++ b/kotlinx-coroutines-core/common/src/Annotations.kt
@@ -6,6 +6,22 @@ package kotlinx.coroutines
import kotlinx.coroutines.flow.*
+/**
+ * Marks declarations in the coroutines that are **delicate** —
+ * they have limited use-case and shall be used with care in general code.
+ * Any use of a delicate declaration has to be carefully reviewed to make sure it is
+ * properly used and does not create problems like memory and resource leaks.
+ * Carefully read documentation of any declaration marked as `DelicateCoroutinesApi`.
+ */
+@MustBeDocumented
+@Retention(value = AnnotationRetention.BINARY)
+@RequiresOptIn(
+ level = RequiresOptIn.Level.WARNING,
+ message = "This is a delicate API and its use requires care." +
+ " Make sure you fully read and understand documentation of the declaration that is marked as a delicate API."
+)
+public annotation class DelicateCoroutinesApi
+
/**
* Marks declarations that are still **experimental** in coroutines API, which means that the design of the
* corresponding declarations has open issues which may (or may not) lead to their changes in the future.
@@ -59,7 +75,7 @@ public annotation class ObsoleteCoroutinesApi
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, AnnotationTarget.PROPERTY)
@RequiresOptIn(
level = RequiresOptIn.Level.ERROR, message = "This is an internal kotlinx.coroutines API that " +
- "should not be used from outside of kotlinx.coroutines. No compatibility guarantees are provided." +
+ "should not be used from outside of kotlinx.coroutines. No compatibility guarantees are provided. " +
"It is recommended to report your use-case of internal API to kotlinx.coroutines issue tracker, " +
"so stable API could be provided instead"
)
diff --git a/kotlinx-coroutines-core/common/src/Builders.common.kt b/kotlinx-coroutines-core/common/src/Builders.common.kt
index 93b3ee4849..a11ffe9eb4 100644
--- a/kotlinx-coroutines-core/common/src/Builders.common.kt
+++ b/kotlinx-coroutines-core/common/src/Builders.common.kt
@@ -96,7 +96,7 @@ public fun CoroutineScope.async(
private open class DeferredCoroutine(
parentContext: CoroutineContext,
active: Boolean
-) : AbstractCoroutine(parentContext, active), Deferred, SelectClause1 {
+) : AbstractCoroutine(parentContext, true, active = active), Deferred, SelectClause1 {
override fun getCompleted(): T = getCompletedInternal() as T
override suspend fun await(): T = awaitInternal() as T
override val onAwait: SelectClause1 get() = this
@@ -150,7 +150,7 @@ public suspend fun withContext(
val oldContext = uCont.context
val newContext = oldContext + context
// always check for cancellation of new context
- newContext.checkCompletion()
+ newContext.ensureActive()
// FAST PATH #1 -- new context is the same as the old one
if (newContext === oldContext) {
val coroutine = ScopeCoroutine(newContext, uCont)
@@ -167,7 +167,6 @@ public suspend fun withContext(
}
// SLOW PATH -- use new dispatcher
val coroutine = DispatchedCoroutine(newContext, uCont)
- coroutine.initParentJob()
block.startCoroutineCancellable(coroutine, coroutine)
coroutine.getResult()
}
@@ -188,7 +187,7 @@ public suspend inline operator fun CoroutineDispatcher.invoke(
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
-) : AbstractCoroutine(parentContext, active) {
+) : AbstractCoroutine(parentContext, initParentJob = true, active = active) {
override fun handleJobException(exception: Throwable): Boolean {
handleCoroutineException(context, exception)
return true
diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt
index 8f589912a0..b133b7935d 100644
--- a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt
+++ b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt
@@ -104,8 +104,10 @@ public interface CancellableContinuation : Continuation {
public fun completeResume(token: Any)
/**
- * Legacy function that turned on cancellation behavior in [suspendCancellableCoroutine] before kotlinx.coroutines 1.1.0.
- * This function does nothing and is left only for binary compatibility with old compiled code.
+ * Internal function that setups cancellation behavior in [suspendCancellableCoroutine].
+ * It's illegal to call this function in any non-`kotlinx.coroutines` code and
+ * such calls lead to undefined behaviour.
+ * Exposed in our ABI since 1.0.0 withing `suspendCancellableCoroutine` body.
*
* @suppress **This is unstable API and it is subject to change.**
*/
@@ -332,7 +334,7 @@ internal suspend inline fun suspendCancellableCoroutineReusable(
internal fun getOrCreateCancellableContinuation(delegate: Continuation): CancellableContinuationImpl {
// If used outside of our dispatcher
if (delegate !is DispatchedContinuation) {
- return CancellableContinuationImpl(delegate, MODE_CANCELLABLE_REUSABLE)
+ return CancellableContinuationImpl(delegate, MODE_CANCELLABLE)
}
/*
* Attempt to claim reusable instance.
diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
index 1a8f35663a..f74155eb8f 100644
--- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
+++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
@@ -72,10 +72,7 @@ internal open class CancellableContinuationImpl(
*/
private val _state = atomic(Active)
- private val _parentHandle = atomic(null)
- private var parentHandle: DisposableHandle?
- get() = _parentHandle.value
- set(value) { _parentHandle.value = value }
+ private var parentHandle: DisposableHandle? = null
internal val state: Any? get() = _state.value
@@ -93,7 +90,21 @@ internal open class CancellableContinuationImpl(
}
public override fun initCancellability() {
- setupCancellation()
+ /*
+ * Invariant: at the moment of invocation, `this` has not yet
+ * leaked to user code and no one is able to invoke `resume` or `cancel`
+ * on it yet. Also, this function is not invoked for reusable continuations.
+ */
+ val handle = installParentHandle()
+ ?: return // fast path -- don't do anything without parent
+ // now check our state _after_ registering, could have completed while we were registering,
+ // but only if parent was cancelled. Parent could be in a "cancelling" state for a while,
+ // so we are helping it and cleaning the node ourselves
+ if (isCompleted) {
+ // Can be invoked concurrently in 'parentCancelled', no problems here
+ handle.dispose()
+ parentHandle = NonDisposableHandle
+ }
}
private fun isReusable(): Boolean = delegate is DispatchedContinuation<*> && delegate.isReusable(this)
@@ -118,40 +129,6 @@ internal open class CancellableContinuationImpl(
return true
}
- /**
- * Setups parent cancellation and checks for postponed cancellation in the case of reusable continuations.
- * It is only invoked from an internal [getResult] function for reusable continuations
- * and from [suspendCancellableCoroutine] to establish a cancellation before registering CC anywhere.
- */
- private fun setupCancellation() {
- if (checkCompleted()) return
- if (parentHandle !== null) return // fast path 2 -- was already initialized
- val parent = delegate.context[Job] ?: return // fast path 3 -- don't do anything without parent
- val handle = parent.invokeOnCompletion(
- onCancelling = true,
- handler = ChildContinuation(this).asHandler
- )
- parentHandle = handle
- // now check our state _after_ registering (could have completed while we were registering)
- // Also note that we do not dispose parent for reusable continuations, dispatcher will do that for us
- if (isCompleted && !isReusable()) {
- handle.dispose() // it is Ok to call dispose twice -- here and in disposeParentHandle
- parentHandle = NonDisposableHandle // release it just in case, to aid GC
- }
- }
-
- private fun checkCompleted(): Boolean {
- val completed = isCompleted
- if (!resumeMode.isReusableMode) return completed // Do not check postponed cancellation for non-reusable continuations
- val dispatched = delegate as? DispatchedContinuation<*> ?: return completed
- val cause = dispatched.checkPostponedCancellation(this) ?: return completed
- if (!completed) {
- // Note: this cancel may fail if one more concurrent cancel is currently being invoked
- cancel(cause)
- }
- return true
- }
-
public override val callerFrame: CoroutineStackFrame?
get() = delegate as? CoroutineStackFrame
@@ -188,7 +165,9 @@ internal open class CancellableContinuationImpl(
*/
private fun cancelLater(cause: Throwable): Boolean {
if (!resumeMode.isReusableMode) return false
- val dispatched = (delegate as? DispatchedContinuation<*>) ?: return false
+ // Ensure that we are postponing cancellation to the right instance
+ if (!isReusable()) return false
+ val dispatched = delegate as DispatchedContinuation<*>
return dispatched.postponeCancellation(cause)
}
@@ -216,7 +195,7 @@ internal open class CancellableContinuationImpl(
private inline fun callCancelHandlerSafely(block: () -> Unit) {
try {
- block()
+ block()
} catch (ex: Throwable) {
// Handler should never fail, if it does -- it is an unhandled exception
handleCoroutineException(
@@ -276,9 +255,37 @@ internal open class CancellableContinuationImpl(
@PublishedApi
internal fun getResult(): Any? {
- setupCancellation()
- if (trySuspend()) return COROUTINE_SUSPENDED
+ val isReusable = isReusable()
+ // trySuspend may fail either if 'block' has resumed/cancelled a continuation
+ // or we got async cancellation from parent.
+ if (trySuspend()) {
+ /*
+ * Invariant: parentHandle is `null` *only* for reusable continuations.
+ * We were neither resumed nor cancelled, time to suspend.
+ * But first we have to install parent cancellation handle (if we didn't yet),
+ * so CC could be properly resumed on parent cancellation.
+ *
+ * This read has benign data-race with write of 'NonDisposableHandle'
+ * in 'detachChildIfNotReusable'.
+ */
+ if (parentHandle == null) {
+ installParentHandle()
+ }
+ /*
+ * Release the continuation after installing the handle (if needed).
+ * If we were successful, then do nothing, it's ok to reuse the instance now.
+ * Otherwise, dispose the handle by ourselves.
+ */
+ if (isReusable) {
+ releaseClaimedReusableContinuation()
+ }
+ return COROUTINE_SUSPENDED
+ }
// otherwise, onCompletionInternal was already invoked & invoked tryResume, and the result is in the state
+ if (isReusable) {
+ // release claimed reusable continuation for the future reuse
+ releaseClaimedReusableContinuation()
+ }
val state = this.state
if (state is CompletedExceptionally) throw recoverStackTrace(state.cause, this)
// if the parent job was already cancelled, then throw the corresponding cancellation exception
@@ -296,6 +303,28 @@ internal open class CancellableContinuationImpl(
return getSuccessfulResult(state)
}
+ private fun installParentHandle(): DisposableHandle? {
+ val parent = context[Job] ?: return null // don't do anything without a parent
+ // Install the handle
+ val handle = parent.invokeOnCompletion(
+ onCancelling = true,
+ handler = ChildContinuation(this).asHandler
+ )
+ parentHandle = handle
+ return handle
+ }
+
+ /**
+ * Tries to release reusable continuation. It can fail is there was an asynchronous cancellation,
+ * in which case it detaches from the parent and cancels this continuation.
+ */
+ private fun releaseClaimedReusableContinuation() {
+ // Cannot be casted if e.g. invoked from `installParentHandleReusable` for context without dispatchers, but with Job in it
+ val cancellationCause = (delegate as? DispatchedContinuation<*>)?.tryReleaseClaimedContinuation(this) ?: return
+ detachChild()
+ cancel(cancellationCause)
+ }
+
override fun resumeWith(result: Result) =
resumeImpl(result.toState(this), resumeMode)
@@ -462,11 +491,10 @@ internal open class CancellableContinuationImpl(
/**
* Detaches from the parent.
- * Invariant: used from [CoroutineDispatcher.releaseInterceptedContinuation] iff [isReusable] is `true`
*/
internal fun detachChild() {
- val handle = parentHandle
- handle?.dispose()
+ val handle = parentHandle ?: return
+ handle.dispose()
parentHandle = NonDisposableHandle
}
diff --git a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt
index c80737968e..5e76593df2 100644
--- a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt
+++ b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt
@@ -80,7 +80,7 @@ public fun CompletableDeferred(value: T): CompletableDeferred = Completab
private class CompletableDeferredImpl(
parent: Job?
) : JobSupport(true), CompletableDeferred, SelectClause1 {
- init { initParentJobInternal(parent) }
+ init { initParentJob(parent) }
override val onCancelComplete get() = true
override fun getCompleted(): T = getCompletedInternal() as T
override suspend fun await(): T = awaitInternal() as T
diff --git a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt
index b2b887988d..10be4a3d9e 100644
--- a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt
@@ -101,7 +101,12 @@ public abstract class CoroutineDispatcher :
@InternalCoroutinesApi
public override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
- (continuation as DispatchedContinuation<*>).reusableCancellableContinuation?.detachChild()
+ /*
+ * Unconditional cast is safe here: we only return DispatchedContinuation from `interceptContinuation`,
+ * any ClassCastException can only indicate compiler bug
+ */
+ val dispatched = continuation as DispatchedContinuation<*>
+ dispatched.release()
}
/**
diff --git a/kotlinx-coroutines-core/common/src/CoroutineScope.kt b/kotlinx-coroutines-core/common/src/CoroutineScope.kt
index e7c243a42d..df2ee615dc 100644
--- a/kotlinx-coroutines-core/common/src/CoroutineScope.kt
+++ b/kotlinx-coroutines-core/common/src/CoroutineScope.kt
@@ -16,7 +16,10 @@ import kotlin.coroutines.intrinsics.*
* is an extension on [CoroutineScope] and inherits its [coroutineContext][CoroutineScope.coroutineContext]
* to automatically propagate all its elements and cancellation.
*
- * The best ways to obtain a standalone instance of the scope are [CoroutineScope()] and [MainScope()] factory functions.
+ * The best ways to obtain a standalone instance of the scope are [CoroutineScope()] and [MainScope()] factory functions,
+ * taking care to cancel these coroutine scopes when they are no longer needed (see section on custom usage below for
+ * explanation and example).
+ *
* Additional context elements can be appended to the scope using the [plus][CoroutineScope.plus] operator.
*
* ### Convention for structured concurrency
@@ -38,12 +41,23 @@ import kotlin.coroutines.intrinsics.*
*
* ### Custom usage
*
- * [CoroutineScope] should be implemented or declared as a property on entities with a well-defined lifecycle that are
- * responsible for launching children coroutines, for example:
+ * `CoroutineScope` should be declared as a property on entities with a well-defined lifecycle that are
+ * responsible for launching children coroutines. The corresponding instance of `CoroutineScope` shall be created
+ * with either `CoroutineScope()` or `MainScope()` functions. The difference between them is only in the
+ * [CoroutineDispatcher]:
+ *
+ * * `CoroutineScope()` uses [Dispatchers.Default] for its coroutines.
+ * * `MainScope()` uses [Dispatchers.Main] for its coroutines.
+ *
+ * **The key part of custom usage of `CustomScope` is cancelling it and the end of the lifecycle.**
+ * The [CoroutineScope.cancel] extension function shall be used when the entity that was launching coroutines
+ * is no longer needed. It cancels all the coroutines that might still be running on behalf of it.
+ *
+ * For example:
*
* ```
* class MyUIClass {
- * val scope = MainScope() // the scope of MyUIClass
+ * val scope = MainScope() // the scope of MyUIClass, uses Dispatchers.Main
*
* fun destroy() { // destroys an instance of MyUIClass
* scope.cancel() // cancels all coroutines launched in this scope
@@ -124,25 +138,81 @@ public val CoroutineScope.isActive: Boolean
/**
* A global [CoroutineScope] not bound to any job.
- *
* Global scope is used to launch top-level coroutines which are operating on the whole application lifetime
* and are not cancelled prematurely.
- * Another use of the global scope is operators running in [Dispatchers.Unconfined], which don't have any job associated with them.
*
- * Application code usually should use an application-defined [CoroutineScope]. Using
- * [async][CoroutineScope.async] or [launch][CoroutineScope.launch]
- * on the instance of [GlobalScope] is highly discouraged.
+ * Active coroutines launched in `GlobalScope` do not keep the process alive. They are like daemon threads.
+ *
+ * This is a **delicate** API. It is easy to accidentally create resource or memory leaks when
+ * `GlobalScope` is used. A coroutine launched in `GlobalScope` is not subject to the principle of structured
+ * concurrency, so if it hangs or gets delayed due to a problem (e.g. due to a slow network), it will stay working
+ * and consuming resources. For example, consider the following code:
+ *
+ * ```
+ * fun loadConfiguration() {
+ * GlobalScope.launch {
+ * val config = fetchConfigFromServer() // network request
+ * updateConfiguration(config)
+ * }
+ * }
+ * ```
+ *
+ * A call to `loadConfiguration` creates a coroutine in the `GlobalScope` that works in background without any
+ * provision to cancel it or to wait for its completion. If a network is slow, it keeps waiting in background,
+ * consuming resources. Repeated calls to `loadConfiguration` will consume more and more resources.
+ *
+ * ### Possible replacements
+ *
+ * In may cases uses of `GlobalScope` should be removed, marking the containing operation with `suspend`, for example:
+ *
+ * ```
+ * suspend fun loadConfiguration() {
+ * val config = fetchConfigFromServer() // network request
+ * updateConfiguration(config)
+ * }
+ * ```
+ *
+ * In cases when `GlobalScope.launch` was used to launch multiple concurrent operations, the corresponding
+ * operations shall be grouped with [coroutineScope] instead:
+ *
+ * ```
+ * // concurrently load configuration and data
+ * suspend fun loadConfigurationAndData() {
+ * coroutinesScope {
+ * launch { loadConfiguration() }
+ * launch { loadData() }
+ * }
+ * }
+ * ```
+ *
+ * In top-level code, when launching a concurrent operation operation from a non-suspending context, an appropriately
+ * confined instance of [CoroutineScope] shall be used instead of a `GlobalScope`. See docs on [CoroutineScope] for
+ * details.
+ *
+ * ### GlobalScope vs custom scope
+ *
+ * Do not replace `GlobalScope.launch { ... }` with `CoroutineScope().launch { ... }` constructor function call.
+ * The latter has the same pitfalls as `GlobalScope`. See [CoroutineScope] documentation on the intended usage of
+ * `CoroutineScope()` constructor function.
+ *
+ * ### Legitimate use-cases
*
- * Usage of this interface may look like this:
+ * There are limited circumstances under which `GlobalScope` can be legitimately and safely used, such as top-level background
+ * processes that must stay active for the whole duration of the application's lifetime. Because of that, any use
+ * of `GlobalScope` requires an explicit opt-in with `@OptIn(DelicateCoroutinesApi::class)`, like this:
*
* ```
- * fun ReceiveChannel.sqrt(): ReceiveChannel = GlobalScope.produce(Dispatchers.Unconfined) {
- * for (number in this) {
- * send(Math.sqrt(number))
+ * // A global coroutine to log statistics every second, must be always active
+ * @OptIn(DelicateCoroutinesApi::class)
+ * val globalScopeReporter = GlobalScope.launch {
+ * while (true) {
+ * delay(1000)
+ * logStatistics()
* }
* }
* ```
*/
+@DelicateCoroutinesApi
public object GlobalScope : CoroutineScope {
/**
* Returns [EmptyCoroutineContext].
diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt
index 31e2ef22b2..be58213288 100644
--- a/kotlinx-coroutines-core/common/src/Job.kt
+++ b/kotlinx-coroutines-core/common/src/Job.kt
@@ -113,16 +113,7 @@ public interface Job : CoroutineContext.Element {
/**
* Key for [Job] instance in the coroutine context.
*/
- public companion object Key : CoroutineContext.Key {
- init {
- /*
- * Here we make sure that CoroutineExceptionHandler is always initialized in advance, so
- * that if a coroutine fails due to StackOverflowError we don't fail to report this error
- * trying to initialize CoroutineExceptionHandler
- */
- CoroutineExceptionHandler
- }
- }
+ public companion object Key : CoroutineContext.Key
// ------------ state query ------------
@@ -217,11 +208,10 @@ public interface Job : CoroutineContext.Element {
* immediately cancels all its children.
* * Parent cannot complete until all its children are complete. Parent waits for all its children to
* complete in _completing_ or _cancelling_ state.
- * * Uncaught exception in a child, by default, cancels parent. In particular, this applies to
- * children created with [launch][CoroutineScope.launch] coroutine builder. Note that
- * [async][CoroutineScope.async] and other future-like
- * coroutine builders do not have uncaught exceptions by definition, since all their exceptions are
- * caught and are encapsulated in their result.
+ * * Uncaught exception in a child, by default, cancels parent. This applies even to
+ * children created with [async][CoroutineScope.async] and other future-like
+ * coroutine builders, even though their exceptions are caught and are encapsulated in their result.
+ * This default behavior can be overridden with [SupervisorJob].
*/
public val children: Sequence
@@ -262,9 +252,9 @@ public interface Job : CoroutineContext.Element {
* suspending function is invoked or while it is suspended, this function
* throws [CancellationException].
*
- * In particular, it means that a parent coroutine invoking `join` on a child coroutine that was started using
- * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child
- * had crashed, unless a non-standard [CoroutineExceptionHandler] is installed in the context.
+ * In particular, it means that a parent coroutine invoking `join` on a child coroutine throws
+ * [CancellationException] if the child had failed, since a failure of a child coroutine cancels parent by default,
+ * unless the child was launched from within [supervisorScope].
*
* This function can be used in [select] invocation with [onJoin] clause.
* Use [isCompleted] to check for a completion of this job without waiting.
@@ -467,6 +457,14 @@ public interface ParentJob : Job {
@InternalCoroutinesApi
@Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases")
public interface ChildHandle : DisposableHandle {
+
+ /**
+ * Returns the parent of the current parent-child relationship.
+ * @suppress **This is unstable API and it is subject to change.**
+ */
+ @InternalCoroutinesApi
+ public val parent: Job?
+
/**
* Child is cancelling its parent by invoking this method.
* This method is invoked by the child twice. The first time child report its root cause as soon as possible,
@@ -500,9 +498,9 @@ internal fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle
* suspending function is invoked or while it is suspended, this function
* throws [CancellationException].
*
- * In particular, it means that a parent coroutine invoking `cancelAndJoin` on a child coroutine that was started using
- * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child
- * had crashed, unless a non-standard [CoroutineExceptionHandler] is installed in the context.
+ * In particular, it means that a parent coroutine invoking `cancelAndJoin` on a child coroutine throws
+ * [CancellationException] if the child had failed, since a failure of a child coroutine cancels parent by default,
+ * unless the child was launched from within [supervisorScope].
*
* This is a shortcut for the invocation of [cancel][Job.cancel] followed by [join][Job.join].
*/
@@ -660,6 +658,9 @@ private fun Throwable?.orCancellation(job: Job): Throwable = this ?: JobCancella
*/
@InternalCoroutinesApi
public object NonDisposableHandle : DisposableHandle, ChildHandle {
+
+ override val parent: Job? get() = null
+
/**
* Does not do anything.
* @suppress
diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt
index 5b516ae27f..0a3dd23472 100644
--- a/kotlinx-coroutines-core/common/src/JobSupport.kt
+++ b/kotlinx-coroutines-core/common/src/JobSupport.kt
@@ -96,7 +96,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
~ waits for start
>> start / join / await invoked
## ACTIVE: state == EMPTY_ACTIVE | is JobNode | is NodeList
- + onStartInternal / onStart (lazy coroutine is started)
+ + onStart (lazy coroutine is started)
~ active coroutine is working (or scheduled to execution)
>> childCancelled / cancelImpl invoked
## CANCELLING: state is Finishing, state.rootCause != null
@@ -139,7 +139,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
* Initializes parent job.
* It shall be invoked at most once after construction after all other initialization.
*/
- internal fun initParentJobInternal(parent: Job?) {
+ protected fun initParentJob(parent: Job?) {
assert { parentHandle == null }
if (parent == null) {
parentHandle = NonDisposableHandle
@@ -393,12 +393,12 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
is Empty -> { // EMPTY_X state -- no completion handlers
if (state.isActive) return FALSE // already active
if (!_state.compareAndSet(state, EMPTY_ACTIVE)) return RETRY
- onStartInternal()
+ onStart()
return TRUE
}
is InactiveNodeList -> { // LIST state -- inactive with a list of completion handlers
if (!_state.compareAndSet(state, state.list)) return RETRY
- onStartInternal()
+ onStart()
return TRUE
}
else -> return FALSE // not a new state
@@ -409,7 +409,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
* Override to provide the actual [start] action.
* This function is invoked exactly once when non-active coroutine is [started][start].
*/
- internal open fun onStartInternal() {}
+ protected open fun onStart() {}
public final override fun getCancellationException(): CancellationException =
when (val state = this.state) {
@@ -541,7 +541,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
public final override suspend fun join() {
if (!joinInternal()) { // fast-path no wait
- coroutineContext.checkCompletion()
+ coroutineContext.ensureActive()
return // do not suspend
}
return joinSuspend() // slow-path wait
@@ -1228,6 +1228,8 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
* thrown and not a JobCancellationException.
*/
val cont = AwaitContinuation(uCont.intercepted(), this)
+ // we are mimicking suspendCancellableCoroutine here and call initCancellability, too.
+ cont.initCancellability()
cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion(cont).asHandler))
cont.getResult()
}
@@ -1311,7 +1313,7 @@ private class Empty(override val isActive: Boolean) : Incomplete {
}
internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob {
- init { initParentJobInternal(parent) }
+ init { initParentJob(parent) }
override val onCancelComplete get() = true
/*
* Check whether parent is able to handle exceptions as well.
@@ -1459,6 +1461,7 @@ private class InvokeOnCancelling(
internal class ChildHandleNode(
@JvmField val childJob: ChildJob
) : JobCancellingNode(), ChildHandle {
+ override val parent: Job get() = job
override fun invoke(cause: Throwable?) = childJob.parentCancelled(job)
override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
}
diff --git a/kotlinx-coroutines-core/common/src/NonCancellable.kt b/kotlinx-coroutines-core/common/src/NonCancellable.kt
index a9c68f090a..5d3644ba91 100644
--- a/kotlinx-coroutines-core/common/src/NonCancellable.kt
+++ b/kotlinx-coroutines-core/common/src/NonCancellable.kt
@@ -18,6 +18,11 @@ import kotlin.coroutines.*
* // this code will not be cancelled
* }
* ```
+ *
+ * **WARNING**: This object is not designed to be used with [launch], [async], and other coroutine builders.
+ * if you write `launch(NonCancellable) { ... }` then not only the newly launched job will not be cancelled
+ * when the parent is cancelled, the whole parent-child relation between parent and child is severed.
+ * The parent will not wait for the child's completion, nor will be cancelled when the child crashed.
*/
public object NonCancellable : AbstractCoroutineContextElement(Job), Job {
/**
diff --git a/kotlinx-coroutines-core/common/src/Yield.kt b/kotlinx-coroutines-core/common/src/Yield.kt
index 975a48935c..98e210412b 100644
--- a/kotlinx-coroutines-core/common/src/Yield.kt
+++ b/kotlinx-coroutines-core/common/src/Yield.kt
@@ -30,7 +30,7 @@ import kotlin.coroutines.intrinsics.*
*/
public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
val context = uCont.context
- context.checkCompletion()
+ context.ensureActive()
val cont = uCont.intercepted() as? DispatchedContinuation ?: return@sc Unit
if (cont.dispatcher.isDispatchNeeded(context)) {
// this is a regular dispatcher -- do simple dispatchYield
@@ -50,8 +50,3 @@ public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { u
}
COROUTINE_SUSPENDED
}
-
-internal fun CoroutineContext.checkCompletion() {
- val job = get(Job)
- if (job != null && !job.isActive) throw job.getCancellationException()
-}
diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
index 9721583e83..bcf1921594 100644
--- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
@@ -127,8 +127,7 @@ internal abstract class AbstractSendChannel(
// ------ SendChannel ------
public final override val isClosedForSend: Boolean get() = closedForSend != null
- public override val isFull: Boolean get() = isFullImpl
- protected val isFullImpl: Boolean get() = queue.nextNode !is ReceiveOrClosed<*> && isBufferFull
+ private val isFullImpl: Boolean get() = queue.nextNode !is ReceiveOrClosed<*> && isBufferFull
public final override suspend fun send(element: E) {
// fast path -- try offer non-blocking
@@ -137,23 +136,43 @@ internal abstract class AbstractSendChannel(
return sendSuspend(element)
}
- public final override fun offer(element: E): Boolean {
+ override fun offer(element: E): Boolean {
+ // Temporary migration for offer users who rely on onUndeliveredElement
+ try {
+ return super.offer(element)
+ } catch (e: Throwable) {
+ onUndeliveredElement?.callUndeliveredElementCatchingException(element)?.let {
+ // If it crashes, add send exception as suppressed for better diagnostics
+ it.addSuppressed(e)
+ throw it
+ }
+ throw e
+ }
+ }
+
+ public final override fun trySend(element: E): ChannelResult {
val result = offerInternal(element)
return when {
- result === OFFER_SUCCESS -> true
+ result === OFFER_SUCCESS -> ChannelResult.success(Unit)
result === OFFER_FAILED -> {
- // We should check for closed token on offer as well, otherwise offer won't be linearizable
+ // We should check for closed token on trySend as well, otherwise trySend won't be linearizable
// in the face of concurrent close()
// See https://github.com/Kotlin/kotlinx.coroutines/issues/359
- throw recoverStackTrace(helpCloseAndGetSendException(element, closedForSend ?: return false))
+ val closedForSend = closedForSend ?: return ChannelResult.failure()
+ ChannelResult.closed(helpCloseAndGetSendException(closedForSend))
}
result is Closed<*> -> {
- throw recoverStackTrace(helpCloseAndGetSendException(element, result))
+ ChannelResult.closed(helpCloseAndGetSendException(result))
}
- else -> error("offerInternal returned $result")
+ else -> error("trySend returned $result")
}
}
+ private fun helpCloseAndGetSendException(closed: Closed<*>): Throwable {
+ helpClose(closed)
+ return closed.sendException
+ }
+
private fun helpCloseAndGetSendException(element: E, closed: Closed<*>): Throwable {
// To ensure linearizablity we must ALWAYS help close the channel when we observe that it was closed
// See https://github.com/Kotlin/kotlinx.coroutines/issues/1419
@@ -604,26 +623,8 @@ internal abstract class AbstractChannel(
if (result) onReceiveEnqueued()
}
- public final override suspend fun receiveOrNull(): E? {
- // fast path -- try poll non-blocking
- val result = pollInternal()
- @Suppress("UNCHECKED_CAST")
- if (result !== POLL_FAILED && result !is Closed<*>) return result as E
- // slow-path does suspend
- return receiveSuspend(RECEIVE_NULL_ON_CLOSE)
- }
-
@Suppress("UNCHECKED_CAST")
- private fun receiveOrNullResult(result: Any?): E? {
- if (result is Closed<*>) {
- if (result.closeCause != null) throw recoverStackTrace(result.closeCause)
- return null
- }
- return result as E
- }
-
- @Suppress("UNCHECKED_CAST")
- public final override suspend fun receiveOrClosed(): ValueOrClosed {
+ public final override suspend fun receiveCatching(): ChannelResult {
// fast path -- try poll non-blocking
val result = pollInternal()
if (result !== POLL_FAILED) return result.toResult()
@@ -632,9 +633,11 @@ internal abstract class AbstractChannel(
}
@Suppress("UNCHECKED_CAST")
- public final override fun poll(): E? {
+ public final override fun tryReceive(): ChannelResult {
val result = pollInternal()
- return if (result === POLL_FAILED) null else receiveOrNullResult(result)
+ if (result === POLL_FAILED) return ChannelResult.failure()
+ if (result is Closed<*>) return ChannelResult.closed(result.closeCause)
+ return ChannelResult.success(result as E)
}
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
@@ -734,18 +737,10 @@ internal abstract class AbstractChannel(
}
}
- final override val onReceiveOrNull: SelectClause1
- get() = object : SelectClause1 {
+ final override val onReceiveCatching: SelectClause1>
+ get() = object : SelectClause1> {
@Suppress("UNCHECKED_CAST")
- override fun registerSelectClause1(select: SelectInstance, block: suspend (E?) -> R) {
- registerSelectReceiveMode(select, RECEIVE_NULL_ON_CLOSE, block as suspend (Any?) -> R)
- }
- }
-
- final override val onReceiveOrClosed: SelectClause1>
- get() = object : SelectClause1> {
- @Suppress("UNCHECKED_CAST")
- override fun registerSelectClause1(select: SelectInstance, block: suspend (ValueOrClosed) -> R) {
+ override fun registerSelectClause1(select: SelectInstance, block: suspend (ChannelResult) -> R) {
registerSelectReceiveMode(select, RECEIVE_RESULT, block as suspend (Any?) -> R)
}
}
@@ -776,15 +771,7 @@ internal abstract class AbstractChannel(
}
RECEIVE_RESULT -> {
if (!select.trySelect()) return
- startCoroutineUnintercepted(ValueOrClosed.closed(value.closeCause), select.completion)
- }
- RECEIVE_NULL_ON_CLOSE -> {
- if (value.closeCause == null) {
- if (!select.trySelect()) return
- startCoroutineUnintercepted(null, select.completion)
- } else {
- throw recoverStackTrace(value.receiveException)
- }
+ startCoroutineUnintercepted(ChannelResult.closed(value.closeCause), select.completion)
}
}
}
@@ -905,7 +892,7 @@ internal abstract class AbstractChannel(
@JvmField val receiveMode: Int
) : Receive() {
fun resumeValue(value: E): Any? = when (receiveMode) {
- RECEIVE_RESULT -> ValueOrClosed.value(value)
+ RECEIVE_RESULT -> ChannelResult.success(value)
else -> value
}
@@ -921,7 +908,6 @@ internal abstract class AbstractChannel(
override fun resumeReceiveClosed(closed: Closed<*>) {
when {
- receiveMode == RECEIVE_NULL_ON_CLOSE && closed.closeCause == null -> cont.resume(null)
receiveMode == RECEIVE_RESULT -> cont.resume(closed.toResult())
else -> cont.resumeWithException(closed.receiveException)
}
@@ -990,7 +976,7 @@ internal abstract class AbstractChannel(
@Suppress("UNCHECKED_CAST")
override fun completeResumeReceive(value: E) {
block.startCoroutineCancellable(
- if (receiveMode == RECEIVE_RESULT) ValueOrClosed.value(value) else value,
+ if (receiveMode == RECEIVE_RESULT) ChannelResult.success(value) else value,
select.completion,
resumeOnCancellationFun(value)
)
@@ -1000,12 +986,7 @@ internal abstract class AbstractChannel(
if (!select.trySelect()) return
when (receiveMode) {
RECEIVE_THROWS_ON_CLOSE -> select.resumeSelectWithException(closed.receiveException)
- RECEIVE_RESULT -> block.startCoroutineCancellable(ValueOrClosed.closed(closed.closeCause), select.completion)
- RECEIVE_NULL_ON_CLOSE -> if (closed.closeCause == null) {
- block.startCoroutineCancellable(null, select.completion)
- } else {
- select.resumeSelectWithException(closed.receiveException)
- }
+ RECEIVE_RESULT -> block.startCoroutineCancellable(ChannelResult.closed(closed.closeCause), select.completion)
}
}
@@ -1023,8 +1004,7 @@ internal abstract class AbstractChannel(
// receiveMode values
internal const val RECEIVE_THROWS_ON_CLOSE = 0
-internal const val RECEIVE_NULL_ON_CLOSE = 1
-internal const val RECEIVE_RESULT = 2
+internal const val RECEIVE_RESULT = 1
@JvmField
@SharedImmutable
@@ -1128,9 +1108,9 @@ internal class Closed(
override val offerResult get() = this
override val pollResult get() = this
- override fun tryResumeSend(otherOp: PrepareOp?): Symbol? = RESUME_TOKEN.also { otherOp?.finishPrepare() }
+ override fun tryResumeSend(otherOp: PrepareOp?): Symbol = RESUME_TOKEN.also { otherOp?.finishPrepare() }
override fun completeResumeSend() {}
- override fun tryResumeReceive(value: E, otherOp: PrepareOp?): Symbol? = RESUME_TOKEN.also { otherOp?.finishPrepare() }
+ override fun tryResumeReceive(value: E, otherOp: PrepareOp?): Symbol = RESUME_TOKEN.also { otherOp?.finishPrepare() }
override fun completeResumeReceive(value: E) {}
override fun resumeSendClosed(closed: Closed<*>) = assert { false } // "Should be never invoked"
override fun toString(): String = "Closed@$hexAddress[$closeCause]"
@@ -1143,8 +1123,8 @@ internal abstract class Receive : LockFreeLinkedListNode(), ReceiveOrClose
}
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
-private inline fun Any?.toResult(): ValueOrClosed =
- if (this is Closed<*>) ValueOrClosed.closed(closeCause) else ValueOrClosed.value(this as E)
+private inline fun Any?.toResult(): ChannelResult =
+ if (this is Closed<*>) ChannelResult.closed(closeCause) else ChannelResult.success(this as E)
@Suppress("NOTHING_TO_INLINE")
-private inline fun Closed<*>.toResult(): ValueOrClosed = ValueOrClosed.closed(closeCause)
+private inline fun Closed<*>.toResult(): ChannelResult = ChannelResult.closed(closeCause)
diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
index 4569ec72fa..7e6c0e68c5 100644
--- a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
@@ -49,7 +49,6 @@ internal open class ArrayChannel(
protected final override val isBufferAlwaysFull: Boolean get() = false
protected final override val isBufferFull: Boolean get() = size.value == capacity && onBufferOverflow == BufferOverflow.SUSPEND
- override val isFull: Boolean get() = lock.withLock { isFullImpl }
override val isEmpty: Boolean get() = lock.withLock { isEmptyImpl }
override val isClosedForReceive: Boolean get() = lock.withLock { super.isClosedForReceive }
diff --git a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt
index 07e7597627..b1c24b456d 100644
--- a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt
@@ -35,11 +35,13 @@ import kotlin.coroutines.intrinsics.*
* [send][BroadcastChannel.send] and [close][BroadcastChannel.close] operations that interfere with
* the broadcasting coroutine in hard-to-specify ways.
*
- * **Note: This API is obsolete.** It will be deprecated and replaced with the
- * [Flow.shareIn][kotlinx.coroutines.flow.shareIn] operator when it becomes stable.
+ * **Note: This API is obsolete since 1.5.0.** It will be deprecated with warning in 1.6.0
+ * and with error in 1.7.0. It is replaced with [Flow.shareIn][kotlinx.coroutines.flow.shareIn]
+ * operator.
*
* @param start coroutine start option. The default value is [CoroutineStart.LAZY].
*/
+@ObsoleteCoroutinesApi
public fun ReceiveChannel.broadcast(
capacity: Int = 1,
start: CoroutineStart = CoroutineStart.LAZY
@@ -95,10 +97,12 @@ public fun ReceiveChannel.broadcast(
*
* ### Future replacement
*
+ * This API is obsolete since 1.5.0.
* This function has an inappropriate result type of [BroadcastChannel] which provides
* [send][BroadcastChannel.send] and [close][BroadcastChannel.close] operations that interfere with
- * the broadcasting coroutine in hard-to-specify ways. It will be replaced with
- * sharing operators on [Flow][kotlinx.coroutines.flow.Flow] in the future.
+ * the broadcasting coroutine in hard-to-specify ways. It will be deprecated with warning in 1.6.0
+ * and with error in 1.7.0. It is replaced with [Flow.shareIn][kotlinx.coroutines.flow.shareIn]
+ * operator.
*
* @param context additional to [CoroutineScope.coroutineContext] context of the coroutine.
* @param capacity capacity of the channel's buffer (1 by default).
@@ -106,6 +110,7 @@ public fun ReceiveChannel.broadcast(
* @param onCompletion optional completion handler for the producer coroutine (see [Job.invokeOnCompletion]).
* @param block the coroutine code.
*/
+@ObsoleteCoroutinesApi
public fun CoroutineScope.broadcast(
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = 1,
@@ -127,7 +132,13 @@ private open class BroadcastCoroutine(
parentContext: CoroutineContext,
protected val _channel: BroadcastChannel,
active: Boolean
-) : AbstractCoroutine(parentContext, active), ProducerScope, BroadcastChannel by _channel {
+) : AbstractCoroutine(parentContext, initParentJob = false, active = active),
+ ProducerScope, BroadcastChannel by _channel {
+
+ init {
+ initParentJob(parentContext[Job])
+ }
+
override val isActive: Boolean get() = super.isActive
override val channel: SendChannel
diff --git a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt
index 6cd79373b2..c82b8dbd63 100644
--- a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt
@@ -20,10 +20,10 @@ import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
* See `BroadcastChannel()` factory function for the description of available
* broadcast channel implementations.
*
- * **Note: This API is obsolete.** It will be deprecated and replaced by [SharedFlow][kotlinx.coroutines.flow.SharedFlow]
- * when it becomes stable.
+ * **Note: This API is obsolete since 1.5.0.** It will be deprecated with warning in 1.6.0
+ * and with error in 1.7.0. It is replaced with [SharedFlow][kotlinx.coroutines.flow.SharedFlow].
*/
-@ExperimentalCoroutinesApi // not @ObsoleteCoroutinesApi to reduce burden for people who are still using it
+@ObsoleteCoroutinesApi
public interface BroadcastChannel : SendChannel {
/**
* Subscribes to this [BroadcastChannel] and returns a channel to receive elements from it.
@@ -60,9 +60,11 @@ public interface BroadcastChannel : SendChannel {
* * when `capacity` is [BUFFERED] -- creates `ArrayBroadcastChannel` with a default capacity.
* * otherwise -- throws [IllegalArgumentException].
*
- * **Note: This is an experimental api.** It may be changed in the future updates.
+ * **Note: This API is obsolete since 1.5.0.** It will be deprecated with warning in 1.6.0
+ * and with error in 1.7.0. It is replaced with [StateFlow][kotlinx.coroutines.flow.StateFlow]
+ * and [SharedFlow][kotlinx.coroutines.flow.SharedFlow].
*/
-@ExperimentalCoroutinesApi
+@ObsoleteCoroutinesApi
public fun BroadcastChannel(capacity: Int): BroadcastChannel =
when (capacity) {
0 -> throw IllegalArgumentException("Unsupported 0 capacity for BroadcastChannel")
diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt
index b8b81aac53..a0dbc6b9b4 100644
--- a/kotlinx-coroutines-core/common/src/channels/Channel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt
@@ -14,6 +14,7 @@ import kotlinx.coroutines.channels.Channel.Factory.RENDEZVOUS
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.selects.*
+import kotlin.contracts.*
import kotlin.internal.*
import kotlin.jvm.*
@@ -23,23 +24,13 @@ import kotlin.jvm.*
public interface SendChannel {
/**
* Returns `true` if this channel was closed by an invocation of [close]. This means that
- * calling [send] or [offer] will result in an exception.
+ * calling [send] will result in an exception.
*
* **Note: This is an experimental api.** This property may change its semantics and/or name in the future.
*/
@ExperimentalCoroutinesApi
public val isClosedForSend: Boolean
- /**
- * Returns `true` if the channel is full (out of capacity), which means that an attempt to [send] will suspend.
- * This function returns `false` if the channel [is closed for `send`][isClosedForSend].
- *
- * @suppress **Will be removed in next releases, no replacement.**
- */
- @ExperimentalCoroutinesApi
- @Deprecated(level = DeprecationLevel.ERROR, message = "Will be removed in next releases without replacement")
- public val isFull: Boolean
-
/**
* Sends the specified [element] to this channel, suspending the caller while the buffer of this channel is full
* or if it does not exist, or throws an exception if the channel [is closed for `send`][isClosedForSend] (see [close] for details).
@@ -60,7 +51,7 @@ public interface SendChannel {
* Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
*
* This function can be used in [select] invocations with the [onSend] clause.
- * Use [offer] to try sending to this channel without waiting.
+ * Use [trySend] to try sending to this channel without waiting.
*/
public suspend fun send(element: E)
@@ -73,19 +64,17 @@ public interface SendChannel {
*/
public val onSend: SelectClause2>
+
/**
* Immediately adds the specified [element] to this channel, if this doesn't violate its capacity restrictions,
- * and returns `true`. Otherwise, just returns `false`. This is a synchronous variant of [send] which backs off
- * in situations when `send` suspends.
+ * and returns the successful result. Otherwise, returns failed or closed result.
+ * This is synchronous variant of [send], which backs off in situations when `send` suspends or throws.
*
- * Throws an exception if the channel [is closed for `send`][isClosedForSend] (see [close] for details).
- *
- * When `offer` call returns `false` it guarantees that the element was not delivered to the consumer and it
- * it does not call `onUndeliveredElement` that was installed for this channel. If the channel was closed,
- * then it calls `onUndeliveredElement` before throwing an exception.
+ * When `trySend` call returns a non-successful result, it guarantees that the element was not delivered to the consumer, and
+ * it does not call `onUndeliveredElement` that was installed for this channel.
* See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements.
*/
- public fun offer(element: E): Boolean
+ public fun trySend(element: E): ChannelResult
/**
* Closes this channel.
@@ -97,7 +86,7 @@ public interface SendChannel {
* on the side of [ReceiveChannel] starts returning `true` only after all previously sent elements
* are received.
*
- * A channel that was closed without a [cause] throws a [ClosedSendChannelException] on attempts to [send] or [offer]
+ * A channel that was closed without a [cause] throws a [ClosedSendChannelException] on attempts to [send]
* and [ClosedReceiveChannelException] on attempts to [receive][ReceiveChannel.receive].
* A channel that was closed with non-null [cause] is called a _failed_ channel. Attempts to send or
* receive on a failed channel throw the specified [cause] exception.
@@ -116,10 +105,11 @@ public interface SendChannel {
* * the cause of `close` or `cancel` otherwise.
*
* Example of usage (exception handling is omitted):
+ *
* ```
* val events = Channel(UNLIMITED)
* callbackBasedApi.registerCallback { event ->
- * events.offer(event)
+ * events.trySend(event)
* }
*
* val uiUpdater = launch(Dispatchers.Main, parent = UILifecycle) {
@@ -128,7 +118,6 @@ public interface SendChannel {
* }
*
* events.invokeOnClose { callbackBasedApi.stop() }
- *
* ```
*
* **Note: This is an experimental api.** This function may change its semantics, parameters or return type in the future.
@@ -140,6 +129,42 @@ public interface SendChannel {
*/
@ExperimentalCoroutinesApi
public fun invokeOnClose(handler: (cause: Throwable?) -> Unit)
+
+ /**
+ * **Deprecated** offer method.
+ *
+ * This method was deprecated in the favour of [trySend].
+ * It has proven itself as the most error-prone method in Channel API:
+ *
+ * * `Boolean` return type creates the false sense of security, implying that `false`
+ * is returned instead of throwing an exception.
+ * * It was used mostly from non-suspending APIs where CancellationException triggered
+ * internal failures in the application (the most common source of bugs).
+ * * Due to signature and explicit `if (ch.offer(...))` checks it was easy to
+ * oversee such error during code review.
+ * * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead.
+ *
+ * **NB** Automatic migration provides best-effort for the user experience, but requires removal
+ * or adjusting of the code that relied on the exception handling.
+ * The complete replacement has a more verbose form:
+ * ```
+ * channel.trySend(element)
+ * .onClosed { throw it ?: ClosedSendChannelException("Channel was closed normally") }
+ * .isSuccess
+ * ```
+ *
+ * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context.
+ */
+ @Deprecated(
+ level = DeprecationLevel.WARNING,
+ message = "Deprecated in the favour of 'trySend' method",
+ replaceWith = ReplaceWith("trySend(element).isSuccess")
+ ) // Warning since 1.5.0
+ public fun offer(element: E): Boolean {
+ val result = trySend(element)
+ if (result.isSuccess) return true
+ throw recoverStackTrace(result.exceptionOrNull() ?: return false)
+ }
}
/**
@@ -182,7 +207,7 @@ public interface ReceiveChannel {
* Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
*
* This function can be used in [select] invocations with the [onReceive] clause.
- * Use [poll] to try receiving from this channel without waiting.
+ * Use [tryReceive] to try receiving from this channel without waiting.
*/
public suspend fun receive(): E
@@ -194,95 +219,40 @@ public interface ReceiveChannel {
*/
public val onReceive: SelectClause1
- /**
- * Retrieves and removes an element from this channel if it's not empty, or suspends the caller while the channel is empty,
- * or returns `null` if the channel is [closed for `receive`][isClosedForReceive] without cause,
- * or throws the original [close][SendChannel.close] cause exception if the channel has _failed_.
- *
- * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this
- * function is suspended, this function immediately resumes with a [CancellationException].
- * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
- * suspended, it will not resume successfully. The `receiveOrNull` call can retrieve the element from the channel,
- * but then throw [CancellationException], thus failing to deliver the element.
- * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements.
- *
- * Note that this function does not check for cancellation when it is not suspended.
- * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
- *
- * This function can be used in [select] invocations with the [onReceiveOrNull] clause.
- * Use [poll] to try receiving from this channel without waiting.
- *
- * @suppress **Deprecated**: in favor of receiveOrClosed and receiveOrNull extension.
- */
- @ObsoleteCoroutinesApi
- @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
- @LowPriorityInOverloadResolution
- @Deprecated(
- message = "Deprecated in favor of receiveOrClosed and receiveOrNull extension",
- level = DeprecationLevel.WARNING,
- replaceWith = ReplaceWith("receiveOrNull", "kotlinx.coroutines.channels.receiveOrNull")
- )
- public suspend fun receiveOrNull(): E?
-
- /**
- * Clause for the [select] expression of the [receiveOrNull] suspending function that selects with the element
- * received from the channel or `null` if the channel is
- * [closed for `receive`][isClosedForReceive] without a cause. The [select] invocation fails with
- * the original [close][SendChannel.close] cause exception if the channel has _failed_.
- *
- * @suppress **Deprecated**: in favor of onReceiveOrClosed and onReceiveOrNull extension.
- */
- @ObsoleteCoroutinesApi
- @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
- @LowPriorityInOverloadResolution
- @Deprecated(
- message = "Deprecated in favor of onReceiveOrClosed and onReceiveOrNull extension",
- level = DeprecationLevel.WARNING,
- replaceWith = ReplaceWith("onReceiveOrNull", "kotlinx.coroutines.channels.onReceiveOrNull")
- )
- public val onReceiveOrNull: SelectClause1
-
/**
* Retrieves and removes an element from this channel if it's not empty, or suspends the caller while this channel is empty.
- * This method returns [ValueOrClosed] with the value of an element successfully retrieved from the channel
- * or the close cause if the channel was closed.
+ * This method returns [ChannelResult] with the value of an element successfully retrieved from the channel
+ * or the close cause if the channel was closed. Closed cause may be `null` if the channel was closed normally.
+ * The result cannot be [failed][ChannelResult.isFailure] without being [closed][ChannelResult.isClosed].
*
* This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this
* function is suspended, this function immediately resumes with a [CancellationException].
* There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
- * suspended, it will not resume successfully. The `receiveOrClosed` call can retrieve the element from the channel,
+ * suspended, it will not resume successfully. The `receiveCatching` call can retrieve the element from the channel,
* but then throw [CancellationException], thus failing to deliver the element.
* See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements.
*
* Note that this function does not check for cancellation when it is not suspended.
* Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
*
- * This function can be used in [select] invocations with the [onReceiveOrClosed] clause.
- * Use [poll] to try receiving from this channel without waiting.
- *
- * @suppress *This is an internal API, do not use*: Inline classes ABI is not stable yet and
- * [KT-27524](https://youtrack.jetbrains.com/issue/KT-27524) needs to be fixed.
+ * This function can be used in [select] invocations with the [onReceiveCatching] clause.
+ * Use [tryReceive] to try receiving from this channel without waiting.
*/
- @InternalCoroutinesApi // until https://youtrack.jetbrains.com/issue/KT-27524 is fixed
- public suspend fun receiveOrClosed(): ValueOrClosed
+ public suspend fun receiveCatching(): ChannelResult
/**
- * Clause for the [select] expression of the [receiveOrClosed] suspending function that selects with the [ValueOrClosed] with a value
+ * Clause for the [select] expression of the [onReceiveCatching] suspending function that selects with the [ChannelResult] with a value
* that is received from the channel or with a close cause if the channel
* [is closed for `receive`][isClosedForReceive].
- *
- * @suppress *This is an internal API, do not use*: Inline classes ABI is not stable yet and
- * [KT-27524](https://youtrack.jetbrains.com/issue/KT-27524) needs to be fixed.
*/
- @InternalCoroutinesApi // until https://youtrack.jetbrains.com/issue/KT-27524 is fixed
- public val onReceiveOrClosed: SelectClause1>
+ public val onReceiveCatching: SelectClause1>
/**
- * Retrieves and removes an element from this channel if its not empty, or returns `null` if the channel is empty
- * or is [is closed for `receive`][isClosedForReceive] without a cause.
- * It throws the original [close][SendChannel.close] cause exception if the channel has _failed_.
+ * Retrieves and removes an element from this channel if it's not empty, returning a [successful][ChannelResult.success]
+ * result, returns [failed][ChannelResult.failed] result if the channel is empty, and [closed][ChannelResult.closed]
+ * result if the channel is closed.
*/
- public fun poll(): E?
+ public fun tryReceive(): ChannelResult
/**
* Returns a new iterator to receive elements from this channel using a `for` loop.
@@ -318,107 +288,243 @@ public interface ReceiveChannel {
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x")
public fun cancel(cause: Throwable? = null): Boolean
+
+ /**
+ * **Deprecated** poll method.
+ *
+ * This method was deprecated in the favour of [tryReceive].
+ * It has proven itself as error-prone method in Channel API:
+ *
+ * * Nullable return type creates the false sense of security, implying that `null`
+ * is returned instead of throwing an exception.
+ * * It was used mostly from non-suspending APIs where CancellationException triggered
+ * internal failures in the application (the most common source of bugs).
+ * * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead.
+ *
+ * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context.
+ */
+ @Deprecated(level = DeprecationLevel.WARNING,
+ message = "Deprecated in the favour of 'tryReceive'",
+ replaceWith = ReplaceWith("tryReceive().getOrNull()")
+ ) // Warning since 1.5.0
+ public fun poll(): E? {
+ val result = tryReceive()
+ if (result.isSuccess) return result.getOrThrow()
+ throw recoverStackTrace(result.exceptionOrNull() ?: return null)
+ }
+
+ /**
+ * This function was deprecated since 1.3.0 and is no longer recommended to use
+ * or to implement in subclasses.
+ *
+ * It had the following pitfalls:
+ * - Didn't allow to distinguish 'null' as "closed channel" from "null as a value"
+ * - Was throwing if the channel has failed even though its signature may suggest it returns 'null'
+ * - It didn't really belong to core channel API and can be exposed as an extension instead.
+ *
+ * @suppress doc
+ */
+ @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+ @LowPriorityInOverloadResolution
+ @Deprecated(
+ message = "Deprecated in favor of receiveCatching",
+ level = DeprecationLevel.ERROR,
+ replaceWith = ReplaceWith("receiveCatching().getOrNull()")
+ ) // Warning since 1.3.0, error in 1.5.0, will be hidden in 1.6.0
+ public suspend fun receiveOrNull(): E? = receiveCatching().getOrNull()
+
+ /**
+ * This function was deprecated since 1.3.0 and is no longer recommended to use
+ * or to implement in subclasses.
+ * See [receiveOrNull] documentation.
+ *
+ * @suppress **Deprecated**: in favor of onReceiveCatching extension.
+ */
+ @Deprecated(
+ message = "Deprecated in favor of onReceiveCatching extension",
+ level = DeprecationLevel.ERROR,
+ replaceWith = ReplaceWith("onReceiveCatching")
+ ) // Warning since 1.3.0, error in 1.5.0, will be hidden or removed in 1.6.0
+ public val onReceiveOrNull: SelectClause1
+ get() {
+ return object : SelectClause1 {
+ @InternalCoroutinesApi
+ override fun registerSelectClause1(select: SelectInstance, block: suspend (E?) -> R) {
+ onReceiveCatching.registerSelectClause1(select) {
+ it.exceptionOrNull()?.let { throw it }
+ block(it.getOrNull())
+ }
+ }
+ }
+ }
}
/**
- * A discriminated union of [ReceiveChannel.receiveOrClosed] result
- * that encapsulates either an element of type [T] successfully received from the channel or a close cause.
+ * A discriminated union of channel operation result.
+ * It encapsulates the successful or failed result of a channel operation or a failed operation to a closed channel with
+ * an optional cause.
+ *
+ * The successful result represents a successful operation with a value of type [T], for example,
+ * the result of [Channel.receiveCatching] operation or a successfully sent element as a result of [Channel.trySend].
*
- * :todo: Do not make it public before resolving todos in the code of this class.
+ * The failed result represents a failed operation attempt to a channel, but it doesn't necessary indicate that the channel is failed.
+ * E.g. when the channel is full, [Channel.trySend] returns failed result, but the channel itself is not in the failed state.
*
- * @suppress *This is an internal API, do not use*: Inline classes ABI is not stable yet and
- * [KT-27524](https://youtrack.jetbrains.com/issue/KT-27524) needs to be fixed.
+ * The closed result represents an operation attempt to a closed channel and also implies that the operation has failed.
+ * It is guaranteed that if the result is _closed_, then the target channel is either [closed for send][Channel.isClosedForSend]
+ * or is [closed for receive][Channel.isClosedForReceive] depending on whether the failed operation was sending or receiving.
*/
-@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS", "EXPERIMENTAL_FEATURE_WARNING")
-@InternalCoroutinesApi // until https://youtrack.jetbrains.com/issue/KT-27524 is fixed
-public inline class ValueOrClosed
-internal constructor(private val holder: Any?) {
+@JvmInline
+public value class ChannelResult
+@PublishedApi internal constructor(@PublishedApi internal val holder: Any?) {
/**
- * Returns `true` if this instance represents a received element.
- * In this case [isClosed] returns `false`.
- * todo: it is commented for now, because it is not used
+ * Returns `true` if this instance represents a successful
+ * operation outcome.
+ *
+ * In this case [isFailure] and [isClosed] return `false`.
*/
- //public val isValue: Boolean get() = holder !is Closed
+ public val isSuccess: Boolean get() = holder !is Failed
/**
- * Returns `true` if this instance represents a close cause.
- * In this case [isValue] returns `false`.
+ * Returns `true` if this instance represents unsuccessful operation.
+ *
+ * In this case [isSuccess] returns false, but it does not imply
+ * that the channel is failed or closed.
+ *
+ * Example of a failed operation without an exception and channel being closed
+ * is [Channel.trySend] attempt to a channel that is full.
*/
- public val isClosed: Boolean get() = holder is Closed
+ public val isFailure: Boolean get() = holder is Failed
/**
- * Returns the received value if this instance represents a received value, or throws an [IllegalStateException] otherwise.
+ * Returns `true` if this instance represents unsuccessful operation
+ * to a closed or cancelled channel.
*
- * :todo: Decide, if it is needed, how it shall be named with relation to [valueOrThrow]:
+ * In this case [isSuccess] returns `false`, [isFailure] returns `true`, but it does not imply
+ * that [exceptionOrNull] returns non-null value.
*
- * So we have the following methods on `ValueOrClosed`: `value`, `valueOrNull`, `valueOrThrow`.
- * On the other hand, the channel has the following `receive` variants:
- * * `receive` which corresponds to `receiveOrClosed().valueOrThrow`... huh?
- * * `receiveOrNull` which corresponds to `receiveOrClosed().valueOrNull`
- * * `receiveOrClosed`
- * For the sake of simplicity consider dropping this version of `value` and rename [valueOrThrow] to simply `value`.
+ * It can happen if the channel was [closed][Channel.close] normally without an exception.
*/
- @Suppress("UNCHECKED_CAST")
- public val value: T
- get() = if (holder is Closed) error(DEFAULT_CLOSE_MESSAGE) else holder as T
+ public val isClosed: Boolean get() = holder is Closed
/**
- * Returns the received value if this element represents a received value, or `null` otherwise.
- * :todo: Decide if it shall be made into extension that is available only for non-null T.
- * Note: it might become inconsistent with kotlin.Result
+ * Returns the encapsulated value if this instance represents success or `null` if it represents failed result.
*/
@Suppress("UNCHECKED_CAST")
- public val valueOrNull: T?
- get() = if (holder is Closed) null else holder as T
+ public fun getOrNull(): T? = if (holder !is Failed) holder as T else null
/**
- * :todo: Decide, if it is needed, how it shall be named with relation to [value].
- * Note that `valueOrThrow` rethrows the cause adding no meaningful information about the call site,
- * so if one is sure that `ValueOrClosed` always holds a value, this very property should be used.
- * Otherwise, it could be very hard to locate the source of the exception.
- * todo: it is commented for now, because it is not used
+ * Returns the encapsulated value if this instance represents success or throws an exception if it is closed or failed.
*/
- //@Suppress("UNCHECKED_CAST")
- //public val valueOrThrow: T
- // get() = if (holder is Closed) throw holder.exception else holder as T
+ public fun getOrThrow(): T {
+ @Suppress("UNCHECKED_CAST")
+ if (holder !is Failed) return holder as T
+ if (holder is Closed && holder.cause != null) throw holder.cause
+ error("Trying to call 'getOrThrow' on a failed channel result: $holder")
+ }
/**
- * Returns the close cause of the channel if this instance represents a close cause, or throws
- * an [IllegalStateException] otherwise.
+ * Returns the encapsulated exception if this instance represents failure or `null` if it is success
+ * or unsuccessful operation to closed channel.
*/
- @Suppress("UNCHECKED_CAST")
- public val closeCause: Throwable? get() =
- if (holder is Closed) holder.cause else error("Channel was not closed")
+ public fun exceptionOrNull(): Throwable? = (holder as? Closed)?.cause
+
+ internal open class Failed {
+ override fun toString(): String = "Failed"
+ }
+
+ internal class Closed(@JvmField val cause: Throwable?): Failed() {
+ override fun equals(other: Any?): Boolean = other is Closed && cause == other.cause
+ override fun hashCode(): Int = cause.hashCode()
+ override fun toString(): String = "Closed($cause)"
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ @InternalCoroutinesApi
+ public companion object {
+ private val failed = Failed()
+
+ @InternalCoroutinesApi
+ public fun success(value: E): ChannelResult =
+ ChannelResult(value)
+
+ @InternalCoroutinesApi
+ public fun failure(): ChannelResult =
+ ChannelResult(failed)
+
+ @InternalCoroutinesApi
+ public fun closed(cause: Throwable?): ChannelResult =
+ ChannelResult(Closed(cause))
+ }
- /**
- * @suppress
- */
public override fun toString(): String =
when (holder) {
is Closed -> holder.toString()
else -> "Value($holder)"
+ }
+}
+
+/**
+ * Returns the encapsulated value if this instance represents [success][ChannelResult.isSuccess] or the
+ * result of [onFailure] function for the encapsulated [Throwable] exception if it is failed or closed
+ * result.
+ */
+@OptIn(ExperimentalContracts::class)
+public inline fun ChannelResult.getOrElse(onFailure: (exception: Throwable?) -> T): T {
+ contract {
+ callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
}
+ @Suppress("UNCHECKED_CAST")
+ return if (holder is ChannelResult.Failed) onFailure(exceptionOrNull()) else holder as T
+}
- internal class Closed(@JvmField val cause: Throwable?) {
- // todo: it is commented for now, because it is not used
- //val exception: Throwable get() = cause ?: ClosedReceiveChannelException(DEFAULT_CLOSE_MESSAGE)
- override fun equals(other: Any?): Boolean = other is Closed && cause == other.cause
- override fun hashCode(): Int = cause.hashCode()
- override fun toString(): String = "Closed($cause)"
+/**
+ * Performs the given [action] on the encapsulated value if this instance represents [success][ChannelResult.isSuccess].
+ * Returns the original `ChannelResult` unchanged.
+ */
+@OptIn(ExperimentalContracts::class)
+public inline fun ChannelResult.onSuccess(action: (value: T) -> Unit): ChannelResult {
+ contract {
+ callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
+ @Suppress("UNCHECKED_CAST")
+ if (holder !is ChannelResult.Failed) action(holder as T)
+ return this
+}
- /**
- * todo: consider making value/closed constructors public in the future.
- */
- internal companion object {
- @Suppress("NOTHING_TO_INLINE")
- internal inline fun value(value: E): ValueOrClosed =
- ValueOrClosed(value)
-
- @Suppress("NOTHING_TO_INLINE")
- internal inline fun closed(cause: Throwable?): ValueOrClosed =
- ValueOrClosed(Closed(cause))
+/**
+ * Performs the given [action] on the encapsulated [Throwable] exception if this instance represents [failure][ChannelResult.isFailure].
+ * The result of [ChannelResult.exceptionOrNull] is passed to the [action] parameter.
+ *
+ * Returns the original `ChannelResult` unchanged.
+ */
+@OptIn(ExperimentalContracts::class)
+public inline fun ChannelResult.onFailure(action: (exception: Throwable?) -> Unit): ChannelResult {
+ contract {
+ callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
+ @Suppress("UNCHECKED_CAST")
+ if (holder is ChannelResult.Failed) action(exceptionOrNull())
+ return this
+}
+
+/**
+ * Performs the given [action] on the encapsulated [Throwable] exception if this instance represents [failure][ChannelResult.isFailure]
+ * due to channel being [closed][Channel.close].
+ * The result of [ChannelResult.exceptionOrNull] is passed to the [action] parameter.
+ * It is guaranteed that if action is invoked, then the channel is either [closed for send][Channel.isClosedForSend]
+ * or is [closed for receive][Channel.isClosedForReceive] depending on the failed operation.
+ *
+ * Returns the original `ChannelResult` unchanged.
+ */
+@OptIn(ExperimentalContracts::class)
+public inline fun ChannelResult.onClosed(action: (exception: Throwable?) -> Unit): ChannelResult {
+ contract {
+ callsInPlace(action, InvocationKind.AT_MOST_ONCE)
+ }
+ @Suppress("UNCHECKED_CAST")
+ if (holder is ChannelResult.Closed) action(exceptionOrNull())
+ return this
}
/**
@@ -493,14 +599,14 @@ public interface ChannelIterator {
*
* * When `capacity` is [Channel.UNLIMITED] — it creates a channel with effectively unlimited buffer.
* This channel has a linked-list buffer of unlimited capacity (limited only by available memory).
- * [Sending][send] to this channel never suspends, and [offer] always returns `true`.
+ * [Sending][send] to this channel never suspends, and [trySend] always succeeds.
*
* * When `capacity` is [Channel.CONFLATED] — it creates a _conflated_ channel
- * This channel buffers at most one element and conflates all subsequent `send` and `offer` invocations,
+ * This channel buffers at most one element and conflates all subsequent `send` and `trySend` invocations,
* so that the receiver always gets the last element sent.
* Back-to-back sent elements are conflated — only the last sent element is received,
* while previously sent elements **are lost**.
- * [Sending][send] to this channel never suspends, and [offer] always returns `true`.
+ * [Sending][send] to this channel never suspends, and [trySend] always succeeds.
*
* * When `capacity` is positive but less than [UNLIMITED] — it creates an array-based channel with the specified capacity.
* This channel has an array buffer of a fixed `capacity`.
@@ -547,8 +653,6 @@ public interface ChannelIterator {
*
* * When [send][SendChannel.send] operation throws an exception because it was cancelled before it had a chance to actually
* send the element or because the channel was [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel].
- * * When [offer][SendChannel.offer] operation throws an exception when
- * the channel was [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel].
* * When [receive][ReceiveChannel.receive], [receiveOrNull][ReceiveChannel.receiveOrNull], or [hasNext][ChannelIterator.hasNext]
* operation throws an exception when it had retrieved the element from the
* channel but was cancelled before the code following the receive call resumed.
diff --git a/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt b/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt
index b2b257def2..57b2797de6 100644
--- a/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt
+++ b/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt
@@ -11,8 +11,10 @@ import kotlin.coroutines.*
internal open class ChannelCoroutine(
parentContext: CoroutineContext,
protected val _channel: Channel,
+ initParentJob: Boolean,
active: Boolean
-) : AbstractCoroutine(parentContext, active), Channel by _channel {
+) : AbstractCoroutine(parentContext, initParentJob, active), Channel by _channel {
+
val channel: Channel get() = this
override fun cancel() {
diff --git a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt
index e3567e3107..e0b4f9d2a5 100644
--- a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt
@@ -37,128 +37,39 @@ public inline fun BroadcastChannel.consume(block: ReceiveChannel.()
}
/**
- * Retrieves and removes the element from this channel suspending the caller while this channel [isEmpty]
- * or returns `null` if the channel is [closed][Channel.isClosedForReceive].
+ * This function is deprecated in the favour of [ReceiveChannel.receiveCatching].
*
- * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this
- * function is suspended, this function immediately resumes with [CancellationException].
- * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
- * suspended, it will not resume successfully. If the `receiveOrNull` call threw [CancellationException] there is no way
- * to tell if some element was already received from the channel or not. See [Channel] documentation for details.
+ * This function is considered error-prone for the following reasons;
+ * * Is throwing if the channel has failed even though its signature may suggest it returns 'null'
+ * * It is easy to forget that exception handling still have to be explicit
+ * * During code reviews and code reading, intentions of the code are frequently unclear:
+ * are potential exceptions ignored deliberately or not?
*
- * Note, that this function does not check for cancellation when it is not suspended.
- * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
- *
- * This extension is defined only for channels on non-null types, so that generic functions defined using
- * these extensions do not accidentally confuse `null` value and a normally closed channel, leading to hard
- * to find bugs.
+ * @suppress doc
*/
+@Deprecated(
+ "Deprecated in the favour of 'receiveCatching'",
+ ReplaceWith("receiveCatching().getOrNull()"),
+ DeprecationLevel.WARNING
+) // Warning since 1.5.0, ERROR in 1.6.0, HIDDEN in 1.7.0
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
-@ExperimentalCoroutinesApi // since 1.3.0, tentatively stable in 1.4.x
public suspend fun ReceiveChannel.receiveOrNull(): E? {
@Suppress("DEPRECATION", "UNCHECKED_CAST")
return (this as ReceiveChannel).receiveOrNull()
}
/**
- * Clause for [select] expression of [receiveOrNull] suspending function that selects with the element that
- * is received from the channel or selects with `null` if the channel
- * [isClosedForReceive][ReceiveChannel.isClosedForReceive] without cause. The [select] invocation fails with
- * the original [close][SendChannel.close] cause exception if the channel has _failed_.
- *
- * This extension is defined only for channels on non-null types, so that generic functions defined using
- * these extensions do not accidentally confuse `null` value and a normally closed channel, leading to hard
- * to find bugs.
- **/
-@ExperimentalCoroutinesApi // since 1.3.0, tentatively stable in 1.4.x
+ * This function is deprecated in the favour of [ReceiveChannel.onReceiveCatching]
+ */
+@Deprecated(
+ "Deprecated in the favour of 'onReceiveCatching'",
+ level = DeprecationLevel.WARNING
+) // Warning since 1.5.0, ERROR in 1.6.0, HIDDEN in 1.7.0
public fun ReceiveChannel.onReceiveOrNull(): SelectClause1 {
@Suppress("DEPRECATION", "UNCHECKED_CAST")
return (this as ReceiveChannel).onReceiveOrNull
}
-/**
- * Subscribes to this [BroadcastChannel] and performs the specified action for each received element.
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@ObsoleteCoroutinesApi
-public suspend inline fun BroadcastChannel.consumeEach(action: (E) -> Unit): Unit =
- consume {
- for (element in this) action(element)
- }
-
-// -------- Operations on ReceiveChannel --------
-
-/**
- * Returns a [List] containing all elements.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- */
-@OptIn(ExperimentalStdlibApi::class)
-public suspend fun ReceiveChannel.toList(): List = buildList {
- consumeEach {
- add(it)
- }
-}
-
-/**
- * Returns a [CompletionHandler] that invokes [cancel][ReceiveChannel.cancel] on the [ReceiveChannel]
- * with the corresponding cause. See also [ReceiveChannel.consume].
- *
- * **WARNING**: It is planned that in the future a second invocation of this method
- * on an channel that is already being consumed is going to fail fast, that it
- * immediately throws an [IllegalStateException].
- * See [this issue](https://github.com/Kotlin/kotlinx.coroutines/issues/167)
- * for details.
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public fun ReceiveChannel<*>.consumes(): CompletionHandler = { cause: Throwable? ->
- cancelConsumed(cause)
-}
-
-@PublishedApi
-internal fun ReceiveChannel<*>.cancelConsumed(cause: Throwable?) {
- cancel(cause?.let {
- it as? CancellationException ?: CancellationException("Channel was consumed, consumer had failed", it)
- })
-}
-
-/**
- * Returns a [CompletionHandler] that invokes [cancel][ReceiveChannel.cancel] on all the
- * specified [ReceiveChannel] instances with the corresponding cause.
- * See also [ReceiveChannel.consumes()] for a version on one channel.
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public fun consumesAll(vararg channels: ReceiveChannel<*>): CompletionHandler =
- { cause: Throwable? ->
- var exception: Throwable? = null
- for (channel in channels)
- try {
- channel.cancelConsumed(cause)
- } catch (e: Throwable) {
- if (exception == null) {
- exception = e
- } else {
- exception.addSuppressedThrowable(e)
- }
- }
- exception?.let { throw it }
- }
-
/**
* Makes sure that the given [block] consumes all elements from the given channel
* by always invoking [cancel][ReceiveChannel.cancel] after the execution of the block.
@@ -194,2006 +105,35 @@ public suspend inline fun ReceiveChannel.consumeEach(action: (E) -> Unit)
}
/**
- * Performs the given [action] for each received element.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend inline fun ReceiveChannel.consumeEachIndexed(action: (IndexedValue) -> Unit) {
- var index = 0
- consumeEach {
- action(IndexedValue(index++, it))
- }
-}
-
-/**
- * Returns an element at the given [index] or throws an [IndexOutOfBoundsException] if the [index] is out of bounds of this channel.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend fun ReceiveChannel.elementAt(index: Int): E =
- elementAtOrElse(index) { throw IndexOutOfBoundsException("ReceiveChannel doesn't contain element at index $index.") }
-
-/**
- * Returns an element at the given [index] or the result of calling the [defaultValue] function if the [index] is out of bounds of this channel.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend inline fun ReceiveChannel.elementAtOrElse(index: Int, defaultValue: (Int) -> E): E =
- consume {
- if (index < 0)
- return defaultValue(index)
- var count = 0
- for (element in this) {
- if (index == count++)
- return element
- }
- return defaultValue(index)
- }
-
-/**
- * Returns an element at the given [index] or `null` if the [index] is out of bounds of this channel.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend fun ReceiveChannel.elementAtOrNull(index: Int): E? =
- consume {
- if (index < 0)
- return null
- var count = 0
- for (element in this) {
- if (index == count++)
- return element
- }
- return null
- }
-
-/**
- * Returns the first element matching the given [predicate], or `null` if no such element was found.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend inline fun ReceiveChannel.find(predicate: (E) -> Boolean): E? =
- firstOrNull(predicate)
-
-/**
- * Returns the last element matching the given [predicate], or `null` if no such element was found.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend inline fun ReceiveChannel.findLast(predicate: (E) -> Boolean): E? =
- lastOrNull(predicate)
-
-/**
- * Returns first element.
- * @throws [NoSuchElementException] if the channel is empty.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend fun ReceiveChannel.first(): E =
- consume {
- val iterator = iterator()
- if (!iterator.hasNext())
- throw NoSuchElementException("ReceiveChannel is empty.")
- return iterator.next()
- }
-
-/**
- * Returns the first element matching the given [predicate].
- * @throws [NoSuchElementException] if no such element is found.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend inline fun ReceiveChannel.first(predicate: (E) -> Boolean): E {
- consumeEach {
- if (predicate(it)) return it
- }
- throw NoSuchElementException("ReceiveChannel contains no element matching the predicate.")
-}
-
-/**
- * Returns the first element, or `null` if the channel is empty.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend fun ReceiveChannel.firstOrNull(): E? =
- consume {
- val iterator = iterator()
- if (!iterator.hasNext())
- return null
- return iterator.next()
- }
-
-/**
- * Returns the first element matching the given [predicate], or `null` if element was not found.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend inline fun ReceiveChannel.firstOrNull(predicate: (E) -> Boolean): E? {
- consumeEach {
- if (predicate(it)) return it
- }
- return null
-}
-
-/**
- * Returns first index of [element], or -1 if the channel does not contain element.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend fun ReceiveChannel.indexOf(element: E): Int {
- var index = 0
- consumeEach {
- if (element == it)
- return index
- index++
- }
- return -1
-}
-
-/**
- * Returns index of the first element matching the given [predicate], or -1 if the channel does not contain such element.
- *
- * The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.**
- * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254).
- */
-@Deprecated(
- message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x",
- level = DeprecationLevel.ERROR
-)
-public suspend inline fun