Merge "Opt-in -Xexpect-actual-classes for kotlinx_coroutines" into main
diff --git a/.gitignore b/.gitignore
index 76d3585..8dc03d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@
local.properties
benchmarks.jar
/kotlin-js-store
+/.kotlin
diff --git a/Android.bp b/Android.bp
index 9c0ce46..90ea70b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -46,7 +46,9 @@
"kotlinx-coroutines-core/common/src/**/*.kt",
"kotlinx-coroutines-core/concurrent/src/**/*.kt",
],
- exclude_srcs: [
+ exclude_common_srcs: [
+ // TODO: go/kotlinx.coroutines/issues/4163 - This file should be deleted
+ "kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt",
"kotlinx-coroutines-core/jvm/src/test_/TestCoroutineContext.kt",
],
java_resource_dirs: ["kotlinx-coroutines-core/jvm/resources"],
@@ -80,6 +82,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ kotlin_lang_version: "2",
}
// Expose the host library to Android targets. This is generally an unsafe operation; in using
diff --git a/CHANGES.md b/CHANGES.md
index cf1e73e..8b843f9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,73 @@
# Change log for kotlinx.coroutines
+## Version 1.9.0
+
+### Features
+
+* Wasm/WASI target support (#4064). Thanks, @igoriakovlev!
+* `limitedParallelism` now optionally accepts the name of the dispatcher view for easier debugging (#4023).
+* No longer initialize `Dispatchers.IO` on the JVM when other standard dispatchers are accessed (#4166). Thanks, @metalhead8816!
+* Introduced the `Flow<T>.chunked(size: Int): Flow<List<T>>` operator that groups emitted values into groups of the given size (#1290).
+* Closeable dispatchers are instances of `AutoCloseable` now (#4123).
+
+### Fixes
+
+* Calling `hasNext` on a `Channel`'s iterator is idempotent (#4065). Thanks, @gitpaxultek!
+* `CoroutineScope()` created without an explicit dispatcher uses `Dispatchers.Default` on Native (#4074). Thanks, @whyoleg!
+* Fixed a bug that prevented non-Android `Dispatchers.Main` from initializing when the Firebase dependency is used (#3914).
+* Ensured a more intuitive ordering of tasks in `runBlocking` (#4134).
+* Forbid casting a `Mutex` to `Semaphore` (#4176).
+* Worked around a stack overflow that may occur when calling `asDeferred` on a `Future` many times (#4156).
+
+### Deprecations and promotions
+
+* Advanced the deprecation levels for `BroadcastChannel`-based API (#4197).
+* Advanced the deprecation levels for the old `kotlinx-coroutines-test` API (#4198).
+* Deprecated `Job.cancelFutureOnCompletion` (#4173).
+* Promoted `CoroutineDispatcher.limitedParallelism` to stable (#3864).
+* Promoted `CoroutineStart.ATOMIC` from `ExperimentalCoroutinesApi` to `DelicateCoroutinesApi` (#4169).
+* Promoted `CancellableContinuation.resume` with an `onCancellation` lambda to stable, providing extra arguments to the lambda (#4088).
+* Marked the classes and interfaces that are not supposed to be inherited from with the new `InternalForInheritanceCoroutinesApi` opt-in (#3770).
+* Marked the classes and interfaces inheriting from which is not stable with the new `ExperimentalForInheritanceCoroutinesApi` opt-in (#3770).
+
+### Other
+
+* Kotlin was updated to 2.0 (#4137).
+* Reworked the documentation for `CoroutineStart` and `Channel`-based API (#4147, #4148, #4167). Thanks, @globsterg!
+* Simplified the internal implementation of `Job` (#4053).
+* Small tweaks, fixes, and documentation improvements.
+
+## Version 1.9.0-RC.2
+
+* Advanced the deprecation levels for `BroadcastChannel`-based API (#4197).
+* Advanced the deprecation levels for the old `kotlinx-coroutines-test` API (#4198).
+* Promoted `CoroutineStart.ATOMIC` from `ExperimentalCoroutinesApi` to `DelicateCoroutinesApi` (#4169).
+* Reworked the documentation for `CoroutineStart` and `Channel`-based API (#4147, #4148, #4167). Thanks, @globsterg!
+* Forbid casting a `Mutex` to `Semaphore` (#4176).
+* Deprecated `Job.cancelFutureOnCompletion` (#4173).
+* Worked around a stack overflow that may occur when calling `asDeferred` on a `Future` many times (#4156).
+* Fixed a bug that disallowed setting a custom `probeCoroutineResumed` when starting coroutines with `UNDISPATCHED` (#4162).
+* No longer initialize `Dispatchers.IO` on the JVM when other standard dispatchers are accessed (#4166). Thanks, @metalhead8816!
+* Small tweaks, fixes, and documentation improvements.
+
+## Version 1.9.0-RC
+
+* Kotlin was updated to 2.0 (#4137).
+* Introduced the `Flow<T>.chunked(size: Int): Flow<List<T>>` operator that groups emitted values into groups of the given size (#1290).
+* Closeable dispatchers are instances of `AutoCloseable` now (#4123).
+* `limitedParallelism` now optionally accepts the name of the dispatcher view for easier debugging (#4023).
+* Marked the classes and interfaces that are not supposed to be inherited from with the new `InternalForInheritanceCoroutinesApi` opt-in (#3770).
+* Marked the classes and interfaces inheriting from which is not stable with the new `ExperimentalForInheritanceCoroutinesApi` opt-in (#3770).
+* Wasm/WASI target support (#4064). Thanks, @igoriakovlev!
+* Promoted `CoroutineDispatcher.limitedParallelism` to stable (#3864).
+* Promoted `CancellableContinuation.resume` with an `onCancellation` lambda to stable, providing extra arguments to the lambda (#4088).
+* Ensured a more intuitive ordering of tasks in `runBlocking` (#4134).
+* Simplified the internal implementation of `Job` (#4053).
+* Fixed a bug that prevented non-Android `Dispatchers.Main` from initializing when the Firebase dependency is used (#3914).
+* Calling `hasNext` on a `Channel`'s iterator is idempotent (#4065). Thanks, @gitpaxultek!
+* `CoroutineScope()` created without an explicit dispatcher uses `Dispatchers.Default` on Native (#4074). Thanks, @whyoleg!
+* Small tweaks and documentation fixes.
+
## Version 1.8.1
* Remove the `@ExperimentalTime` annotation from usages of `TimeSource` (#4046). Thanks, @hfhbd!
diff --git a/METADATA b/METADATA
index 68041d8..f00165f 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
last_upgrade_date {
year: 2024
month: 10
- day: 17
+ day: 24
}
identifier {
type: "Git"
value: "https://github.com/Kotlin/kotlinx.coroutines"
- version: "1.8.1"
+ version: "1.9.0"
}
}
diff --git a/OWNERS b/OWNERS
index d325c3f..679a35a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,3 @@
[email protected]
[email protected]
+include platform/system/core:/janitors/OWNERS #{LAST_RESORT_SUGGESTION}
diff --git a/README.md b/README.md
index 0cf3c19..89df68e 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,12 @@
[![Kotlin Stable](https://kotl.in/badges/stable.svg)](https://kotlinlang.org/docs/components-stability.html)
[![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0)
-[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.8.1)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.8.1)
-[![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-blue.svg?logo=kotlin)](http://kotlinlang.org)
+[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.9.0)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.9.0)
+[![Kotlin](https://img.shields.io/badge/kotlin-2.0.0-blue.svg?logo=kotlin)](http://kotlinlang.org)
[![Slack channel](https://img.shields.io/badge/chat-slack-green.svg?logo=slack)](https://kotlinlang.slack.com/messages/coroutines/)
Library support for Kotlin coroutines with [multiplatform](#multiplatform) support.
-This is a companion version for the Kotlin `1.9.21` release.
+This is a companion version for the Kotlin `2.0.0` release.
```kotlin
suspend fun main() = coroutineScope {
@@ -43,7 +43,7 @@
* Integration with `Window` via [Window.asCoroutineDispatcher], etc.
* [test](kotlinx-coroutines-test/README.md) — test utilities for coroutines:
* [Dispatchers.setMain] to override [Dispatchers.Main] in tests;
- * [TestCoroutineScope] to test suspending functions and coroutines.
+ * [runTest] and [TestScope] to test suspending functions and coroutines.
* [debug](kotlinx-coroutines-debug/README.md) — debug utilities for coroutines:
* [DebugProbes] API to probe, keep track of, print and dump active coroutines;
* [CoroutinesTimeout] test rule to automatically dump coroutines on test timeout.
@@ -85,7 +85,7 @@
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
- <version>1.8.1</version>
+ <version>1.9.0</version>
</dependency>
```
@@ -93,7 +93,7 @@
```xml
<properties>
- <kotlin.version>1.9.21</kotlin.version>
+ <kotlin.version>2.0.0</kotlin.version>
</properties>
```
@@ -103,7 +103,7 @@
```kotlin
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
}
```
@@ -112,10 +112,10 @@
```kotlin
plugins {
// For build.gradle.kts (Kotlin DSL)
- kotlin("jvm") version "1.9.21"
+ kotlin("jvm") version "2.0.0"
// For build.gradle (Groovy DSL)
- id "org.jetbrains.kotlin.jvm" version "1.9.21"
+ id "org.jetbrains.kotlin.jvm" version "2.0.0"
}
```
@@ -133,7 +133,7 @@
module as a dependency when using `kotlinx.coroutines` on Android:
```kotlin
-implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
+implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
```
This gives you access to the Android [Dispatchers.Main]
@@ -168,7 +168,7 @@
```kotlin
commonMain {
dependencies {
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
}
}
```
@@ -178,7 +178,7 @@
#### JS
Kotlin/JS version of `kotlinx.coroutines` is published as
-[`kotlinx-coroutines-core-js`](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.8.1)
+[`kotlinx-coroutines-core-js`](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.9.0)
(follow the link to get the dependency declaration snippet).
#### Native
@@ -240,7 +240,8 @@
<!--- INDEX kotlinx.coroutines.test -->
[Dispatchers.setMain]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html
-[TestCoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/index.html
+[runTest]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html
+[TestScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/index.html
<!--- MODULE kotlinx-coroutines-debug -->
<!--- INDEX kotlinx.coroutines.debug -->
diff --git a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt
index 14bc44b..47d4cb2 100644
--- a/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt
+++ b/benchmarks/src/jmh/kotlin/benchmarks/scheduler/StatefulAwaitsBenchmark.kt
@@ -40,6 +40,7 @@
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
+@Suppress("DEPRECATION_ERROR")
open class StatefulAsyncBenchmark : ParametrizedDispatcherBase() {
private val stateSize = 2048
diff --git a/build.gradle.kts b/build.gradle.kts
index 002a1d8..caef126 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -66,7 +66,7 @@
}
plugins {
- id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.2"
+ id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.2"
}
apply(plugin = "base")
@@ -78,6 +78,10 @@
ignoredProjects += coreModule
}
ignoredPackages += "kotlinx.coroutines.internal"
+ @OptIn(kotlinx.validation.ExperimentalBCVApi::class)
+ klib {
+ enabled = true
+ }
}
// Configure repositories
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 96d718e..d8c7c19 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -55,6 +55,8 @@
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
}
+ // Force ASM version, otherwise the one from animalsniffer wins (which is too low for BCV)
+ implementation("org.ow2.asm:asm:9.6")
implementation("ru.vyarus:gradle-animalsniffer-plugin:${version("animalsniffer")}") // Android API check
implementation("org.jetbrains.kotlinx:kover-gradle-plugin:${version("kover")}") {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
diff --git a/buildSrc/src/main/kotlin/AuxBuildConfiguration.kt b/buildSrc/src/main/kotlin/AuxBuildConfiguration.kt
index 3acba9a..558f95c 100644
--- a/buildSrc/src/main/kotlin/AuxBuildConfiguration.kt
+++ b/buildSrc/src/main/kotlin/AuxBuildConfiguration.kt
@@ -19,7 +19,6 @@
}
CacheRedirector.configureJsPackageManagers(rootProject)
- CacheRedirector.configureWasmNodeRepositories(rootProject)
// Sigh, there is no BuildScanExtension in classpath when there is no --scan
rootProject.extensions.findByName("buildScan")?.withGroovyBuilder {
diff --git a/buildSrc/src/main/kotlin/CacheRedirector.kt b/buildSrc/src/main/kotlin/CacheRedirector.kt
index 5b00bd0..084ffe7 100644
--- a/buildSrc/src/main/kotlin/CacheRedirector.kt
+++ b/buildSrc/src/main/kotlin/CacheRedirector.kt
@@ -138,23 +138,6 @@
project.configureYarnAndNodeRedirects()
}
- /**
- * Temporary repositories to depend on until GC milestone 4 in KGP
- * and stable Node release. Safe to remove when its removal does not break WASM tests.
- */
- @JvmStatic
- fun configureWasmNodeRepositories(project: Project) {
- val extension = project.extensions.findByType<NodeJsRootExtension>()
- if (extension != null) {
- extension.nodeVersion = "21.0.0-v8-canary202309167e82ab1fa2"
- extension.nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary"
- }
-
- project.tasks.withType<KotlinNpmInstallTask>().configureEach {
- args.add("--ignore-engines")
- }
- }
-
@JvmStatic
fun maybeRedirect(url: String): String {
if (!cacheRedirectorEnabled) return url
diff --git a/buildSrc/src/main/kotlin/GlobalKotlinCompilerOptions.kt b/buildSrc/src/main/kotlin/GlobalKotlinCompilerOptions.kt
new file mode 100644
index 0000000..5e16671
--- /dev/null
+++ b/buildSrc/src/main/kotlin/GlobalKotlinCompilerOptions.kt
@@ -0,0 +1,14 @@
+import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions
+
+internal fun KotlinCommonCompilerOptions.configureGlobalKotlinArgumentsAndOptIns() {
+ freeCompilerArgs.addAll("-progressive")
+ optIn.addAll(
+ "kotlin.experimental.ExperimentalTypeInference",
+ // our own opt-ins that we don't want to bother with in our own code:
+ "kotlinx.coroutines.DelicateCoroutinesApi",
+ "kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "kotlinx.coroutines.ObsoleteCoroutinesApi",
+ "kotlinx.coroutines.InternalCoroutinesApi",
+ "kotlinx.coroutines.FlowPreview"
+ )
+}
diff --git a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts
index 26ffe11..c03deb1 100644
--- a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts
@@ -1,12 +1,9 @@
-import org.jetbrains.kotlin.gradle.dsl.*
-import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.*
-import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
configure(subprojects) {
val project = this
if (name in sourceless) return@configure
- apply(plugin = "kotlinx-atomicfu")
+ apply(plugin = "org.jetbrains.kotlinx.atomicfu")
tasks.withType<KotlinCompilationTask<*>>().configureEach {
val isMainTaskName = name.startsWith("compileKotlin")
compilerOptions {
@@ -39,17 +36,6 @@
"kotlin.experimental.ExperimentalNativeApi",
)
}
- freeCompilerArgs.addAll("-progressive", "-Xexpect-actual-classes")
- optIn.addAll(
- "kotlin.experimental.ExperimentalTypeInference",
- "kotlin.ExperimentalMultiplatform",
- // our own opt-ins that we don't want to bother with in our own code:
- "kotlinx.coroutines.DelicateCoroutinesApi",
- "kotlinx.coroutines.ExperimentalCoroutinesApi",
- "kotlinx.coroutines.ObsoleteCoroutinesApi",
- "kotlinx.coroutines.InternalCoroutinesApi",
- "kotlinx.coroutines.FlowPreview"
- )
}
}
diff --git a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts
index 34c45d1..e467865 100644
--- a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts
@@ -15,6 +15,7 @@
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_1_8
+ configureGlobalKotlinArgumentsAndOptIns()
}
jvmToolchain(jdkToolchainVersion)
}
diff --git a/buildSrc/src/main/kotlin/kotlin-multiplatform-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotlin-multiplatform-conventions.gradle.kts
index fd2d3fd..273bf10 100644
--- a/buildSrc/src/main/kotlin/kotlin-multiplatform-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/kotlin-multiplatform-conventions.gradle.kts
@@ -63,6 +63,18 @@
api("org.jetbrains.kotlinx:atomicfu-wasm-js:${version("atomicfu")}")
}
}
+ @OptIn(org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl::class)
+ wasmWasi {
+ nodejs()
+ compilations["main"]?.dependencies {
+ api("org.jetbrains.kotlinx:atomicfu-wasm-wasi:${version("atomicfu")}")
+ }
+ compilations.configureEach {
+ compilerOptions.configure {
+ optIn.add("kotlin.wasm.internal.InternalWasmApi")
+ }
+ }
+ }
applyDefaultHierarchyTemplate()
sourceSets {
commonTest {
@@ -101,15 +113,33 @@
api("org.jetbrains.kotlin:kotlin-test-wasm-js:${version("kotlin")}")
}
}
- groupSourceSets("jsAndWasmShared", listOf("js", "wasmJs"), listOf("common"))
+ val wasmWasiMain by getting {
+ }
+ val wasmWasiTest by getting {
+ dependencies {
+ api("org.jetbrains.kotlin:kotlin-test-wasm-wasi:${version("kotlin")}")
+ }
+ }
+ groupSourceSets("jsAndWasmJsShared", listOf("js", "wasmJs"), emptyList())
+ groupSourceSets("jsAndWasmShared", listOf("jsAndWasmJsShared", "wasmWasi"), listOf("common"))
+ }
+
+ @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
+ compilerOptions {
+ configureGlobalKotlinArgumentsAndOptIns()
+ freeCompilerArgs.add("-Xexpect-actual-classes")
+ optIn.add("kotlin.ExperimentalMultiplatform")
}
}
-// Disable intermediate sourceSet compilation because we do not need js-wasmJs artifact
+// Disable intermediate sourceSet compilation because we do not need js-wasm common artifact
tasks.configureEach {
if (name == "compileJsAndWasmSharedMainKotlinMetadata") {
enabled = false
}
+ if (name == "compileJsAndWasmJsSharedMainKotlinMetadata") {
+ enabled = false
+ }
}
tasks.named("jvmTest", Test::class) {
diff --git a/docs/topics/channels.md b/docs/topics/channels.md
index 6820f4c..402fb5a 100644
--- a/docs/topics/channels.md
+++ b/docs/topics/channels.md
@@ -364,7 +364,7 @@
>
{type="note"}
-The output will be similar to the the following one, albeit the processor ids that receive
+The output will be similar to the following one, albeit the processor ids that receive
each specific integer may be different:
```text
diff --git a/docs/topics/flow.md b/docs/topics/flow.md
index b678b70..c436c2c 100644
--- a/docs/topics/flow.md
+++ b/docs/topics/flow.md
@@ -677,14 +677,16 @@
Notice how `flow { ... }` works in the background thread, while collection happens in the main thread:
-<!--- TEST FLEXIBLE_THREAD
+```text
[DefaultDispatcher-worker-1 @coroutine#2] Emitting 1
[main @coroutine#1] Collected 1
[DefaultDispatcher-worker-1 @coroutine#2] Emitting 2
[main @coroutine#1] Collected 2
[DefaultDispatcher-worker-1 @coroutine#2] Emitting 3
[main @coroutine#1] Collected 3
--->
+```
+
+<!--- TEST FLEXIBLE_THREAD -->
Another thing to observe here is that the [flowOn] operator has changed the default sequential nature of the flow.
Now collection happens in one coroutine ("coroutine#1") and emission happens in another coroutine
diff --git a/gradle.properties b/gradle.properties
index 59063db..168fb6b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,15 +1,15 @@
# Kotlin
-version=1.8.1-SNAPSHOT
+version=1.9.0-SNAPSHOT
group=org.jetbrains.kotlinx
-kotlin_version=1.9.21
+kotlin_version=2.0.0
+kotlin_language_version=2.0
# DO NOT rename this property without adapting kotlinx.train build chain:
-atomicfu_version=0.23.1
+atomicfu_version=0.25.0
# Dependencies
junit_version=4.12
junit5_version=5.7.0
knit_version=0.5.0
-html_version=0.7.2
lincheck_version=2.18.1
dokka_version=1.9.20
byte_buddy_version=1.10.9
@@ -20,8 +20,8 @@
rxjava3_version=3.0.2
javafx_version=17.0.2
javafx_plugin_version=0.0.8
-binary_compatibility_validator_version=0.13.2
-kover_version=0.8.0-Beta
+binary_compatibility_validator_version=0.16.2
+kover_version=0.8.0-Beta2
blockhound_version=1.0.8.RELEASE
jna_version=5.9.0
diff --git a/integration-testing/gradle.properties b/integration-testing/gradle.properties
index af5497e..60d2fb5 100644
--- a/integration-testing/gradle.properties
+++ b/integration-testing/gradle.properties
@@ -1,5 +1,5 @@
-kotlin_version=1.9.21
-coroutines_version=1.8.1-SNAPSHOT
+kotlin_version=2.0.0
+coroutines_version=1.9.0-SNAPSHOT
asm_version=9.3
kotlin.code.style=official
diff --git a/integration-testing/smokeTest/build.gradle b/integration-testing/smokeTest/build.gradle
index 16c8663..65d09df 100644
--- a/integration-testing/smokeTest/build.gradle
+++ b/integration-testing/smokeTest/build.gradle
@@ -48,6 +48,11 @@
implementation kotlin('test-wasm-js')
}
}
+ wasmWasiTest {
+ dependencies {
+ implementation kotlin('test-wasm-wasi')
+ }
+ }
jvmTest {
dependencies {
implementation kotlin('test')
diff --git a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt
index fdf24bc..ea9addc 100644
--- a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt
+++ b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt
@@ -151,6 +151,7 @@
cancel(false)
}
// Return hides the CompletableDeferred. This should prevent casting.
+ @OptIn(InternalForInheritanceCoroutinesApi::class)
return object : Deferred<T> by deferred {}
}
diff --git a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt
index a873206..e8625dc 100644
--- a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt
+++ b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt
@@ -627,16 +627,15 @@
fun testFutureIsDoneAfterChildrenCompleted() = runTest {
expect(1)
val testException = TestException()
+ val latch = CountDownLatch(1)
// Don't propagate exception to the test and use different dispatchers as we are going to block test thread.
val future = future(context = NonCancellable + Dispatchers.Default) {
- val foo = async {
+ val foo = async(start = CoroutineStart.UNDISPATCHED) {
try {
delay(Long.MAX_VALUE)
42
} finally {
- withContext(NonCancellable) {
- delay(200)
- }
+ latch.await()
}
}
foo.invokeOnCompletion {
@@ -647,6 +646,7 @@
}
yield()
expect(2)
+ latch.countDown()
// Blocking get should succeed after internal coroutine completes.
val thrown = assertFailsWith<ExecutionException> { future.get() }
expect(4)
diff --git a/integration/kotlinx-coroutines-play-services/src/Tasks.kt b/integration/kotlinx-coroutines-play-services/src/Tasks.kt
index be4069d..946449b 100644
--- a/integration/kotlinx-coroutines-play-services/src/Tasks.kt
+++ b/integration/kotlinx-coroutines-play-services/src/Tasks.kt
@@ -87,6 +87,7 @@
}
}
// Prevent casting to CompletableDeferred and manual completion.
+ @OptIn(InternalForInheritanceCoroutinesApi::class)
return object : Deferred<T> by deferred {}
}
diff --git a/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt b/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt
index fb4bdfe..32830fc 100644
--- a/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt
+++ b/integration/kotlinx-coroutines-slf4j/src/MDCContext.kt
@@ -33,9 +33,9 @@
*
* ```
* launch(MDCContext()) {
- * MDC.put("key", "value") // This update will be lost
- * delay(100)
- * println(MDC.get("key")) // This will print null
+ * MDC.put("key", "value") // This update will be lost
+ * delay(100)
+ * println(MDC.get("key")) // This will print null
* }
* ```
*
diff --git a/kotlinx-coroutines-core/README.md b/kotlinx-coroutines-core/README.md
index 6f59b68..1fbd90d 100644
--- a/kotlinx-coroutines-core/README.md
+++ b/kotlinx-coroutines-core/README.md
@@ -13,10 +13,12 @@
Coroutine dispatchers implementing [CoroutineDispatcher]:
-| **Name** | **Description**
-| ------------------------------------------------------------------- | ---------------
-| [Dispatchers.Default][kotlinx.coroutines.Dispatchers.Default] | Confines coroutine execution to a shared pool of background threads
-| [Dispatchers.Unconfined][kotlinx.coroutines.Dispatchers.Unconfined] | Does not confine coroutine execution in any way
+| **Name** | **Description**
+| --------------------------------------------------------------------------------------------------- | ---------------
+| [Dispatchers.Main][kotlinx.coroutines.Dispatchers.Main] | Confines coroutine execution to the UI thread
+| [Dispatchers.Default][kotlinx.coroutines.Dispatchers.Default] | Confines coroutine execution to a shared pool of background threads
+| [Dispatchers.Unconfined][kotlinx.coroutines.Dispatchers.Unconfined] | Does not confine coroutine execution in any way
+| [CoroutineDispatcher.limitedParallelism][kotlinx.coroutines.CoroutineDispatcher.limitedParallelism] | Creates a view of the given dispatcher, limiting the number of tasks executing in parallel
More context elements:
@@ -27,10 +29,14 @@
Synchronization primitives for coroutines:
-| **Name** | **Suspending functions** | **Description**
-| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------
-| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | Mutual exclusion
-| [Channel][kotlinx.coroutines.channels.Channel] | [send][kotlinx.coroutines.channels.SendChannel.send], [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | Communication channel (aka queue or exchanger)
+| **Name** | **Suspending functions** | **Description**
+|------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| ---------------
+| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | Mutual exclusion
+| [Semaphore][kotlinx.coroutines.sync.Semaphore] | [acquire][kotlinx.coroutines.sync.Semaphore.acquire] | Limiting the maximum concurrency
+| [Channel][kotlinx.coroutines.channels.Channel] | [send][kotlinx.coroutines.channels.SendChannel.send], [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | Communication channel (aka queue or exchanger)
+| [Flow](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/) | [collect][kotlinx.coroutines.flow.Flow.collect] | Asynchronous stream of values
+
+<!--- Flow direct link is here to workaround MD case-insensitivity -->
Top-level suspending functions:
@@ -45,10 +51,27 @@
| [joinAll][kotlinx.coroutines.joinAll] | Joins on all given jobs
Cancellation support for user-defined suspending functions is available with [suspendCancellableCoroutine]
-helper function. [NonCancellable] job object is provided to suppress cancellation with
+helper function.
+The [NonCancellable] job object is provided to suppress cancellation inside the
`withContext(NonCancellable) {...}` block of code.
-[Select][kotlinx.coroutines.selects.select] expression waits for the result of multiple suspending functions simultaneously:
+Ways to construct asynchronous streams of values:
+
+| **Name** | **Type** | **Description**
+| --------------------------------------------------------------------- | -------- | ---------------
+| [flow][kotlinx.coroutines.flow.flow] | cold | Runs a generator-style block of code that emits values
+| [flowOf][kotlinx.coroutines.flow.flowOf] | cold | Emits the values passed as arguments
+| [channelFlow][kotlinx.coroutines.flow.channelFlow] | cold | Runs the given code, providing a channel sending to which means emitting from the flow
+| [callbackFlow][kotlinx.coroutines.flow.callbackFlow] | cold | Allows transforming a callback-based API into a flow
+| [ReceiveChannel.consumeAsFlow][kotlinx.coroutines.flow.consumeAsFlow] | hot | Transforms a channel into a flow, emitting all of the received values to a single subscriber
+| [ReceiveChannel.receiveAsFlow][kotlinx.coroutines.flow.receiveAsFlow] | hot | Transforms a channel into a flow, distributing the received values among its subscribers
+| [MutableSharedFlow][kotlinx.coroutines.flow.MutableSharedFlow] | hot | Allows emitting each value to arbitrarily many subscribers at once
+| [MutableStateFlow][kotlinx.coroutines.flow.MutableStateFlow] | hot | Represents mutable state as a flow
+
+A *cold* stream is some process of generating values, and this process is performed separately for each subscriber.
+A *hot* stream uses the same source of values independently of whether there are subscribers.
+
+A [select][kotlinx.coroutines.selects.select] expression waits for the result of multiple suspending functions simultaneously:
| **Receiver** | **Suspending function** | **Select clause** | **Non-suspending version**
| ------------------------------------------------------------ | --------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------
@@ -65,7 +88,7 @@
# Package kotlinx.coroutines.sync
-Synchronization primitives (mutex).
+Synchronization primitives (mutex and semaphore).
# Package kotlinx.coroutines.channels
@@ -73,16 +96,28 @@
# Package kotlinx.coroutines.flow
-Flow — asynchronous cold stream of elements.
+Flow — asynchronous cold and hot streams of elements.
# Package kotlinx.coroutines.selects
-Select expression to perform multiple suspending operations simultaneously until one of them succeeds.
+Select — expressions that perform multiple suspending operations simultaneously until one of them succeeds.
# Package kotlinx.coroutines.intrinsics
Low-level primitives for finer-grained control of coroutines.
+# Package kotlinx.coroutines.future
+
+[JDK 8's `CompletableFuture`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) support.
+
+# Package kotlinx.coroutines.stream
+
+[JDK 8's `Stream`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) support.
+
+# Package kotlinx.coroutines.time
+
+[JDK 8's `Duration`](https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html) support via additional overloads for existing time-based operators.
+
<!--- MODULE kotlinx-coroutines-core -->
<!--- INDEX kotlinx.coroutines -->
@@ -93,8 +128,10 @@
[kotlinx.coroutines.Deferred]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html
[kotlinx.coroutines.runBlocking]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
[CoroutineDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html
+[kotlinx.coroutines.Dispatchers.Main]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
[kotlinx.coroutines.Dispatchers.Default]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
[kotlinx.coroutines.Dispatchers.Unconfined]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html
+[kotlinx.coroutines.CoroutineDispatcher.limitedParallelism]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/limited-parallelism.html
[kotlinx.coroutines.NonCancellable]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable/index.html
[kotlinx.coroutines.CoroutineExceptionHandler]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html
[kotlinx.coroutines.delay]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html
@@ -112,10 +149,24 @@
[kotlinx.coroutines.Deferred.await]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html
[kotlinx.coroutines.Deferred.onAwait]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/on-await.html
+<!--- INDEX kotlinx.coroutines.flow -->
+
+[kotlinx.coroutines.flow.Flow.collect]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/collect.html
+[kotlinx.coroutines.flow.flow]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow.html
+[kotlinx.coroutines.flow.flowOf]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow-of.html
+[kotlinx.coroutines.flow.channelFlow]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/channel-flow.html
+[kotlinx.coroutines.flow.callbackFlow]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/callback-flow.html
+[kotlinx.coroutines.flow.consumeAsFlow]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/consume-as-flow.html
+[kotlinx.coroutines.flow.receiveAsFlow]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/receive-as-flow.html
+[kotlinx.coroutines.flow.MutableSharedFlow]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-mutable-shared-flow/index.html
+[kotlinx.coroutines.flow.MutableStateFlow]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-mutable-state-flow/index.html
+
<!--- INDEX kotlinx.coroutines.sync -->
[kotlinx.coroutines.sync.Mutex]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/index.html
[kotlinx.coroutines.sync.Mutex.lock]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/lock.html
+[kotlinx.coroutines.sync.Semaphore]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-semaphore/index.html
+[kotlinx.coroutines.sync.Semaphore.acquire]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-semaphore/acquire.html
<!--- INDEX kotlinx.coroutines.channels -->
diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
index 2674d73..1f5b05a 100644
--- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
+++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
@@ -39,10 +39,11 @@
public abstract fun isCancelled ()Z
public abstract fun isCompleted ()Z
public abstract fun resume (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
+ public abstract fun resume (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V
public abstract fun resumeUndispatched (Lkotlinx/coroutines/CoroutineDispatcher;Ljava/lang/Object;)V
public abstract fun resumeUndispatchedWithException (Lkotlinx/coroutines/CoroutineDispatcher;Ljava/lang/Throwable;)V
public abstract fun tryResume (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- public abstract fun tryResume (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
+ public abstract fun tryResume (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Ljava/lang/Object;
public abstract fun tryResumeWithException (Ljava/lang/Throwable;)Ljava/lang/Object;
}
@@ -51,10 +52,10 @@
public static synthetic fun tryResume$default (Lkotlinx/coroutines/CancellableContinuation;Ljava/lang/Object;Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;
}
-public class kotlinx/coroutines/CancellableContinuationImpl : kotlinx/coroutines/DispatchedTask, kotlin/coroutines/jvm/internal/CoroutineStackFrame, kotlinx/coroutines/CancellableContinuation, kotlinx/coroutines/Waiter {
+public class kotlinx/coroutines/CancellableContinuationImpl : kotlin/coroutines/jvm/internal/CoroutineStackFrame, kotlinx/coroutines/CancellableContinuation, kotlinx/coroutines/Waiter {
public fun <init> (Lkotlin/coroutines/Continuation;I)V
public final fun callCancelHandler (Lkotlinx/coroutines/CancelHandler;Ljava/lang/Throwable;)V
- public final fun callOnCancellation (Lkotlin/jvm/functions/Function1;Ljava/lang/Throwable;)V
+ public final fun callOnCancellation (Lkotlin/jvm/functions/Function3;Ljava/lang/Throwable;Ljava/lang/Object;)V
public fun cancel (Ljava/lang/Throwable;)Z
public fun completeResume (Ljava/lang/Object;)V
public fun getCallerFrame ()Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;
@@ -70,12 +71,13 @@
public fun isCompleted ()Z
protected fun nameString ()Ljava/lang/String;
public fun resume (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
+ public fun resume (Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V
public fun resumeUndispatched (Lkotlinx/coroutines/CoroutineDispatcher;Ljava/lang/Object;)V
public fun resumeUndispatchedWithException (Lkotlinx/coroutines/CoroutineDispatcher;Ljava/lang/Throwable;)V
public fun resumeWith (Ljava/lang/Object;)V
public fun toString ()Ljava/lang/String;
public fun tryResume (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- public fun tryResume (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
+ public fun tryResume (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Ljava/lang/Object;
public fun tryResumeWithException (Ljava/lang/Throwable;)Ljava/lang/Object;
}
@@ -84,12 +86,6 @@
public static final fun suspendCancellableCoroutine (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
-public final class kotlinx/coroutines/ChildContinuation {
- public final field child Lkotlinx/coroutines/CancellableContinuationImpl;
- public fun <init> (Lkotlinx/coroutines/CancellableContinuationImpl;)V
- public fun invoke (Ljava/lang/Throwable;)V
-}
-
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;
@@ -176,7 +172,9 @@
public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public final fun interceptContinuation (Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
public fun isDispatchNeeded (Lkotlin/coroutines/CoroutineContext;)Z
- public fun limitedParallelism (I)Lkotlinx/coroutines/CoroutineDispatcher;
+ public synthetic fun limitedParallelism (I)Lkotlinx/coroutines/CoroutineDispatcher;
+ public fun limitedParallelism (ILjava/lang/String;)Lkotlinx/coroutines/CoroutineDispatcher;
+ public static synthetic fun limitedParallelism$default (Lkotlinx/coroutines/CoroutineDispatcher;ILjava/lang/String;ILjava/lang/Object;)Lkotlinx/coroutines/CoroutineDispatcher;
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public final fun plus (Lkotlinx/coroutines/CoroutineDispatcher;)Lkotlinx/coroutines/CoroutineDispatcher;
public final fun releaseInterceptedContinuation (Lkotlin/coroutines/Continuation;)V
@@ -318,15 +316,6 @@
public abstract interface annotation class kotlinx/coroutines/DelicateCoroutinesApi : java/lang/annotation/Annotation {
}
-public final class kotlinx/coroutines/DispatchedCoroutine {
- public static final synthetic fun get_decision$volatile$FU$kotlinx_coroutines_core ()Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;
-}
-
-public abstract class kotlinx/coroutines/DispatchedTask : kotlinx/coroutines/scheduling/Task {
- public field resumeMode I
- public final fun run ()V
-}
-
public final class kotlinx/coroutines/DispatchedTaskKt {
public static final field MODE_CANCELLABLE I
}
@@ -359,7 +348,7 @@
public static final fun CancellationException (Ljava/lang/String;Ljava/lang/Throwable;)Ljava/util/concurrent/CancellationException;
}
-public abstract class kotlinx/coroutines/ExecutorCoroutineDispatcher : kotlinx/coroutines/CoroutineDispatcher, java/io/Closeable {
+public abstract class kotlinx/coroutines/ExecutorCoroutineDispatcher : kotlinx/coroutines/CoroutineDispatcher, java/io/Closeable, java/lang/AutoCloseable {
public static final field Key Lkotlinx/coroutines/ExecutorCoroutineDispatcher$Key;
public fun <init> ()V
public abstract fun close ()V
@@ -378,6 +367,9 @@
public abstract interface annotation class kotlinx/coroutines/ExperimentalCoroutinesApi : java/lang/annotation/Annotation {
}
+public abstract interface annotation class kotlinx/coroutines/ExperimentalForInheritanceCoroutinesApi : java/lang/annotation/Annotation {
+}
+
public abstract interface annotation class kotlinx/coroutines/FlowPreview : java/lang/annotation/Annotation {
}
@@ -389,6 +381,9 @@
public abstract interface annotation class kotlinx/coroutines/InternalCoroutinesApi : java/lang/annotation/Annotation {
}
+public abstract interface annotation class kotlinx/coroutines/InternalForInheritanceCoroutinesApi : java/lang/annotation/Annotation {
+}
+
public final class kotlinx/coroutines/InterruptibleKt {
public static final fun runInterruptible (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun runInterruptible$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
@@ -459,7 +454,6 @@
public static synthetic fun cancelChildren$default (Lkotlinx/coroutines/Job;Ljava/lang/Throwable;ILjava/lang/Object;)V
public static synthetic fun cancelChildren$default (Lkotlinx/coroutines/Job;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V
public static final fun cancelFutureOnCancellation (Lkotlinx/coroutines/CancellableContinuation;Ljava/util/concurrent/Future;)V
- public static final fun cancelFutureOnCompletion (Lkotlinx/coroutines/Job;Ljava/util/concurrent/Future;)Lkotlinx/coroutines/DisposableHandle;
public static final fun ensureActive (Lkotlin/coroutines/CoroutineContext;)V
public static final fun ensureActive (Lkotlinx/coroutines/Job;)V
public static final fun getJob (Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/Job;
@@ -517,7 +511,7 @@
public abstract class kotlinx/coroutines/MainCoroutineDispatcher : kotlinx/coroutines/CoroutineDispatcher {
public fun <init> ()V
public abstract fun getImmediate ()Lkotlinx/coroutines/MainCoroutineDispatcher;
- public fun limitedParallelism (I)Lkotlinx/coroutines/CoroutineDispatcher;
+ public fun limitedParallelism (ILjava/lang/String;)Lkotlinx/coroutines/CoroutineDispatcher;
public fun toString ()Ljava/lang/String;
protected final fun toStringInternalImpl ()Ljava/lang/String;
}
@@ -970,12 +964,6 @@
public final fun getState ()Ljava/lang/String;
}
-public final class kotlinx/coroutines/debug/internal/StackTraceFrame : kotlin/coroutines/jvm/internal/CoroutineStackFrame {
- public final field stackTraceElement Ljava/lang/StackTraceElement;
- public fun getCallerFrame ()Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;
- public fun getStackTraceElement ()Ljava/lang/StackTraceElement;
-}
-
public abstract class kotlinx/coroutines/flow/AbstractFlow : kotlinx/coroutines/flow/CancellableFlow, kotlinx/coroutines/flow/Flow {
public fun <init> ()V
public final fun collect (Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -999,7 +987,6 @@
public static final fun asFlow (Lkotlin/ranges/IntRange;)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow (Lkotlin/ranges/LongRange;)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow (Lkotlin/sequences/Sequence;)Lkotlinx/coroutines/flow/Flow;
- public static final fun asFlow (Lkotlinx/coroutines/channels/BroadcastChannel;)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow ([I)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow ([J)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow ([Ljava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
@@ -1014,6 +1001,7 @@
public static final fun cancellable (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
public static final fun catch (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
public static final fun channelFlow (Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
+ public static final fun chunked (Lkotlinx/coroutines/flow/Flow;I)Lkotlinx/coroutines/flow/Flow;
public static final fun collect (Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final synthetic fun collect (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun collectIndexed (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -1270,27 +1258,6 @@
public static final fun startCoroutineCancellable (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)V
}
-public class kotlinx/coroutines/scheduling/ExperimentalCoroutineDispatcher : kotlinx/coroutines/ExecutorCoroutineDispatcher {
- public synthetic fun <init> (II)V
- public synthetic fun <init> (IIILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun <init> (IIJLjava/lang/String;)V
- public synthetic fun <init> (IIJLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun <init> (IILjava/lang/String;)V
- public synthetic fun <init> (IILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun blocking (I)Lkotlinx/coroutines/CoroutineDispatcher;
- public static synthetic fun blocking$default (Lkotlinx/coroutines/scheduling/ExperimentalCoroutineDispatcher;IILjava/lang/Object;)Lkotlinx/coroutines/CoroutineDispatcher;
- public fun close ()V
- public fun dispatch (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V
- public fun dispatchYield (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V
- public fun getExecutor ()Ljava/util/concurrent/Executor;
- public final fun limited (I)Lkotlinx/coroutines/CoroutineDispatcher;
- public fun toString ()Ljava/lang/String;
-}
-
-public abstract class kotlinx/coroutines/scheduling/Task : java/lang/Runnable {
- public field submissionTime J
-}
-
public final class kotlinx/coroutines/selects/OnTimeoutKt {
public static final fun onTimeout (Lkotlinx/coroutines/selects/SelectBuilder;JLkotlin/jvm/functions/Function1;)V
public static final fun onTimeout-8Mi8wO0 (Lkotlinx/coroutines/selects/SelectBuilder;JLkotlin/jvm/functions/Function1;)V
diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api
new file mode 100644
index 0000000..4cdf598
--- /dev/null
+++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api
@@ -0,0 +1,1098 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Alias: native => [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-coroutines-core>
+open annotation class kotlinx.coroutines/DelicateCoroutinesApi : kotlin/Annotation { // kotlinx.coroutines/DelicateCoroutinesApi|null[0]
+ constructor <init>() // kotlinx.coroutines/DelicateCoroutinesApi.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.coroutines/ExperimentalCoroutinesApi : kotlin/Annotation { // kotlinx.coroutines/ExperimentalCoroutinesApi|null[0]
+ constructor <init>() // kotlinx.coroutines/ExperimentalCoroutinesApi.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.coroutines/ExperimentalForInheritanceCoroutinesApi : kotlin/Annotation { // kotlinx.coroutines/ExperimentalForInheritanceCoroutinesApi|null[0]
+ constructor <init>() // kotlinx.coroutines/ExperimentalForInheritanceCoroutinesApi.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.coroutines/FlowPreview : kotlin/Annotation { // kotlinx.coroutines/FlowPreview|null[0]
+ constructor <init>() // kotlinx.coroutines/FlowPreview.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.coroutines/InternalCoroutinesApi : kotlin/Annotation { // kotlinx.coroutines/InternalCoroutinesApi|null[0]
+ constructor <init>() // kotlinx.coroutines/InternalCoroutinesApi.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.coroutines/InternalForInheritanceCoroutinesApi : kotlin/Annotation { // kotlinx.coroutines/InternalForInheritanceCoroutinesApi|null[0]
+ constructor <init>() // kotlinx.coroutines/InternalForInheritanceCoroutinesApi.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.coroutines/ObsoleteCoroutinesApi : kotlin/Annotation { // kotlinx.coroutines/ObsoleteCoroutinesApi|null[0]
+ constructor <init>() // kotlinx.coroutines/ObsoleteCoroutinesApi.<init>|<init>(){}[0]
+}
+
+final enum class kotlinx.coroutines.channels/BufferOverflow : kotlin/Enum<kotlinx.coroutines.channels/BufferOverflow> { // kotlinx.coroutines.channels/BufferOverflow|null[0]
+ enum entry DROP_LATEST // kotlinx.coroutines.channels/BufferOverflow.DROP_LATEST|null[0]
+ enum entry DROP_OLDEST // kotlinx.coroutines.channels/BufferOverflow.DROP_OLDEST|null[0]
+ enum entry SUSPEND // kotlinx.coroutines.channels/BufferOverflow.SUSPEND|null[0]
+
+ final val entries // kotlinx.coroutines.channels/BufferOverflow.entries|#static{}entries[0]
+ final fun <get-entries>(): kotlin.enums/EnumEntries<kotlinx.coroutines.channels/BufferOverflow> // kotlinx.coroutines.channels/BufferOverflow.entries.<get-entries>|<get-entries>#static(){}[0]
+
+ final fun valueOf(kotlin/String): kotlinx.coroutines.channels/BufferOverflow // kotlinx.coroutines.channels/BufferOverflow.valueOf|valueOf#static(kotlin.String){}[0]
+ final fun values(): kotlin/Array<kotlinx.coroutines.channels/BufferOverflow> // kotlinx.coroutines.channels/BufferOverflow.values|values#static(){}[0]
+}
+
+final enum class kotlinx.coroutines.flow/SharingCommand : kotlin/Enum<kotlinx.coroutines.flow/SharingCommand> { // kotlinx.coroutines.flow/SharingCommand|null[0]
+ enum entry START // kotlinx.coroutines.flow/SharingCommand.START|null[0]
+ enum entry STOP // kotlinx.coroutines.flow/SharingCommand.STOP|null[0]
+ enum entry STOP_AND_RESET_REPLAY_CACHE // kotlinx.coroutines.flow/SharingCommand.STOP_AND_RESET_REPLAY_CACHE|null[0]
+
+ final val entries // kotlinx.coroutines.flow/SharingCommand.entries|#static{}entries[0]
+ final fun <get-entries>(): kotlin.enums/EnumEntries<kotlinx.coroutines.flow/SharingCommand> // kotlinx.coroutines.flow/SharingCommand.entries.<get-entries>|<get-entries>#static(){}[0]
+
+ final fun valueOf(kotlin/String): kotlinx.coroutines.flow/SharingCommand // kotlinx.coroutines.flow/SharingCommand.valueOf|valueOf#static(kotlin.String){}[0]
+ final fun values(): kotlin/Array<kotlinx.coroutines.flow/SharingCommand> // kotlinx.coroutines.flow/SharingCommand.values|values#static(){}[0]
+}
+
+final enum class kotlinx.coroutines/CoroutineStart : kotlin/Enum<kotlinx.coroutines/CoroutineStart> { // kotlinx.coroutines/CoroutineStart|null[0]
+ enum entry ATOMIC // kotlinx.coroutines/CoroutineStart.ATOMIC|null[0]
+ enum entry DEFAULT // kotlinx.coroutines/CoroutineStart.DEFAULT|null[0]
+ enum entry LAZY // kotlinx.coroutines/CoroutineStart.LAZY|null[0]
+ enum entry UNDISPATCHED // kotlinx.coroutines/CoroutineStart.UNDISPATCHED|null[0]
+
+ final val entries // kotlinx.coroutines/CoroutineStart.entries|#static{}entries[0]
+ final fun <get-entries>(): kotlin.enums/EnumEntries<kotlinx.coroutines/CoroutineStart> // kotlinx.coroutines/CoroutineStart.entries.<get-entries>|<get-entries>#static(){}[0]
+ final val isLazy // kotlinx.coroutines/CoroutineStart.isLazy|{}isLazy[0]
+ final fun <get-isLazy>(): kotlin/Boolean // kotlinx.coroutines/CoroutineStart.isLazy.<get-isLazy>|<get-isLazy>(){}[0]
+
+ final fun <#A1: kotlin/Any?, #B1: kotlin/Any?> invoke(kotlin.coroutines/SuspendFunction1<#A1, #B1>, #A1, kotlin.coroutines/Continuation<#B1>) // kotlinx.coroutines/CoroutineStart.invoke|invoke(kotlin.coroutines.SuspendFunction1<0:0,0:1>;0:0;kotlin.coroutines.Continuation<0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+ final fun valueOf(kotlin/String): kotlinx.coroutines/CoroutineStart // kotlinx.coroutines/CoroutineStart.valueOf|valueOf#static(kotlin.String){}[0]
+ final fun values(): kotlin/Array<kotlinx.coroutines/CoroutineStart> // kotlinx.coroutines/CoroutineStart.values|values#static(){}[0]
+}
+
+abstract fun interface <#A: in kotlin/Any?> kotlinx.coroutines.flow/FlowCollector { // kotlinx.coroutines.flow/FlowCollector|null[0]
+ abstract suspend fun emit(#A) // kotlinx.coroutines.flow/FlowCollector.emit|emit(1:0){}[0]
+}
+
+abstract fun interface kotlinx.coroutines.flow/SharingStarted { // kotlinx.coroutines.flow/SharingStarted|null[0]
+ abstract fun command(kotlinx.coroutines.flow/StateFlow<kotlin/Int>): kotlinx.coroutines.flow/Flow<kotlinx.coroutines.flow/SharingCommand> // kotlinx.coroutines.flow/SharingStarted.command|command(kotlinx.coroutines.flow.StateFlow<kotlin.Int>){}[0]
+
+ final object Companion { // kotlinx.coroutines.flow/SharingStarted.Companion|null[0]
+ final val Eagerly // kotlinx.coroutines.flow/SharingStarted.Companion.Eagerly|{}Eagerly[0]
+ final fun <get-Eagerly>(): kotlinx.coroutines.flow/SharingStarted // kotlinx.coroutines.flow/SharingStarted.Companion.Eagerly.<get-Eagerly>|<get-Eagerly>(){}[0]
+ final val Lazily // kotlinx.coroutines.flow/SharingStarted.Companion.Lazily|{}Lazily[0]
+ final fun <get-Lazily>(): kotlinx.coroutines.flow/SharingStarted // kotlinx.coroutines.flow/SharingStarted.Companion.Lazily.<get-Lazily>|<get-Lazily>(){}[0]
+
+ final fun WhileSubscribed(kotlin/Long = ..., kotlin/Long = ...): kotlinx.coroutines.flow/SharingStarted // kotlinx.coroutines.flow/SharingStarted.Companion.WhileSubscribed|WhileSubscribed(kotlin.Long;kotlin.Long){}[0]
+ }
+}
+
+abstract fun interface kotlinx.coroutines/DisposableHandle { // kotlinx.coroutines/DisposableHandle|null[0]
+ abstract fun dispose() // kotlinx.coroutines/DisposableHandle.dispose|dispose(){}[0]
+}
+
+abstract interface <#A: in kotlin/Any?> kotlinx.coroutines.channels/ProducerScope : kotlinx.coroutines.channels/SendChannel<#A>, kotlinx.coroutines/CoroutineScope { // kotlinx.coroutines.channels/ProducerScope|null[0]
+ abstract val channel // kotlinx.coroutines.channels/ProducerScope.channel|{}channel[0]
+ abstract fun <get-channel>(): kotlinx.coroutines.channels/SendChannel<#A> // kotlinx.coroutines.channels/ProducerScope.channel.<get-channel>|<get-channel>(){}[0]
+}
+
+abstract interface <#A: in kotlin/Any?> kotlinx.coroutines.channels/SendChannel { // kotlinx.coroutines.channels/SendChannel|null[0]
+ abstract val isClosedForSend // kotlinx.coroutines.channels/SendChannel.isClosedForSend|{}isClosedForSend[0]
+ abstract fun <get-isClosedForSend>(): kotlin/Boolean // kotlinx.coroutines.channels/SendChannel.isClosedForSend.<get-isClosedForSend>|<get-isClosedForSend>(){}[0]
+ abstract val onSend // kotlinx.coroutines.channels/SendChannel.onSend|{}onSend[0]
+ abstract fun <get-onSend>(): kotlinx.coroutines.selects/SelectClause2<#A, kotlinx.coroutines.channels/SendChannel<#A>> // kotlinx.coroutines.channels/SendChannel.onSend.<get-onSend>|<get-onSend>(){}[0]
+
+ abstract fun close(kotlin/Throwable? = ...): kotlin/Boolean // kotlinx.coroutines.channels/SendChannel.close|close(kotlin.Throwable?){}[0]
+ abstract fun invokeOnClose(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>) // kotlinx.coroutines.channels/SendChannel.invokeOnClose|invokeOnClose(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ abstract fun trySend(#A): kotlinx.coroutines.channels/ChannelResult<kotlin/Unit> // kotlinx.coroutines.channels/SendChannel.trySend|trySend(1:0){}[0]
+ abstract suspend fun send(#A) // kotlinx.coroutines.channels/SendChannel.send|send(1:0){}[0]
+ open fun offer(#A): kotlin/Boolean // kotlinx.coroutines.channels/SendChannel.offer|offer(1:0){}[0]
+}
+
+abstract interface <#A: in kotlin/Any?> kotlinx.coroutines/CancellableContinuation : kotlin.coroutines/Continuation<#A> { // kotlinx.coroutines/CancellableContinuation|null[0]
+ abstract val isActive // kotlinx.coroutines/CancellableContinuation.isActive|{}isActive[0]
+ abstract fun <get-isActive>(): kotlin/Boolean // kotlinx.coroutines/CancellableContinuation.isActive.<get-isActive>|<get-isActive>(){}[0]
+ abstract val isCancelled // kotlinx.coroutines/CancellableContinuation.isCancelled|{}isCancelled[0]
+ abstract fun <get-isCancelled>(): kotlin/Boolean // kotlinx.coroutines/CancellableContinuation.isCancelled.<get-isCancelled>|<get-isCancelled>(){}[0]
+ abstract val isCompleted // kotlinx.coroutines/CancellableContinuation.isCompleted|{}isCompleted[0]
+ abstract fun <get-isCompleted>(): kotlin/Boolean // kotlinx.coroutines/CancellableContinuation.isCompleted.<get-isCompleted>|<get-isCompleted>(){}[0]
+
+ abstract fun (kotlinx.coroutines/CoroutineDispatcher).resumeUndispatched(#A) // kotlinx.coroutines/CancellableContinuation.resumeUndispatched|[email protected](1:0){}[0]
+ abstract fun (kotlinx.coroutines/CoroutineDispatcher).resumeUndispatchedWithException(kotlin/Throwable) // kotlinx.coroutines/CancellableContinuation.resumeUndispatchedWithException|resumeUndispatchedWithException@kotlinx.coroutines.CoroutineDispatcher(kotlin.Throwable){}[0]
+ abstract fun <#A1: #A> resume(#A1, kotlin/Function3<kotlin/Throwable, #A1, kotlin.coroutines/CoroutineContext, kotlin/Unit>?) // kotlinx.coroutines/CancellableContinuation.resume|resume(0:0;kotlin.Function3<kotlin.Throwable,0:0,kotlin.coroutines.CoroutineContext,kotlin.Unit>?){0§<1:0>}[0]
+ abstract fun <#A1: #A> tryResume(#A1, kotlin/Any?, kotlin/Function3<kotlin/Throwable, #A1, kotlin.coroutines/CoroutineContext, kotlin/Unit>?): kotlin/Any? // kotlinx.coroutines/CancellableContinuation.tryResume|tryResume(0:0;kotlin.Any?;kotlin.Function3<kotlin.Throwable,0:0,kotlin.coroutines.CoroutineContext,kotlin.Unit>?){0§<1:0>}[0]
+ abstract fun cancel(kotlin/Throwable? = ...): kotlin/Boolean // kotlinx.coroutines/CancellableContinuation.cancel|cancel(kotlin.Throwable?){}[0]
+ abstract fun completeResume(kotlin/Any) // kotlinx.coroutines/CancellableContinuation.completeResume|completeResume(kotlin.Any){}[0]
+ abstract fun initCancellability() // kotlinx.coroutines/CancellableContinuation.initCancellability|initCancellability(){}[0]
+ abstract fun invokeOnCancellation(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>) // kotlinx.coroutines/CancellableContinuation.invokeOnCancellation|invokeOnCancellation(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ abstract fun resume(#A, kotlin/Function1<kotlin/Throwable, kotlin/Unit>?) // kotlinx.coroutines/CancellableContinuation.resume|resume(1:0;kotlin.Function1<kotlin.Throwable,kotlin.Unit>?){}[0]
+ abstract fun tryResume(#A, kotlin/Any? = ...): kotlin/Any? // kotlinx.coroutines/CancellableContinuation.tryResume|tryResume(1:0;kotlin.Any?){}[0]
+ abstract fun tryResumeWithException(kotlin/Throwable): kotlin/Any? // kotlinx.coroutines/CancellableContinuation.tryResumeWithException|tryResumeWithException(kotlin.Throwable){}[0]
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.coroutines.channels/BroadcastChannel : kotlinx.coroutines.channels/SendChannel<#A> { // kotlinx.coroutines.channels/BroadcastChannel|null[0]
+ abstract fun cancel(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines.channels/BroadcastChannel.cancel|cancel(kotlin.coroutines.cancellation.CancellationException?){}[0]
+ abstract fun cancel(kotlin/Throwable? = ...): kotlin/Boolean // kotlinx.coroutines.channels/BroadcastChannel.cancel|cancel(kotlin.Throwable?){}[0]
+ abstract fun openSubscription(): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/BroadcastChannel.openSubscription|openSubscription(){}[0]
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.coroutines.channels/Channel : kotlinx.coroutines.channels/ReceiveChannel<#A>, kotlinx.coroutines.channels/SendChannel<#A> { // kotlinx.coroutines.channels/Channel|null[0]
+ final object Factory { // kotlinx.coroutines.channels/Channel.Factory|null[0]
+ final const val BUFFERED // kotlinx.coroutines.channels/Channel.Factory.BUFFERED|{}BUFFERED[0]
+ final fun <get-BUFFERED>(): kotlin/Int // kotlinx.coroutines.channels/Channel.Factory.BUFFERED.<get-BUFFERED>|<get-BUFFERED>(){}[0]
+ final const val CONFLATED // kotlinx.coroutines.channels/Channel.Factory.CONFLATED|{}CONFLATED[0]
+ final fun <get-CONFLATED>(): kotlin/Int // kotlinx.coroutines.channels/Channel.Factory.CONFLATED.<get-CONFLATED>|<get-CONFLATED>(){}[0]
+ final const val DEFAULT_BUFFER_PROPERTY_NAME // kotlinx.coroutines.channels/Channel.Factory.DEFAULT_BUFFER_PROPERTY_NAME|{}DEFAULT_BUFFER_PROPERTY_NAME[0]
+ final fun <get-DEFAULT_BUFFER_PROPERTY_NAME>(): kotlin/String // kotlinx.coroutines.channels/Channel.Factory.DEFAULT_BUFFER_PROPERTY_NAME.<get-DEFAULT_BUFFER_PROPERTY_NAME>|<get-DEFAULT_BUFFER_PROPERTY_NAME>(){}[0]
+ final const val RENDEZVOUS // kotlinx.coroutines.channels/Channel.Factory.RENDEZVOUS|{}RENDEZVOUS[0]
+ final fun <get-RENDEZVOUS>(): kotlin/Int // kotlinx.coroutines.channels/Channel.Factory.RENDEZVOUS.<get-RENDEZVOUS>|<get-RENDEZVOUS>(){}[0]
+ final const val UNLIMITED // kotlinx.coroutines.channels/Channel.Factory.UNLIMITED|{}UNLIMITED[0]
+ final fun <get-UNLIMITED>(): kotlin/Int // kotlinx.coroutines.channels/Channel.Factory.UNLIMITED.<get-UNLIMITED>|<get-UNLIMITED>(){}[0]
+ }
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.coroutines.flow.internal/FusibleFlow : kotlinx.coroutines.flow/Flow<#A> { // kotlinx.coroutines.flow.internal/FusibleFlow|null[0]
+ abstract fun fuse(kotlin.coroutines/CoroutineContext = ..., kotlin/Int = ..., kotlinx.coroutines.channels/BufferOverflow = ...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow.internal/FusibleFlow.fuse|fuse(kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.channels.BufferOverflow){}[0]
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.coroutines.flow/MutableSharedFlow : kotlinx.coroutines.flow/FlowCollector<#A>, kotlinx.coroutines.flow/SharedFlow<#A> { // kotlinx.coroutines.flow/MutableSharedFlow|null[0]
+ abstract val subscriptionCount // kotlinx.coroutines.flow/MutableSharedFlow.subscriptionCount|{}subscriptionCount[0]
+ abstract fun <get-subscriptionCount>(): kotlinx.coroutines.flow/StateFlow<kotlin/Int> // kotlinx.coroutines.flow/MutableSharedFlow.subscriptionCount.<get-subscriptionCount>|<get-subscriptionCount>(){}[0]
+
+ abstract fun resetReplayCache() // kotlinx.coroutines.flow/MutableSharedFlow.resetReplayCache|resetReplayCache(){}[0]
+ abstract fun tryEmit(#A): kotlin/Boolean // kotlinx.coroutines.flow/MutableSharedFlow.tryEmit|tryEmit(1:0){}[0]
+ abstract suspend fun emit(#A) // kotlinx.coroutines.flow/MutableSharedFlow.emit|emit(1:0){}[0]
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.coroutines.flow/MutableStateFlow : kotlinx.coroutines.flow/MutableSharedFlow<#A>, kotlinx.coroutines.flow/StateFlow<#A> { // kotlinx.coroutines.flow/MutableStateFlow|null[0]
+ abstract var value // kotlinx.coroutines.flow/MutableStateFlow.value|{}value[0]
+ abstract fun <get-value>(): #A // kotlinx.coroutines.flow/MutableStateFlow.value.<get-value>|<get-value>(){}[0]
+ abstract fun <set-value>(#A) // kotlinx.coroutines.flow/MutableStateFlow.value.<set-value>|<set-value>(1:0){}[0]
+
+ abstract fun compareAndSet(#A, #A): kotlin/Boolean // kotlinx.coroutines.flow/MutableStateFlow.compareAndSet|compareAndSet(1:0;1:0){}[0]
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.coroutines/CompletableDeferred : kotlinx.coroutines/Deferred<#A> { // kotlinx.coroutines/CompletableDeferred|null[0]
+ abstract fun complete(#A): kotlin/Boolean // kotlinx.coroutines/CompletableDeferred.complete|complete(1:0){}[0]
+ abstract fun completeExceptionally(kotlin/Throwable): kotlin/Boolean // kotlinx.coroutines/CompletableDeferred.completeExceptionally|completeExceptionally(kotlin.Throwable){}[0]
+}
+
+abstract interface <#A: kotlin/Throwable & kotlinx.coroutines/CopyableThrowable<#A>> kotlinx.coroutines/CopyableThrowable { // kotlinx.coroutines/CopyableThrowable|null[0]
+ abstract fun createCopy(): #A? // kotlinx.coroutines/CopyableThrowable.createCopy|createCopy(){}[0]
+}
+
+abstract interface <#A: out kotlin/Any?> kotlinx.coroutines.channels/ChannelIterator { // kotlinx.coroutines.channels/ChannelIterator|null[0]
+ abstract fun next(): #A // kotlinx.coroutines.channels/ChannelIterator.next|next(){}[0]
+ abstract suspend fun hasNext(): kotlin/Boolean // kotlinx.coroutines.channels/ChannelIterator.hasNext|hasNext(){}[0]
+ open suspend fun next0(): #A // kotlinx.coroutines.channels/ChannelIterator.next0|next0(){}[0]
+}
+
+abstract interface <#A: out kotlin/Any?> kotlinx.coroutines.channels/ReceiveChannel { // kotlinx.coroutines.channels/ReceiveChannel|null[0]
+ abstract val isClosedForReceive // kotlinx.coroutines.channels/ReceiveChannel.isClosedForReceive|{}isClosedForReceive[0]
+ abstract fun <get-isClosedForReceive>(): kotlin/Boolean // kotlinx.coroutines.channels/ReceiveChannel.isClosedForReceive.<get-isClosedForReceive>|<get-isClosedForReceive>(){}[0]
+ abstract val isEmpty // kotlinx.coroutines.channels/ReceiveChannel.isEmpty|{}isEmpty[0]
+ abstract fun <get-isEmpty>(): kotlin/Boolean // kotlinx.coroutines.channels/ReceiveChannel.isEmpty.<get-isEmpty>|<get-isEmpty>(){}[0]
+ abstract val onReceive // kotlinx.coroutines.channels/ReceiveChannel.onReceive|{}onReceive[0]
+ abstract fun <get-onReceive>(): kotlinx.coroutines.selects/SelectClause1<#A> // kotlinx.coroutines.channels/ReceiveChannel.onReceive.<get-onReceive>|<get-onReceive>(){}[0]
+ abstract val onReceiveCatching // kotlinx.coroutines.channels/ReceiveChannel.onReceiveCatching|{}onReceiveCatching[0]
+ abstract fun <get-onReceiveCatching>(): kotlinx.coroutines.selects/SelectClause1<kotlinx.coroutines.channels/ChannelResult<#A>> // kotlinx.coroutines.channels/ReceiveChannel.onReceiveCatching.<get-onReceiveCatching>|<get-onReceiveCatching>(){}[0]
+ open val onReceiveOrNull // kotlinx.coroutines.channels/ReceiveChannel.onReceiveOrNull|{}onReceiveOrNull[0]
+ open fun <get-onReceiveOrNull>(): kotlinx.coroutines.selects/SelectClause1<#A?> // kotlinx.coroutines.channels/ReceiveChannel.onReceiveOrNull.<get-onReceiveOrNull>|<get-onReceiveOrNull>(){}[0]
+
+ abstract fun cancel(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines.channels/ReceiveChannel.cancel|cancel(kotlin.coroutines.cancellation.CancellationException?){}[0]
+ abstract fun cancel(kotlin/Throwable? = ...): kotlin/Boolean // kotlinx.coroutines.channels/ReceiveChannel.cancel|cancel(kotlin.Throwable?){}[0]
+ abstract fun iterator(): kotlinx.coroutines.channels/ChannelIterator<#A> // kotlinx.coroutines.channels/ReceiveChannel.iterator|iterator(){}[0]
+ abstract fun tryReceive(): kotlinx.coroutines.channels/ChannelResult<#A> // kotlinx.coroutines.channels/ReceiveChannel.tryReceive|tryReceive(){}[0]
+ abstract suspend fun receive(): #A // kotlinx.coroutines.channels/ReceiveChannel.receive|receive(){}[0]
+ abstract suspend fun receiveCatching(): kotlinx.coroutines.channels/ChannelResult<#A> // kotlinx.coroutines.channels/ReceiveChannel.receiveCatching|receiveCatching(){}[0]
+ open fun cancel() // kotlinx.coroutines.channels/ReceiveChannel.cancel|cancel(){}[0]
+ open fun poll(): #A? // kotlinx.coroutines.channels/ReceiveChannel.poll|poll(){}[0]
+ open suspend fun receiveOrNull(): #A? // kotlinx.coroutines.channels/ReceiveChannel.receiveOrNull|receiveOrNull(){}[0]
+}
+
+abstract interface <#A: out kotlin/Any?> kotlinx.coroutines.flow/Flow { // kotlinx.coroutines.flow/Flow|null[0]
+ abstract suspend fun collect(kotlinx.coroutines.flow/FlowCollector<#A>) // kotlinx.coroutines.flow/Flow.collect|collect(kotlinx.coroutines.flow.FlowCollector<1:0>){}[0]
+}
+
+abstract interface <#A: out kotlin/Any?> kotlinx.coroutines.flow/SharedFlow : kotlinx.coroutines.flow/Flow<#A> { // kotlinx.coroutines.flow/SharedFlow|null[0]
+ abstract val replayCache // kotlinx.coroutines.flow/SharedFlow.replayCache|{}replayCache[0]
+ abstract fun <get-replayCache>(): kotlin.collections/List<#A> // kotlinx.coroutines.flow/SharedFlow.replayCache.<get-replayCache>|<get-replayCache>(){}[0]
+
+ abstract suspend fun collect(kotlinx.coroutines.flow/FlowCollector<#A>): kotlin/Nothing // kotlinx.coroutines.flow/SharedFlow.collect|collect(kotlinx.coroutines.flow.FlowCollector<1:0>){}[0]
+}
+
+abstract interface <#A: out kotlin/Any?> kotlinx.coroutines.flow/StateFlow : kotlinx.coroutines.flow/SharedFlow<#A> { // kotlinx.coroutines.flow/StateFlow|null[0]
+ abstract val value // kotlinx.coroutines.flow/StateFlow.value|{}value[0]
+ abstract fun <get-value>(): #A // kotlinx.coroutines.flow/StateFlow.value.<get-value>|<get-value>(){}[0]
+}
+
+abstract interface <#A: out kotlin/Any?> kotlinx.coroutines/Deferred : kotlinx.coroutines/Job { // kotlinx.coroutines/Deferred|null[0]
+ abstract val onAwait // kotlinx.coroutines/Deferred.onAwait|{}onAwait[0]
+ abstract fun <get-onAwait>(): kotlinx.coroutines.selects/SelectClause1<#A> // kotlinx.coroutines/Deferred.onAwait.<get-onAwait>|<get-onAwait>(){}[0]
+
+ abstract fun getCompleted(): #A // kotlinx.coroutines/Deferred.getCompleted|getCompleted(){}[0]
+ abstract fun getCompletionExceptionOrNull(): kotlin/Throwable? // kotlinx.coroutines/Deferred.getCompletionExceptionOrNull|getCompletionExceptionOrNull(){}[0]
+ abstract suspend fun await(): #A // kotlinx.coroutines/Deferred.await|await(){}[0]
+}
+
+abstract interface kotlinx.coroutines.sync/Mutex { // kotlinx.coroutines.sync/Mutex|null[0]
+ abstract val isLocked // kotlinx.coroutines.sync/Mutex.isLocked|{}isLocked[0]
+ abstract fun <get-isLocked>(): kotlin/Boolean // kotlinx.coroutines.sync/Mutex.isLocked.<get-isLocked>|<get-isLocked>(){}[0]
+ abstract val onLock // kotlinx.coroutines.sync/Mutex.onLock|{}onLock[0]
+ abstract fun <get-onLock>(): kotlinx.coroutines.selects/SelectClause2<kotlin/Any?, kotlinx.coroutines.sync/Mutex> // kotlinx.coroutines.sync/Mutex.onLock.<get-onLock>|<get-onLock>(){}[0]
+
+ abstract fun holdsLock(kotlin/Any): kotlin/Boolean // kotlinx.coroutines.sync/Mutex.holdsLock|holdsLock(kotlin.Any){}[0]
+ abstract fun tryLock(kotlin/Any? = ...): kotlin/Boolean // kotlinx.coroutines.sync/Mutex.tryLock|tryLock(kotlin.Any?){}[0]
+ abstract fun unlock(kotlin/Any? = ...) // kotlinx.coroutines.sync/Mutex.unlock|unlock(kotlin.Any?){}[0]
+ abstract suspend fun lock(kotlin/Any? = ...) // kotlinx.coroutines.sync/Mutex.lock|lock(kotlin.Any?){}[0]
+}
+
+abstract interface kotlinx.coroutines.sync/Semaphore { // kotlinx.coroutines.sync/Semaphore|null[0]
+ abstract val availablePermits // kotlinx.coroutines.sync/Semaphore.availablePermits|{}availablePermits[0]
+ abstract fun <get-availablePermits>(): kotlin/Int // kotlinx.coroutines.sync/Semaphore.availablePermits.<get-availablePermits>|<get-availablePermits>(){}[0]
+
+ abstract fun release() // kotlinx.coroutines.sync/Semaphore.release|release(){}[0]
+ abstract fun tryAcquire(): kotlin/Boolean // kotlinx.coroutines.sync/Semaphore.tryAcquire|tryAcquire(){}[0]
+ abstract suspend fun acquire() // kotlinx.coroutines.sync/Semaphore.acquire|acquire(){}[0]
+}
+
+abstract interface kotlinx.coroutines/ChildHandle : kotlinx.coroutines/DisposableHandle { // kotlinx.coroutines/ChildHandle|null[0]
+ abstract val parent // kotlinx.coroutines/ChildHandle.parent|{}parent[0]
+ abstract fun <get-parent>(): kotlinx.coroutines/Job? // kotlinx.coroutines/ChildHandle.parent.<get-parent>|<get-parent>(){}[0]
+
+ abstract fun childCancelled(kotlin/Throwable): kotlin/Boolean // kotlinx.coroutines/ChildHandle.childCancelled|childCancelled(kotlin.Throwable){}[0]
+}
+
+abstract interface kotlinx.coroutines/ChildJob : kotlinx.coroutines/Job { // kotlinx.coroutines/ChildJob|null[0]
+ abstract fun parentCancelled(kotlinx.coroutines/ParentJob) // kotlinx.coroutines/ChildJob.parentCancelled|parentCancelled(kotlinx.coroutines.ParentJob){}[0]
+}
+
+abstract interface kotlinx.coroutines/CompletableJob : kotlinx.coroutines/Job { // kotlinx.coroutines/CompletableJob|null[0]
+ abstract fun complete(): kotlin/Boolean // kotlinx.coroutines/CompletableJob.complete|complete(){}[0]
+ abstract fun completeExceptionally(kotlin/Throwable): kotlin/Boolean // kotlinx.coroutines/CompletableJob.completeExceptionally|completeExceptionally(kotlin.Throwable){}[0]
+}
+
+abstract interface kotlinx.coroutines/CoroutineExceptionHandler : kotlin.coroutines/CoroutineContext.Element { // kotlinx.coroutines/CoroutineExceptionHandler|null[0]
+ abstract fun handleException(kotlin.coroutines/CoroutineContext, kotlin/Throwable) // kotlinx.coroutines/CoroutineExceptionHandler.handleException|handleException(kotlin.coroutines.CoroutineContext;kotlin.Throwable){}[0]
+
+ final object Key : kotlin.coroutines/CoroutineContext.Key<kotlinx.coroutines/CoroutineExceptionHandler> // kotlinx.coroutines/CoroutineExceptionHandler.Key|null[0]
+}
+
+abstract interface kotlinx.coroutines/CoroutineScope { // kotlinx.coroutines/CoroutineScope|null[0]
+ abstract val coroutineContext // kotlinx.coroutines/CoroutineScope.coroutineContext|{}coroutineContext[0]
+ abstract fun <get-coroutineContext>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/CoroutineScope.coroutineContext.<get-coroutineContext>|<get-coroutineContext>(){}[0]
+}
+
+abstract interface kotlinx.coroutines/Delay { // kotlinx.coroutines/Delay|null[0]
+ abstract fun scheduleResumeAfterDelay(kotlin/Long, kotlinx.coroutines/CancellableContinuation<kotlin/Unit>) // kotlinx.coroutines/Delay.scheduleResumeAfterDelay|scheduleResumeAfterDelay(kotlin.Long;kotlinx.coroutines.CancellableContinuation<kotlin.Unit>){}[0]
+ open fun invokeOnTimeout(kotlin/Long, kotlinx.coroutines/Runnable, kotlin.coroutines/CoroutineContext): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines/Delay.invokeOnTimeout|invokeOnTimeout(kotlin.Long;kotlinx.coroutines.Runnable;kotlin.coroutines.CoroutineContext){}[0]
+ open suspend fun delay(kotlin/Long) // kotlinx.coroutines/Delay.delay|delay(kotlin.Long){}[0]
+}
+
+abstract interface kotlinx.coroutines/Job : kotlin.coroutines/CoroutineContext.Element { // kotlinx.coroutines/Job|null[0]
+ abstract val children // kotlinx.coroutines/Job.children|{}children[0]
+ abstract fun <get-children>(): kotlin.sequences/Sequence<kotlinx.coroutines/Job> // kotlinx.coroutines/Job.children.<get-children>|<get-children>(){}[0]
+ abstract val isActive // kotlinx.coroutines/Job.isActive|{}isActive[0]
+ abstract fun <get-isActive>(): kotlin/Boolean // kotlinx.coroutines/Job.isActive.<get-isActive>|<get-isActive>(){}[0]
+ abstract val isCancelled // kotlinx.coroutines/Job.isCancelled|{}isCancelled[0]
+ abstract fun <get-isCancelled>(): kotlin/Boolean // kotlinx.coroutines/Job.isCancelled.<get-isCancelled>|<get-isCancelled>(){}[0]
+ abstract val isCompleted // kotlinx.coroutines/Job.isCompleted|{}isCompleted[0]
+ abstract fun <get-isCompleted>(): kotlin/Boolean // kotlinx.coroutines/Job.isCompleted.<get-isCompleted>|<get-isCompleted>(){}[0]
+ abstract val onJoin // kotlinx.coroutines/Job.onJoin|{}onJoin[0]
+ abstract fun <get-onJoin>(): kotlinx.coroutines.selects/SelectClause0 // kotlinx.coroutines/Job.onJoin.<get-onJoin>|<get-onJoin>(){}[0]
+ abstract val parent // kotlinx.coroutines/Job.parent|{}parent[0]
+ abstract fun <get-parent>(): kotlinx.coroutines/Job? // kotlinx.coroutines/Job.parent.<get-parent>|<get-parent>(){}[0]
+
+ abstract fun attachChild(kotlinx.coroutines/ChildJob): kotlinx.coroutines/ChildHandle // kotlinx.coroutines/Job.attachChild|attachChild(kotlinx.coroutines.ChildJob){}[0]
+ abstract fun cancel(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines/Job.cancel|cancel(kotlin.coroutines.cancellation.CancellationException?){}[0]
+ abstract fun cancel(kotlin/Throwable? = ...): kotlin/Boolean // kotlinx.coroutines/Job.cancel|cancel(kotlin.Throwable?){}[0]
+ abstract fun getCancellationException(): kotlin.coroutines.cancellation/CancellationException // kotlinx.coroutines/Job.getCancellationException|getCancellationException(){}[0]
+ abstract fun invokeOnCompletion(kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines/Job.invokeOnCompletion|invokeOnCompletion(kotlin.Boolean;kotlin.Boolean;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ abstract fun invokeOnCompletion(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines/Job.invokeOnCompletion|invokeOnCompletion(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ abstract fun start(): kotlin/Boolean // kotlinx.coroutines/Job.start|start(){}[0]
+ abstract suspend fun join() // kotlinx.coroutines/Job.join|join(){}[0]
+ open fun cancel() // kotlinx.coroutines/Job.cancel|cancel(){}[0]
+ open fun plus(kotlinx.coroutines/Job): kotlinx.coroutines/Job // kotlinx.coroutines/Job.plus|plus(kotlinx.coroutines.Job){}[0]
+
+ final object Key : kotlin.coroutines/CoroutineContext.Key<kotlinx.coroutines/Job> // kotlinx.coroutines/Job.Key|null[0]
+}
+
+abstract interface kotlinx.coroutines/ParentJob : kotlinx.coroutines/Job { // kotlinx.coroutines/ParentJob|null[0]
+ abstract fun getChildJobCancellationCause(): kotlin.coroutines.cancellation/CancellationException // kotlinx.coroutines/ParentJob.getChildJobCancellationCause|getChildJobCancellationCause(){}[0]
+}
+
+abstract interface kotlinx.coroutines/Runnable { // kotlinx.coroutines/Runnable|null[0]
+ abstract fun run() // kotlinx.coroutines/Runnable.run|run(){}[0]
+}
+
+sealed interface <#A: in kotlin/Any?, #B: out kotlin/Any?> kotlinx.coroutines.selects/SelectClause2 : kotlinx.coroutines.selects/SelectClause // kotlinx.coroutines.selects/SelectClause2|null[0]
+
+sealed interface <#A: in kotlin/Any?> kotlinx.coroutines.selects/SelectBuilder { // kotlinx.coroutines.selects/SelectBuilder|null[0]
+ abstract fun (kotlinx.coroutines.selects/SelectClause0).invoke(kotlin.coroutines/SuspendFunction0<#A>) // kotlinx.coroutines.selects/SelectBuilder.invoke|[email protected](kotlin.coroutines.SuspendFunction0<1:0>){}[0]
+ abstract fun <#A1: kotlin/Any?, #B1: kotlin/Any?> (kotlinx.coroutines.selects/SelectClause2<#A1, #B1>).invoke(#A1, kotlin.coroutines/SuspendFunction1<#B1, #A>) // kotlinx.coroutines.selects/SelectBuilder.invoke|[email protected]<0:0,0:1>(0:0;kotlin.coroutines.SuspendFunction1<0:1,1:0>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+ abstract fun <#A1: kotlin/Any?> (kotlinx.coroutines.selects/SelectClause1<#A1>).invoke(kotlin.coroutines/SuspendFunction1<#A1, #A>) // kotlinx.coroutines.selects/SelectBuilder.invoke|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,1:0>){0§<kotlin.Any?>}[0]
+ open fun <#A1: kotlin/Any?, #B1: kotlin/Any?> (kotlinx.coroutines.selects/SelectClause2<#A1?, #B1>).invoke(kotlin.coroutines/SuspendFunction1<#B1, #A>) // kotlinx.coroutines.selects/SelectBuilder.invoke|[email protected]<0:0?,0:1>(kotlin.coroutines.SuspendFunction1<0:1,1:0>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+ open fun onTimeout(kotlin/Long, kotlin.coroutines/SuspendFunction0<#A>) // kotlinx.coroutines.selects/SelectBuilder.onTimeout|onTimeout(kotlin.Long;kotlin.coroutines.SuspendFunction0<1:0>){}[0]
+}
+
+sealed interface <#A: in kotlin/Any?> kotlinx.coroutines.selects/SelectInstance { // kotlinx.coroutines.selects/SelectInstance|null[0]
+ abstract val context // kotlinx.coroutines.selects/SelectInstance.context|{}context[0]
+ abstract fun <get-context>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines.selects/SelectInstance.context.<get-context>|<get-context>(){}[0]
+
+ abstract fun disposeOnCompletion(kotlinx.coroutines/DisposableHandle) // kotlinx.coroutines.selects/SelectInstance.disposeOnCompletion|disposeOnCompletion(kotlinx.coroutines.DisposableHandle){}[0]
+ abstract fun selectInRegistrationPhase(kotlin/Any?) // kotlinx.coroutines.selects/SelectInstance.selectInRegistrationPhase|selectInRegistrationPhase(kotlin.Any?){}[0]
+ abstract fun trySelect(kotlin/Any, kotlin/Any?): kotlin/Boolean // kotlinx.coroutines.selects/SelectInstance.trySelect|trySelect(kotlin.Any;kotlin.Any?){}[0]
+}
+
+sealed interface <#A: out kotlin/Any?> kotlinx.coroutines.selects/SelectClause1 : kotlinx.coroutines.selects/SelectClause // kotlinx.coroutines.selects/SelectClause1|null[0]
+
+sealed interface kotlinx.coroutines.selects/SelectClause { // kotlinx.coroutines.selects/SelectClause|null[0]
+ abstract val clauseObject // kotlinx.coroutines.selects/SelectClause.clauseObject|{}clauseObject[0]
+ abstract fun <get-clauseObject>(): kotlin/Any // kotlinx.coroutines.selects/SelectClause.clauseObject.<get-clauseObject>|<get-clauseObject>(){}[0]
+ abstract val onCancellationConstructor // kotlinx.coroutines.selects/SelectClause.onCancellationConstructor|{}onCancellationConstructor[0]
+ abstract fun <get-onCancellationConstructor>(): kotlin/Function3<kotlinx.coroutines.selects/SelectInstance<*>, kotlin/Any?, kotlin/Any?, kotlin/Function3<kotlin/Throwable, kotlin/Any?, kotlin.coroutines/CoroutineContext, kotlin/Unit>>? // kotlinx.coroutines.selects/SelectClause.onCancellationConstructor.<get-onCancellationConstructor>|<get-onCancellationConstructor>(){}[0]
+ abstract val processResFunc // kotlinx.coroutines.selects/SelectClause.processResFunc|{}processResFunc[0]
+ abstract fun <get-processResFunc>(): kotlin/Function3<kotlin/Any, kotlin/Any?, kotlin/Any?, kotlin/Any?> // kotlinx.coroutines.selects/SelectClause.processResFunc.<get-processResFunc>|<get-processResFunc>(){}[0]
+ abstract val regFunc // kotlinx.coroutines.selects/SelectClause.regFunc|{}regFunc[0]
+ abstract fun <get-regFunc>(): kotlin/Function3<kotlin/Any, kotlinx.coroutines.selects/SelectInstance<*>, kotlin/Any?, kotlin/Unit> // kotlinx.coroutines.selects/SelectClause.regFunc.<get-regFunc>|<get-regFunc>(){}[0]
+}
+
+sealed interface kotlinx.coroutines.selects/SelectClause0 : kotlinx.coroutines.selects/SelectClause // kotlinx.coroutines.selects/SelectClause0|null[0]
+
+abstract class <#A: in kotlin/Any?> kotlinx.coroutines/AbstractCoroutine : kotlin.coroutines/Continuation<#A>, kotlinx.coroutines/CoroutineScope, kotlinx.coroutines/Job, kotlinx.coroutines/JobSupport { // kotlinx.coroutines/AbstractCoroutine|null[0]
+ constructor <init>(kotlin.coroutines/CoroutineContext, kotlin/Boolean, kotlin/Boolean) // kotlinx.coroutines/AbstractCoroutine.<init>|<init>(kotlin.coroutines.CoroutineContext;kotlin.Boolean;kotlin.Boolean){}[0]
+
+ final val context // kotlinx.coroutines/AbstractCoroutine.context|{}context[0]
+ final fun <get-context>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/AbstractCoroutine.context.<get-context>|<get-context>(){}[0]
+ open val coroutineContext // kotlinx.coroutines/AbstractCoroutine.coroutineContext|{}coroutineContext[0]
+ open fun <get-coroutineContext>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/AbstractCoroutine.coroutineContext.<get-coroutineContext>|<get-coroutineContext>(){}[0]
+ open val isActive // kotlinx.coroutines/AbstractCoroutine.isActive|{}isActive[0]
+ open fun <get-isActive>(): kotlin/Boolean // kotlinx.coroutines/AbstractCoroutine.isActive.<get-isActive>|<get-isActive>(){}[0]
+
+ final fun <#A1: kotlin/Any?> start(kotlinx.coroutines/CoroutineStart, #A1, kotlin.coroutines/SuspendFunction1<#A1, #A>) // kotlinx.coroutines/AbstractCoroutine.start|start(kotlinx.coroutines.CoroutineStart;0:0;kotlin.coroutines.SuspendFunction1<0:0,1:0>){0§<kotlin.Any?>}[0]
+ final fun onCompletionInternal(kotlin/Any?) // kotlinx.coroutines/AbstractCoroutine.onCompletionInternal|onCompletionInternal(kotlin.Any?){}[0]
+ final fun resumeWith(kotlin/Result<#A>) // kotlinx.coroutines/AbstractCoroutine.resumeWith|resumeWith(kotlin.Result<1:0>){}[0]
+ open fun afterResume(kotlin/Any?) // kotlinx.coroutines/AbstractCoroutine.afterResume|afterResume(kotlin.Any?){}[0]
+ open fun cancellationExceptionMessage(): kotlin/String // kotlinx.coroutines/AbstractCoroutine.cancellationExceptionMessage|cancellationExceptionMessage(){}[0]
+ open fun onCancelled(kotlin/Throwable, kotlin/Boolean) // kotlinx.coroutines/AbstractCoroutine.onCancelled|onCancelled(kotlin.Throwable;kotlin.Boolean){}[0]
+ open fun onCompleted(#A) // kotlinx.coroutines/AbstractCoroutine.onCompleted|onCompleted(1:0){}[0]
+}
+
+abstract class <#A: kotlin/Any?> kotlinx.coroutines.flow.internal/ChannelFlow : kotlinx.coroutines.flow.internal/FusibleFlow<#A> { // kotlinx.coroutines.flow.internal/ChannelFlow|null[0]
+ constructor <init>(kotlin.coroutines/CoroutineContext, kotlin/Int, kotlinx.coroutines.channels/BufferOverflow) // kotlinx.coroutines.flow.internal/ChannelFlow.<init>|<init>(kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.channels.BufferOverflow){}[0]
+
+ final val capacity // kotlinx.coroutines.flow.internal/ChannelFlow.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // kotlinx.coroutines.flow.internal/ChannelFlow.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val context // kotlinx.coroutines.flow.internal/ChannelFlow.context|{}context[0]
+ final fun <get-context>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines.flow.internal/ChannelFlow.context.<get-context>|<get-context>(){}[0]
+ final val onBufferOverflow // kotlinx.coroutines.flow.internal/ChannelFlow.onBufferOverflow|{}onBufferOverflow[0]
+ final fun <get-onBufferOverflow>(): kotlinx.coroutines.channels/BufferOverflow // kotlinx.coroutines.flow.internal/ChannelFlow.onBufferOverflow.<get-onBufferOverflow>|<get-onBufferOverflow>(){}[0]
+
+ abstract fun create(kotlin.coroutines/CoroutineContext, kotlin/Int, kotlinx.coroutines.channels/BufferOverflow): kotlinx.coroutines.flow.internal/ChannelFlow<#A> // kotlinx.coroutines.flow.internal/ChannelFlow.create|create(kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.channels.BufferOverflow){}[0]
+ abstract suspend fun collectTo(kotlinx.coroutines.channels/ProducerScope<#A>) // kotlinx.coroutines.flow.internal/ChannelFlow.collectTo|collectTo(kotlinx.coroutines.channels.ProducerScope<1:0>){}[0]
+ open fun additionalToStringProps(): kotlin/String? // kotlinx.coroutines.flow.internal/ChannelFlow.additionalToStringProps|additionalToStringProps(){}[0]
+ open fun dropChannelOperators(): kotlinx.coroutines.flow/Flow<#A>? // kotlinx.coroutines.flow.internal/ChannelFlow.dropChannelOperators|dropChannelOperators(){}[0]
+ open fun fuse(kotlin.coroutines/CoroutineContext, kotlin/Int, kotlinx.coroutines.channels/BufferOverflow): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow.internal/ChannelFlow.fuse|fuse(kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.channels.BufferOverflow){}[0]
+ open fun produceImpl(kotlinx.coroutines/CoroutineScope): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.flow.internal/ChannelFlow.produceImpl|produceImpl(kotlinx.coroutines.CoroutineScope){}[0]
+ open fun toString(): kotlin/String // kotlinx.coroutines.flow.internal/ChannelFlow.toString|toString(){}[0]
+ open suspend fun collect(kotlinx.coroutines.flow/FlowCollector<#A>) // kotlinx.coroutines.flow.internal/ChannelFlow.collect|collect(kotlinx.coroutines.flow.FlowCollector<1:0>){}[0]
+}
+
+abstract class <#A: kotlin/Any?> kotlinx.coroutines.flow/AbstractFlow : kotlinx.coroutines.flow/CancellableFlow<#A>, kotlinx.coroutines.flow/Flow<#A> { // kotlinx.coroutines.flow/AbstractFlow|null[0]
+ constructor <init>() // kotlinx.coroutines.flow/AbstractFlow.<init>|<init>(){}[0]
+
+ abstract suspend fun collectSafely(kotlinx.coroutines.flow/FlowCollector<#A>) // kotlinx.coroutines.flow/AbstractFlow.collectSafely|collectSafely(kotlinx.coroutines.flow.FlowCollector<1:0>){}[0]
+ final suspend fun collect(kotlinx.coroutines.flow/FlowCollector<#A>) // kotlinx.coroutines.flow/AbstractFlow.collect|collect(kotlinx.coroutines.flow.FlowCollector<1:0>){}[0]
+}
+
+abstract class kotlinx.coroutines/CloseableCoroutineDispatcher : kotlin/AutoCloseable, kotlinx.coroutines/CoroutineDispatcher { // kotlinx.coroutines/CloseableCoroutineDispatcher|null[0]
+ constructor <init>() // kotlinx.coroutines/CloseableCoroutineDispatcher.<init>|<init>(){}[0]
+
+ abstract fun close() // kotlinx.coroutines/CloseableCoroutineDispatcher.close|close(){}[0]
+}
+
+abstract class kotlinx.coroutines/CoroutineDispatcher : kotlin.coroutines/AbstractCoroutineContextElement, kotlin.coroutines/ContinuationInterceptor { // kotlinx.coroutines/CoroutineDispatcher|null[0]
+ constructor <init>() // kotlinx.coroutines/CoroutineDispatcher.<init>|<init>(){}[0]
+
+ abstract fun dispatch(kotlin.coroutines/CoroutineContext, kotlinx.coroutines/Runnable) // kotlinx.coroutines/CoroutineDispatcher.dispatch|dispatch(kotlin.coroutines.CoroutineContext;kotlinx.coroutines.Runnable){}[0]
+ final fun <#A1: kotlin/Any?> interceptContinuation(kotlin.coroutines/Continuation<#A1>): kotlin.coroutines/Continuation<#A1> // kotlinx.coroutines/CoroutineDispatcher.interceptContinuation|interceptContinuation(kotlin.coroutines.Continuation<0:0>){0§<kotlin.Any?>}[0]
+ final fun plus(kotlinx.coroutines/CoroutineDispatcher): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/CoroutineDispatcher.plus|plus(kotlinx.coroutines.CoroutineDispatcher){}[0]
+ final fun releaseInterceptedContinuation(kotlin.coroutines/Continuation<*>) // kotlinx.coroutines/CoroutineDispatcher.releaseInterceptedContinuation|releaseInterceptedContinuation(kotlin.coroutines.Continuation<*>){}[0]
+ open fun dispatchYield(kotlin.coroutines/CoroutineContext, kotlinx.coroutines/Runnable) // kotlinx.coroutines/CoroutineDispatcher.dispatchYield|dispatchYield(kotlin.coroutines.CoroutineContext;kotlinx.coroutines.Runnable){}[0]
+ open fun isDispatchNeeded(kotlin.coroutines/CoroutineContext): kotlin/Boolean // kotlinx.coroutines/CoroutineDispatcher.isDispatchNeeded|isDispatchNeeded(kotlin.coroutines.CoroutineContext){}[0]
+ open fun limitedParallelism(kotlin/Int): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/CoroutineDispatcher.limitedParallelism|limitedParallelism(kotlin.Int){}[0]
+ open fun limitedParallelism(kotlin/Int, kotlin/String? = ...): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/CoroutineDispatcher.limitedParallelism|limitedParallelism(kotlin.Int;kotlin.String?){}[0]
+ open fun toString(): kotlin/String // kotlinx.coroutines/CoroutineDispatcher.toString|toString(){}[0]
+
+ final object Key : kotlin.coroutines/AbstractCoroutineContextKey<kotlin.coroutines/ContinuationInterceptor, kotlinx.coroutines/CoroutineDispatcher> // kotlinx.coroutines/CoroutineDispatcher.Key|null[0]
+}
+
+abstract class kotlinx.coroutines/MainCoroutineDispatcher : kotlinx.coroutines/CoroutineDispatcher { // kotlinx.coroutines/MainCoroutineDispatcher|null[0]
+ constructor <init>() // kotlinx.coroutines/MainCoroutineDispatcher.<init>|<init>(){}[0]
+
+ abstract val immediate // kotlinx.coroutines/MainCoroutineDispatcher.immediate|{}immediate[0]
+ abstract fun <get-immediate>(): kotlinx.coroutines/MainCoroutineDispatcher // kotlinx.coroutines/MainCoroutineDispatcher.immediate.<get-immediate>|<get-immediate>(){}[0]
+
+ final fun toStringInternalImpl(): kotlin/String? // kotlinx.coroutines/MainCoroutineDispatcher.toStringInternalImpl|toStringInternalImpl(){}[0]
+ open fun limitedParallelism(kotlin/Int, kotlin/String?): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/MainCoroutineDispatcher.limitedParallelism|limitedParallelism(kotlin.Int;kotlin.String?){}[0]
+ open fun toString(): kotlin/String // kotlinx.coroutines/MainCoroutineDispatcher.toString|toString(){}[0]
+}
+
+final class <#A: kotlin/Any?> kotlinx.coroutines.channels/ConflatedBroadcastChannel : kotlinx.coroutines.channels/BroadcastChannel<#A> { // kotlinx.coroutines.channels/ConflatedBroadcastChannel|null[0]
+ constructor <init>(#A) // kotlinx.coroutines.channels/ConflatedBroadcastChannel.<init>|<init>(1:0){}[0]
+ constructor <init>() // kotlinx.coroutines.channels/ConflatedBroadcastChannel.<init>|<init>(){}[0]
+
+ final val isClosedForSend // kotlinx.coroutines.channels/ConflatedBroadcastChannel.isClosedForSend|{}isClosedForSend[0]
+ final fun <get-isClosedForSend>(): kotlin/Boolean // kotlinx.coroutines.channels/ConflatedBroadcastChannel.isClosedForSend.<get-isClosedForSend>|<get-isClosedForSend>(){}[0]
+ final val onSend // kotlinx.coroutines.channels/ConflatedBroadcastChannel.onSend|{}onSend[0]
+ final fun <get-onSend>(): kotlinx.coroutines.selects/SelectClause2<#A, kotlinx.coroutines.channels/SendChannel<#A>> // kotlinx.coroutines.channels/ConflatedBroadcastChannel.onSend.<get-onSend>|<get-onSend>(){}[0]
+ final val value // kotlinx.coroutines.channels/ConflatedBroadcastChannel.value|{}value[0]
+ final fun <get-value>(): #A // kotlinx.coroutines.channels/ConflatedBroadcastChannel.value.<get-value>|<get-value>(){}[0]
+ final val valueOrNull // kotlinx.coroutines.channels/ConflatedBroadcastChannel.valueOrNull|{}valueOrNull[0]
+ final fun <get-valueOrNull>(): #A? // kotlinx.coroutines.channels/ConflatedBroadcastChannel.valueOrNull.<get-valueOrNull>|<get-valueOrNull>(){}[0]
+
+ final fun cancel(kotlin.coroutines.cancellation/CancellationException?) // kotlinx.coroutines.channels/ConflatedBroadcastChannel.cancel|cancel(kotlin.coroutines.cancellation.CancellationException?){}[0]
+ final fun cancel(kotlin/Throwable?): kotlin/Boolean // kotlinx.coroutines.channels/ConflatedBroadcastChannel.cancel|cancel(kotlin.Throwable?){}[0]
+ final fun close(kotlin/Throwable?): kotlin/Boolean // kotlinx.coroutines.channels/ConflatedBroadcastChannel.close|close(kotlin.Throwable?){}[0]
+ final fun invokeOnClose(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>) // kotlinx.coroutines.channels/ConflatedBroadcastChannel.invokeOnClose|invokeOnClose(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ final fun offer(#A): kotlin/Boolean // kotlinx.coroutines.channels/ConflatedBroadcastChannel.offer|offer(1:0){}[0]
+ final fun openSubscription(): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/ConflatedBroadcastChannel.openSubscription|openSubscription(){}[0]
+ final fun trySend(#A): kotlinx.coroutines.channels/ChannelResult<kotlin/Unit> // kotlinx.coroutines.channels/ConflatedBroadcastChannel.trySend|trySend(1:0){}[0]
+ final suspend fun send(#A) // kotlinx.coroutines.channels/ConflatedBroadcastChannel.send|send(1:0){}[0]
+}
+
+final class <#A: kotlin/Any?> kotlinx.coroutines.flow.internal/SendingCollector : kotlinx.coroutines.flow/FlowCollector<#A> { // kotlinx.coroutines.flow.internal/SendingCollector|null[0]
+ constructor <init>(kotlinx.coroutines.channels/SendChannel<#A>) // kotlinx.coroutines.flow.internal/SendingCollector.<init>|<init>(kotlinx.coroutines.channels.SendChannel<1:0>){}[0]
+
+ final suspend fun emit(#A) // kotlinx.coroutines.flow.internal/SendingCollector.emit|emit(1:0){}[0]
+}
+
+final class <#A: kotlin/Any?> kotlinx.coroutines.selects/SelectBuilderImpl : kotlinx.coroutines.selects/SelectImplementation<#A> { // kotlinx.coroutines.selects/SelectBuilderImpl|null[0]
+ constructor <init>(kotlin.coroutines/Continuation<#A>) // kotlinx.coroutines.selects/SelectBuilderImpl.<init>|<init>(kotlin.coroutines.Continuation<1:0>){}[0]
+
+ final fun getResult(): kotlin/Any? // kotlinx.coroutines.selects/SelectBuilderImpl.getResult|getResult(){}[0]
+ final fun handleBuilderException(kotlin/Throwable) // kotlinx.coroutines.selects/SelectBuilderImpl.handleBuilderException|handleBuilderException(kotlin.Throwable){}[0]
+}
+
+final class <#A: kotlin/Any?> kotlinx.coroutines.selects/UnbiasedSelectBuilderImpl : kotlinx.coroutines.selects/UnbiasedSelectImplementation<#A> { // kotlinx.coroutines.selects/UnbiasedSelectBuilderImpl|null[0]
+ constructor <init>(kotlin.coroutines/Continuation<#A>) // kotlinx.coroutines.selects/UnbiasedSelectBuilderImpl.<init>|<init>(kotlin.coroutines.Continuation<1:0>){}[0]
+
+ final fun handleBuilderException(kotlin/Throwable) // kotlinx.coroutines.selects/UnbiasedSelectBuilderImpl.handleBuilderException|handleBuilderException(kotlin.Throwable){}[0]
+ final fun initSelectResult(): kotlin/Any? // kotlinx.coroutines.selects/UnbiasedSelectBuilderImpl.initSelectResult|initSelectResult(){}[0]
+}
+
+final class kotlinx.coroutines.channels/ClosedReceiveChannelException : kotlin/NoSuchElementException { // kotlinx.coroutines.channels/ClosedReceiveChannelException|null[0]
+ constructor <init>(kotlin/String?) // kotlinx.coroutines.channels/ClosedReceiveChannelException.<init>|<init>(kotlin.String?){}[0]
+}
+
+final class kotlinx.coroutines.channels/ClosedSendChannelException : kotlin/IllegalStateException { // kotlinx.coroutines.channels/ClosedSendChannelException|null[0]
+ constructor <init>(kotlin/String?) // kotlinx.coroutines.channels/ClosedSendChannelException.<init>|<init>(kotlin.String?){}[0]
+}
+
+final class kotlinx.coroutines/CompletionHandlerException : kotlin/RuntimeException { // kotlinx.coroutines/CompletionHandlerException|null[0]
+ constructor <init>(kotlin/String, kotlin/Throwable) // kotlinx.coroutines/CompletionHandlerException.<init>|<init>(kotlin.String;kotlin.Throwable){}[0]
+}
+
+final class kotlinx.coroutines/CoroutineName : kotlin.coroutines/AbstractCoroutineContextElement { // kotlinx.coroutines/CoroutineName|null[0]
+ constructor <init>(kotlin/String) // kotlinx.coroutines/CoroutineName.<init>|<init>(kotlin.String){}[0]
+
+ final val name // kotlinx.coroutines/CoroutineName.name|{}name[0]
+ final fun <get-name>(): kotlin/String // kotlinx.coroutines/CoroutineName.name.<get-name>|<get-name>(){}[0]
+
+ final fun component1(): kotlin/String // kotlinx.coroutines/CoroutineName.component1|component1(){}[0]
+ final fun copy(kotlin/String = ...): kotlinx.coroutines/CoroutineName // kotlinx.coroutines/CoroutineName.copy|copy(kotlin.String){}[0]
+ final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.coroutines/CoroutineName.equals|equals(kotlin.Any?){}[0]
+ final fun hashCode(): kotlin/Int // kotlinx.coroutines/CoroutineName.hashCode|hashCode(){}[0]
+ final fun toString(): kotlin/String // kotlinx.coroutines/CoroutineName.toString|toString(){}[0]
+
+ final object Key : kotlin.coroutines/CoroutineContext.Key<kotlinx.coroutines/CoroutineName> // kotlinx.coroutines/CoroutineName.Key|null[0]
+}
+
+final class kotlinx.coroutines/TimeoutCancellationException : kotlin.coroutines.cancellation/CancellationException, kotlinx.coroutines/CopyableThrowable<kotlinx.coroutines/TimeoutCancellationException> { // kotlinx.coroutines/TimeoutCancellationException|null[0]
+ final fun createCopy(): kotlinx.coroutines/TimeoutCancellationException // kotlinx.coroutines/TimeoutCancellationException.createCopy|createCopy(){}[0]
+}
+
+final class kotlinx.coroutines/YieldContext : kotlin.coroutines/AbstractCoroutineContextElement { // kotlinx.coroutines/YieldContext|null[0]
+ constructor <init>() // kotlinx.coroutines/YieldContext.<init>|<init>(){}[0]
+
+ final var dispatcherWasUnconfined // kotlinx.coroutines/YieldContext.dispatcherWasUnconfined|{}dispatcherWasUnconfined[0]
+ final fun <get-dispatcherWasUnconfined>(): kotlin/Boolean // kotlinx.coroutines/YieldContext.dispatcherWasUnconfined.<get-dispatcherWasUnconfined>|<get-dispatcherWasUnconfined>(){}[0]
+ final fun <set-dispatcherWasUnconfined>(kotlin/Boolean) // kotlinx.coroutines/YieldContext.dispatcherWasUnconfined.<set-dispatcherWasUnconfined>|<set-dispatcherWasUnconfined>(kotlin.Boolean){}[0]
+
+ final object Key : kotlin.coroutines/CoroutineContext.Key<kotlinx.coroutines/YieldContext> // kotlinx.coroutines/YieldContext.Key|null[0]
+}
+
+final value class <#A: out kotlin/Any?> kotlinx.coroutines.channels/ChannelResult { // kotlinx.coroutines.channels/ChannelResult|null[0]
+ constructor <init>(kotlin/Any?) // kotlinx.coroutines.channels/ChannelResult.<init>|<init>(kotlin.Any?){}[0]
+
+ final val holder // kotlinx.coroutines.channels/ChannelResult.holder|{}holder[0]
+ final fun <get-holder>(): kotlin/Any? // kotlinx.coroutines.channels/ChannelResult.holder.<get-holder>|<get-holder>(){}[0]
+ final val isClosed // kotlinx.coroutines.channels/ChannelResult.isClosed|{}isClosed[0]
+ final fun <get-isClosed>(): kotlin/Boolean // kotlinx.coroutines.channels/ChannelResult.isClosed.<get-isClosed>|<get-isClosed>(){}[0]
+ final val isFailure // kotlinx.coroutines.channels/ChannelResult.isFailure|{}isFailure[0]
+ final fun <get-isFailure>(): kotlin/Boolean // kotlinx.coroutines.channels/ChannelResult.isFailure.<get-isFailure>|<get-isFailure>(){}[0]
+ final val isSuccess // kotlinx.coroutines.channels/ChannelResult.isSuccess|{}isSuccess[0]
+ final fun <get-isSuccess>(): kotlin/Boolean // kotlinx.coroutines.channels/ChannelResult.isSuccess.<get-isSuccess>|<get-isSuccess>(){}[0]
+
+ final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.coroutines.channels/ChannelResult.equals|equals(kotlin.Any?){}[0]
+ final fun exceptionOrNull(): kotlin/Throwable? // kotlinx.coroutines.channels/ChannelResult.exceptionOrNull|exceptionOrNull(){}[0]
+ final fun getOrNull(): #A? // kotlinx.coroutines.channels/ChannelResult.getOrNull|getOrNull(){}[0]
+ final fun getOrThrow(): #A // kotlinx.coroutines.channels/ChannelResult.getOrThrow|getOrThrow(){}[0]
+ final fun hashCode(): kotlin/Int // kotlinx.coroutines.channels/ChannelResult.hashCode|hashCode(){}[0]
+ final fun toString(): kotlin/String // kotlinx.coroutines.channels/ChannelResult.toString|toString(){}[0]
+
+ final object Companion { // kotlinx.coroutines.channels/ChannelResult.Companion|null[0]
+ final fun <#A2: kotlin/Any?> closed(kotlin/Throwable?): kotlinx.coroutines.channels/ChannelResult<#A2> // kotlinx.coroutines.channels/ChannelResult.Companion.closed|closed(kotlin.Throwable?){0§<kotlin.Any?>}[0]
+ final fun <#A2: kotlin/Any?> failure(): kotlinx.coroutines.channels/ChannelResult<#A2> // kotlinx.coroutines.channels/ChannelResult.Companion.failure|failure(){0§<kotlin.Any?>}[0]
+ final fun <#A2: kotlin/Any?> success(#A2): kotlinx.coroutines.channels/ChannelResult<#A2> // kotlinx.coroutines.channels/ChannelResult.Companion.success|success(0:0){0§<kotlin.Any?>}[0]
+ }
+}
+
+open class <#A: in kotlin/Any?> kotlinx.coroutines/CancellableContinuationImpl : kotlinx.coroutines.internal/CoroutineStackFrame, kotlinx.coroutines/CancellableContinuation<#A>, kotlinx.coroutines/DispatchedTask<#A>, kotlinx.coroutines/Waiter { // kotlinx.coroutines/CancellableContinuationImpl|null[0]
+ constructor <init>(kotlin.coroutines/Continuation<#A>, kotlin/Int) // kotlinx.coroutines/CancellableContinuationImpl.<init>|<init>(kotlin.coroutines.Continuation<1:0>;kotlin.Int){}[0]
+
+ open val callerFrame // kotlinx.coroutines/CancellableContinuationImpl.callerFrame|{}callerFrame[0]
+ open fun <get-callerFrame>(): kotlinx.coroutines.internal/CoroutineStackFrame? // kotlinx.coroutines/CancellableContinuationImpl.callerFrame.<get-callerFrame>|<get-callerFrame>(){}[0]
+ open val context // kotlinx.coroutines/CancellableContinuationImpl.context|{}context[0]
+ open fun <get-context>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/CancellableContinuationImpl.context.<get-context>|<get-context>(){}[0]
+ open val isActive // kotlinx.coroutines/CancellableContinuationImpl.isActive|{}isActive[0]
+ open fun <get-isActive>(): kotlin/Boolean // kotlinx.coroutines/CancellableContinuationImpl.isActive.<get-isActive>|<get-isActive>(){}[0]
+ open val isCancelled // kotlinx.coroutines/CancellableContinuationImpl.isCancelled|{}isCancelled[0]
+ open fun <get-isCancelled>(): kotlin/Boolean // kotlinx.coroutines/CancellableContinuationImpl.isCancelled.<get-isCancelled>|<get-isCancelled>(){}[0]
+ open val isCompleted // kotlinx.coroutines/CancellableContinuationImpl.isCompleted|{}isCompleted[0]
+ open fun <get-isCompleted>(): kotlin/Boolean // kotlinx.coroutines/CancellableContinuationImpl.isCompleted.<get-isCompleted>|<get-isCompleted>(){}[0]
+
+ final fun <#A1: kotlin/Any?> callOnCancellation(kotlin/Function3<kotlin/Throwable, #A1, kotlin.coroutines/CoroutineContext, kotlin/Unit>, kotlin/Throwable, #A1) // kotlinx.coroutines/CancellableContinuationImpl.callOnCancellation|callOnCancellation(kotlin.Function3<kotlin.Throwable,0:0,kotlin.coroutines.CoroutineContext,kotlin.Unit>;kotlin.Throwable;0:0){0§<kotlin.Any?>}[0]
+ final fun callCancelHandler(kotlinx.coroutines/CancelHandler, kotlin/Throwable?) // kotlinx.coroutines/CancellableContinuationImpl.callCancelHandler|callCancelHandler(kotlinx.coroutines.CancelHandler;kotlin.Throwable?){}[0]
+ final fun getResult(): kotlin/Any? // kotlinx.coroutines/CancellableContinuationImpl.getResult|getResult(){}[0]
+ open fun (kotlinx.coroutines/CoroutineDispatcher).resumeUndispatched(#A) // kotlinx.coroutines/CancellableContinuationImpl.resumeUndispatched|[email protected](1:0){}[0]
+ open fun (kotlinx.coroutines/CoroutineDispatcher).resumeUndispatchedWithException(kotlin/Throwable) // kotlinx.coroutines/CancellableContinuationImpl.resumeUndispatchedWithException|resumeUndispatchedWithException@kotlinx.coroutines.CoroutineDispatcher(kotlin.Throwable){}[0]
+ open fun <#A1: #A> resume(#A1, kotlin/Function3<kotlin/Throwable, #A1, kotlin.coroutines/CoroutineContext, kotlin/Unit>?) // kotlinx.coroutines/CancellableContinuationImpl.resume|resume(0:0;kotlin.Function3<kotlin.Throwable,0:0,kotlin.coroutines.CoroutineContext,kotlin.Unit>?){0§<1:0>}[0]
+ open fun <#A1: #A> tryResume(#A1, kotlin/Any?, kotlin/Function3<kotlin/Throwable, #A1, kotlin.coroutines/CoroutineContext, kotlin/Unit>?): kotlin/Any? // kotlinx.coroutines/CancellableContinuationImpl.tryResume|tryResume(0:0;kotlin.Any?;kotlin.Function3<kotlin.Throwable,0:0,kotlin.coroutines.CoroutineContext,kotlin.Unit>?){0§<1:0>}[0]
+ open fun cancel(kotlin/Throwable?): kotlin/Boolean // kotlinx.coroutines/CancellableContinuationImpl.cancel|cancel(kotlin.Throwable?){}[0]
+ open fun completeResume(kotlin/Any) // kotlinx.coroutines/CancellableContinuationImpl.completeResume|completeResume(kotlin.Any){}[0]
+ open fun getContinuationCancellationCause(kotlinx.coroutines/Job): kotlin/Throwable // kotlinx.coroutines/CancellableContinuationImpl.getContinuationCancellationCause|getContinuationCancellationCause(kotlinx.coroutines.Job){}[0]
+ open fun getStackTraceElement(): kotlin/Any? // kotlinx.coroutines/CancellableContinuationImpl.getStackTraceElement|getStackTraceElement(){}[0]
+ open fun initCancellability() // kotlinx.coroutines/CancellableContinuationImpl.initCancellability|initCancellability(){}[0]
+ open fun invokeOnCancellation(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>) // kotlinx.coroutines/CancellableContinuationImpl.invokeOnCancellation|invokeOnCancellation(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ open fun invokeOnCancellation(kotlinx.coroutines.internal/Segment<*>, kotlin/Int) // kotlinx.coroutines/CancellableContinuationImpl.invokeOnCancellation|invokeOnCancellation(kotlinx.coroutines.internal.Segment<*>;kotlin.Int){}[0]
+ open fun nameString(): kotlin/String // kotlinx.coroutines/CancellableContinuationImpl.nameString|nameString(){}[0]
+ open fun resume(#A, kotlin/Function1<kotlin/Throwable, kotlin/Unit>?) // kotlinx.coroutines/CancellableContinuationImpl.resume|resume(1:0;kotlin.Function1<kotlin.Throwable,kotlin.Unit>?){}[0]
+ open fun resumeWith(kotlin/Result<#A>) // kotlinx.coroutines/CancellableContinuationImpl.resumeWith|resumeWith(kotlin.Result<1:0>){}[0]
+ open fun toString(): kotlin/String // kotlinx.coroutines/CancellableContinuationImpl.toString|toString(){}[0]
+ open fun tryResume(#A, kotlin/Any?): kotlin/Any? // kotlinx.coroutines/CancellableContinuationImpl.tryResume|tryResume(1:0;kotlin.Any?){}[0]
+ open fun tryResumeWithException(kotlin/Throwable): kotlin/Any? // kotlinx.coroutines/CancellableContinuationImpl.tryResumeWithException|tryResumeWithException(kotlin.Throwable){}[0]
+}
+
+open class <#A: kotlin/Any?> kotlinx.coroutines.selects/SelectImplementation : kotlinx.coroutines.selects/SelectBuilder<#A>, kotlinx.coroutines.selects/SelectInstanceInternal<#A>, kotlinx.coroutines/CancelHandler { // kotlinx.coroutines.selects/SelectImplementation|null[0]
+ constructor <init>(kotlin.coroutines/CoroutineContext) // kotlinx.coroutines.selects/SelectImplementation.<init>|<init>(kotlin.coroutines.CoroutineContext){}[0]
+
+ open val context // kotlinx.coroutines.selects/SelectImplementation.context|{}context[0]
+ open fun <get-context>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines.selects/SelectImplementation.context.<get-context>|<get-context>(){}[0]
+
+ final fun trySelectDetailed(kotlin/Any, kotlin/Any?): kotlinx.coroutines.selects/TrySelectDetailedResult // kotlinx.coroutines.selects/SelectImplementation.trySelectDetailed|trySelectDetailed(kotlin.Any;kotlin.Any?){}[0]
+ open fun (kotlinx.coroutines.selects/SelectClause0).invoke(kotlin.coroutines/SuspendFunction0<#A>) // kotlinx.coroutines.selects/SelectImplementation.invoke|[email protected](kotlin.coroutines.SuspendFunction0<1:0>){}[0]
+ open fun <#A1: kotlin/Any?, #B1: kotlin/Any?> (kotlinx.coroutines.selects/SelectClause2<#A1, #B1>).invoke(#A1, kotlin.coroutines/SuspendFunction1<#B1, #A>) // kotlinx.coroutines.selects/SelectImplementation.invoke|[email protected]<0:0,0:1>(0:0;kotlin.coroutines.SuspendFunction1<0:1,1:0>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+ open fun <#A1: kotlin/Any?> (kotlinx.coroutines.selects/SelectClause1<#A1>).invoke(kotlin.coroutines/SuspendFunction1<#A1, #A>) // kotlinx.coroutines.selects/SelectImplementation.invoke|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,1:0>){0§<kotlin.Any?>}[0]
+ open fun disposeOnCompletion(kotlinx.coroutines/DisposableHandle) // kotlinx.coroutines.selects/SelectImplementation.disposeOnCompletion|disposeOnCompletion(kotlinx.coroutines.DisposableHandle){}[0]
+ open fun invoke(kotlin/Throwable?) // kotlinx.coroutines.selects/SelectImplementation.invoke|invoke(kotlin.Throwable?){}[0]
+ open fun invokeOnCancellation(kotlinx.coroutines.internal/Segment<*>, kotlin/Int) // kotlinx.coroutines.selects/SelectImplementation.invokeOnCancellation|invokeOnCancellation(kotlinx.coroutines.internal.Segment<*>;kotlin.Int){}[0]
+ open fun selectInRegistrationPhase(kotlin/Any?) // kotlinx.coroutines.selects/SelectImplementation.selectInRegistrationPhase|selectInRegistrationPhase(kotlin.Any?){}[0]
+ open fun trySelect(kotlin/Any, kotlin/Any?): kotlin/Boolean // kotlinx.coroutines.selects/SelectImplementation.trySelect|trySelect(kotlin.Any;kotlin.Any?){}[0]
+ open suspend fun doSelect(): #A // kotlinx.coroutines.selects/SelectImplementation.doSelect|doSelect(){}[0]
+}
+
+open class <#A: kotlin/Any?> kotlinx.coroutines.selects/UnbiasedSelectImplementation : kotlinx.coroutines.selects/SelectImplementation<#A> { // kotlinx.coroutines.selects/UnbiasedSelectImplementation|null[0]
+ constructor <init>(kotlin.coroutines/CoroutineContext) // kotlinx.coroutines.selects/UnbiasedSelectImplementation.<init>|<init>(kotlin.coroutines.CoroutineContext){}[0]
+
+ open fun (kotlinx.coroutines.selects/SelectClause0).invoke(kotlin.coroutines/SuspendFunction0<#A>) // kotlinx.coroutines.selects/UnbiasedSelectImplementation.invoke|[email protected](kotlin.coroutines.SuspendFunction0<1:0>){}[0]
+ open fun <#A1: kotlin/Any?, #B1: kotlin/Any?> (kotlinx.coroutines.selects/SelectClause2<#A1, #B1>).invoke(#A1, kotlin.coroutines/SuspendFunction1<#B1, #A>) // kotlinx.coroutines.selects/UnbiasedSelectImplementation.invoke|[email protected]<0:0,0:1>(0:0;kotlin.coroutines.SuspendFunction1<0:1,1:0>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+ open fun <#A1: kotlin/Any?> (kotlinx.coroutines.selects/SelectClause1<#A1>).invoke(kotlin.coroutines/SuspendFunction1<#A1, #A>) // kotlinx.coroutines.selects/UnbiasedSelectImplementation.invoke|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,1:0>){0§<kotlin.Any?>}[0]
+ open suspend fun doSelect(): #A // kotlinx.coroutines.selects/UnbiasedSelectImplementation.doSelect|doSelect(){}[0]
+}
+
+open class kotlinx.coroutines/JobImpl : kotlinx.coroutines/CompletableJob, kotlinx.coroutines/JobSupport { // kotlinx.coroutines/JobImpl|null[0]
+ constructor <init>(kotlinx.coroutines/Job?) // kotlinx.coroutines/JobImpl.<init>|<init>(kotlinx.coroutines.Job?){}[0]
+
+ open fun complete(): kotlin/Boolean // kotlinx.coroutines/JobImpl.complete|complete(){}[0]
+ open fun completeExceptionally(kotlin/Throwable): kotlin/Boolean // kotlinx.coroutines/JobImpl.completeExceptionally|completeExceptionally(kotlin.Throwable){}[0]
+}
+
+open class kotlinx.coroutines/JobSupport : kotlinx.coroutines/ChildJob, kotlinx.coroutines/Job, kotlinx.coroutines/ParentJob { // kotlinx.coroutines/JobSupport|null[0]
+ constructor <init>(kotlin/Boolean) // kotlinx.coroutines/JobSupport.<init>|<init>(kotlin.Boolean){}[0]
+
+ final val children // kotlinx.coroutines/JobSupport.children|{}children[0]
+ final fun <get-children>(): kotlin.sequences/Sequence<kotlinx.coroutines/Job> // kotlinx.coroutines/JobSupport.children.<get-children>|<get-children>(){}[0]
+ final val completionCause // kotlinx.coroutines/JobSupport.completionCause|{}completionCause[0]
+ final fun <get-completionCause>(): kotlin/Throwable? // kotlinx.coroutines/JobSupport.completionCause.<get-completionCause>|<get-completionCause>(){}[0]
+ final val completionCauseHandled // kotlinx.coroutines/JobSupport.completionCauseHandled|{}completionCauseHandled[0]
+ final fun <get-completionCauseHandled>(): kotlin/Boolean // kotlinx.coroutines/JobSupport.completionCauseHandled.<get-completionCauseHandled>|<get-completionCauseHandled>(){}[0]
+ final val isCancelled // kotlinx.coroutines/JobSupport.isCancelled|{}isCancelled[0]
+ final fun <get-isCancelled>(): kotlin/Boolean // kotlinx.coroutines/JobSupport.isCancelled.<get-isCancelled>|<get-isCancelled>(){}[0]
+ final val isCompleted // kotlinx.coroutines/JobSupport.isCompleted|{}isCompleted[0]
+ final fun <get-isCompleted>(): kotlin/Boolean // kotlinx.coroutines/JobSupport.isCompleted.<get-isCompleted>|<get-isCompleted>(){}[0]
+ final val isCompletedExceptionally // kotlinx.coroutines/JobSupport.isCompletedExceptionally|{}isCompletedExceptionally[0]
+ final fun <get-isCompletedExceptionally>(): kotlin/Boolean // kotlinx.coroutines/JobSupport.isCompletedExceptionally.<get-isCompletedExceptionally>|<get-isCompletedExceptionally>(){}[0]
+ final val key // kotlinx.coroutines/JobSupport.key|{}key[0]
+ final fun <get-key>(): kotlin.coroutines/CoroutineContext.Key<*> // kotlinx.coroutines/JobSupport.key.<get-key>|<get-key>(){}[0]
+ final val onAwaitInternal // kotlinx.coroutines/JobSupport.onAwaitInternal|{}onAwaitInternal[0]
+ final fun <get-onAwaitInternal>(): kotlinx.coroutines.selects/SelectClause1<*> // kotlinx.coroutines/JobSupport.onAwaitInternal.<get-onAwaitInternal>|<get-onAwaitInternal>(){}[0]
+ final val onJoin // kotlinx.coroutines/JobSupport.onJoin|{}onJoin[0]
+ final fun <get-onJoin>(): kotlinx.coroutines.selects/SelectClause0 // kotlinx.coroutines/JobSupport.onJoin.<get-onJoin>|<get-onJoin>(){}[0]
+ open val isActive // kotlinx.coroutines/JobSupport.isActive|{}isActive[0]
+ open fun <get-isActive>(): kotlin/Boolean // kotlinx.coroutines/JobSupport.isActive.<get-isActive>|<get-isActive>(){}[0]
+ open val isScopedCoroutine // kotlinx.coroutines/JobSupport.isScopedCoroutine|{}isScopedCoroutine[0]
+ open fun <get-isScopedCoroutine>(): kotlin/Boolean // kotlinx.coroutines/JobSupport.isScopedCoroutine.<get-isScopedCoroutine>|<get-isScopedCoroutine>(){}[0]
+ open val parent // kotlinx.coroutines/JobSupport.parent|{}parent[0]
+ open fun <get-parent>(): kotlinx.coroutines/Job? // kotlinx.coroutines/JobSupport.parent.<get-parent>|<get-parent>(){}[0]
+
+ final fun (kotlin/Throwable).toCancellationException(kotlin/String? = ...): kotlin.coroutines.cancellation/CancellationException // kotlinx.coroutines/JobSupport.toCancellationException|[email protected](kotlin.String?){}[0]
+ final fun attachChild(kotlinx.coroutines/ChildJob): kotlinx.coroutines/ChildHandle // kotlinx.coroutines/JobSupport.attachChild|attachChild(kotlinx.coroutines.ChildJob){}[0]
+ final fun cancelCoroutine(kotlin/Throwable?): kotlin/Boolean // kotlinx.coroutines/JobSupport.cancelCoroutine|cancelCoroutine(kotlin.Throwable?){}[0]
+ final fun getCancellationException(): kotlin.coroutines.cancellation/CancellationException // kotlinx.coroutines/JobSupport.getCancellationException|getCancellationException(){}[0]
+ final fun getCompletionExceptionOrNull(): kotlin/Throwable? // kotlinx.coroutines/JobSupport.getCompletionExceptionOrNull|getCompletionExceptionOrNull(){}[0]
+ final fun initParentJob(kotlinx.coroutines/Job?) // kotlinx.coroutines/JobSupport.initParentJob|initParentJob(kotlinx.coroutines.Job?){}[0]
+ final fun invokeOnCompletion(kotlin/Boolean, kotlin/Boolean, kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines/JobSupport.invokeOnCompletion|invokeOnCompletion(kotlin.Boolean;kotlin.Boolean;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ final fun invokeOnCompletion(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines/JobSupport.invokeOnCompletion|invokeOnCompletion(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ final fun parentCancelled(kotlinx.coroutines/ParentJob) // kotlinx.coroutines/JobSupport.parentCancelled|parentCancelled(kotlinx.coroutines.ParentJob){}[0]
+ final fun start(): kotlin/Boolean // kotlinx.coroutines/JobSupport.start|start(){}[0]
+ final fun toDebugString(): kotlin/String // kotlinx.coroutines/JobSupport.toDebugString|toDebugString(){}[0]
+ final suspend fun awaitInternal(): kotlin/Any? // kotlinx.coroutines/JobSupport.awaitInternal|awaitInternal(){}[0]
+ final suspend fun join() // kotlinx.coroutines/JobSupport.join|join(){}[0]
+ open fun afterCompletion(kotlin/Any?) // kotlinx.coroutines/JobSupport.afterCompletion|afterCompletion(kotlin.Any?){}[0]
+ open fun cancel(kotlin.coroutines.cancellation/CancellationException?) // kotlinx.coroutines/JobSupport.cancel|cancel(kotlin.coroutines.cancellation.CancellationException?){}[0]
+ open fun cancel(kotlin/Throwable?): kotlin/Boolean // kotlinx.coroutines/JobSupport.cancel|cancel(kotlin.Throwable?){}[0]
+ open fun cancelInternal(kotlin/Throwable) // kotlinx.coroutines/JobSupport.cancelInternal|cancelInternal(kotlin.Throwable){}[0]
+ open fun cancellationExceptionMessage(): kotlin/String // kotlinx.coroutines/JobSupport.cancellationExceptionMessage|cancellationExceptionMessage(){}[0]
+ open fun childCancelled(kotlin/Throwable): kotlin/Boolean // kotlinx.coroutines/JobSupport.childCancelled|childCancelled(kotlin.Throwable){}[0]
+ open fun getChildJobCancellationCause(): kotlin.coroutines.cancellation/CancellationException // kotlinx.coroutines/JobSupport.getChildJobCancellationCause|getChildJobCancellationCause(){}[0]
+ open fun handleJobException(kotlin/Throwable): kotlin/Boolean // kotlinx.coroutines/JobSupport.handleJobException|handleJobException(kotlin.Throwable){}[0]
+ open fun onCancelling(kotlin/Throwable?) // kotlinx.coroutines/JobSupport.onCancelling|onCancelling(kotlin.Throwable?){}[0]
+ open fun onCompletionInternal(kotlin/Any?) // kotlinx.coroutines/JobSupport.onCompletionInternal|onCompletionInternal(kotlin.Any?){}[0]
+ open fun onStart() // kotlinx.coroutines/JobSupport.onStart|onStart(){}[0]
+ open fun toString(): kotlin/String // kotlinx.coroutines/JobSupport.toString|toString(){}[0]
+}
+
+final object kotlinx.coroutines/Dispatchers { // kotlinx.coroutines/Dispatchers|null[0]
+ final val Default // kotlinx.coroutines/Dispatchers.Default|{}Default[0]
+ final fun <get-Default>(): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/Dispatchers.Default.<get-Default>|<get-Default>(){}[0]
+ final val Main // kotlinx.coroutines/Dispatchers.Main|{}Main[0]
+ final fun <get-Main>(): kotlinx.coroutines/MainCoroutineDispatcher // kotlinx.coroutines/Dispatchers.Main.<get-Main>|<get-Main>(){}[0]
+ final val Unconfined // kotlinx.coroutines/Dispatchers.Unconfined|{}Unconfined[0]
+ final fun <get-Unconfined>(): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/Dispatchers.Unconfined.<get-Unconfined>|<get-Unconfined>(){}[0]
+
+ final fun injectMain(kotlinx.coroutines/MainCoroutineDispatcher) // kotlinx.coroutines/Dispatchers.injectMain|injectMain(kotlinx.coroutines.MainCoroutineDispatcher){}[0]
+}
+
+final object kotlinx.coroutines/GlobalScope : kotlinx.coroutines/CoroutineScope { // kotlinx.coroutines/GlobalScope|null[0]
+ final val coroutineContext // kotlinx.coroutines/GlobalScope.coroutineContext|{}coroutineContext[0]
+ final fun <get-coroutineContext>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/GlobalScope.coroutineContext.<get-coroutineContext>|<get-coroutineContext>(){}[0]
+}
+
+final object kotlinx.coroutines/NonCancellable : kotlin.coroutines/AbstractCoroutineContextElement, kotlinx.coroutines/Job { // kotlinx.coroutines/NonCancellable|null[0]
+ final val children // kotlinx.coroutines/NonCancellable.children|{}children[0]
+ final fun <get-children>(): kotlin.sequences/Sequence<kotlinx.coroutines/Job> // kotlinx.coroutines/NonCancellable.children.<get-children>|<get-children>(){}[0]
+ final val isActive // kotlinx.coroutines/NonCancellable.isActive|{}isActive[0]
+ final fun <get-isActive>(): kotlin/Boolean // kotlinx.coroutines/NonCancellable.isActive.<get-isActive>|<get-isActive>(){}[0]
+ final val isCancelled // kotlinx.coroutines/NonCancellable.isCancelled|{}isCancelled[0]
+ final fun <get-isCancelled>(): kotlin/Boolean // kotlinx.coroutines/NonCancellable.isCancelled.<get-isCancelled>|<get-isCancelled>(){}[0]
+ final val isCompleted // kotlinx.coroutines/NonCancellable.isCompleted|{}isCompleted[0]
+ final fun <get-isCompleted>(): kotlin/Boolean // kotlinx.coroutines/NonCancellable.isCompleted.<get-isCompleted>|<get-isCompleted>(){}[0]
+ final val onJoin // kotlinx.coroutines/NonCancellable.onJoin|{}onJoin[0]
+ final fun <get-onJoin>(): kotlinx.coroutines.selects/SelectClause0 // kotlinx.coroutines/NonCancellable.onJoin.<get-onJoin>|<get-onJoin>(){}[0]
+ final val parent // kotlinx.coroutines/NonCancellable.parent|{}parent[0]
+ final fun <get-parent>(): kotlinx.coroutines/Job? // kotlinx.coroutines/NonCancellable.parent.<get-parent>|<get-parent>(){}[0]
+
+ final fun attachChild(kotlinx.coroutines/ChildJob): kotlinx.coroutines/ChildHandle // kotlinx.coroutines/NonCancellable.attachChild|attachChild(kotlinx.coroutines.ChildJob){}[0]
+ final fun cancel(kotlin.coroutines.cancellation/CancellationException?) // kotlinx.coroutines/NonCancellable.cancel|cancel(kotlin.coroutines.cancellation.CancellationException?){}[0]
+ final fun cancel(kotlin/Throwable?): kotlin/Boolean // kotlinx.coroutines/NonCancellable.cancel|cancel(kotlin.Throwable?){}[0]
+ final fun getCancellationException(): kotlin.coroutines.cancellation/CancellationException // kotlinx.coroutines/NonCancellable.getCancellationException|getCancellationException(){}[0]
+ final fun invokeOnCompletion(kotlin/Boolean, kotlin/Boolean, kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines/NonCancellable.invokeOnCompletion|invokeOnCompletion(kotlin.Boolean;kotlin.Boolean;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ final fun invokeOnCompletion(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines/NonCancellable.invokeOnCompletion|invokeOnCompletion(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}[0]
+ final fun start(): kotlin/Boolean // kotlinx.coroutines/NonCancellable.start|start(){}[0]
+ final fun toString(): kotlin/String // kotlinx.coroutines/NonCancellable.toString|toString(){}[0]
+ final suspend fun join() // kotlinx.coroutines/NonCancellable.join|join(){}[0]
+}
+
+final object kotlinx.coroutines/NonDisposableHandle : kotlinx.coroutines/ChildHandle, kotlinx.coroutines/DisposableHandle { // kotlinx.coroutines/NonDisposableHandle|null[0]
+ final val parent // kotlinx.coroutines/NonDisposableHandle.parent|{}parent[0]
+ final fun <get-parent>(): kotlinx.coroutines/Job? // kotlinx.coroutines/NonDisposableHandle.parent.<get-parent>|<get-parent>(){}[0]
+
+ final fun childCancelled(kotlin/Throwable): kotlin/Boolean // kotlinx.coroutines/NonDisposableHandle.childCancelled|childCancelled(kotlin.Throwable){}[0]
+ final fun dispose() // kotlinx.coroutines/NonDisposableHandle.dispose|dispose(){}[0]
+ final fun toString(): kotlin/String // kotlinx.coroutines/NonDisposableHandle.toString|toString(){}[0]
+}
+
+final const val kotlinx.coroutines.flow/DEFAULT_CONCURRENCY_PROPERTY_NAME // kotlinx.coroutines.flow/DEFAULT_CONCURRENCY_PROPERTY_NAME|{}DEFAULT_CONCURRENCY_PROPERTY_NAME[0]
+ final fun <get-DEFAULT_CONCURRENCY_PROPERTY_NAME>(): kotlin/String // kotlinx.coroutines.flow/DEFAULT_CONCURRENCY_PROPERTY_NAME.<get-DEFAULT_CONCURRENCY_PROPERTY_NAME>|<get-DEFAULT_CONCURRENCY_PROPERTY_NAME>(){}[0]
+final const val kotlinx.coroutines/MODE_CANCELLABLE // kotlinx.coroutines/MODE_CANCELLABLE|{}MODE_CANCELLABLE[0]
+ final fun <get-MODE_CANCELLABLE>(): kotlin/Int // kotlinx.coroutines/MODE_CANCELLABLE.<get-MODE_CANCELLABLE>|<get-MODE_CANCELLABLE>(){}[0]
+
+final val kotlinx.coroutines.flow/DEFAULT_CONCURRENCY // kotlinx.coroutines.flow/DEFAULT_CONCURRENCY|{}DEFAULT_CONCURRENCY[0]
+ final fun <get-DEFAULT_CONCURRENCY>(): kotlin/Int // kotlinx.coroutines.flow/DEFAULT_CONCURRENCY.<get-DEFAULT_CONCURRENCY>|<get-DEFAULT_CONCURRENCY>(){}[0]
+final val kotlinx.coroutines.flow/coroutineContext // kotlinx.coroutines.flow/coroutineContext|@kotlinx.coroutines.flow.FlowCollector<*>{}coroutineContext[0]
+ final fun (kotlinx.coroutines.flow/FlowCollector<*>).<get-coroutineContext>(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines.flow/coroutineContext.<get-coroutineContext>|<get-coroutineContext>@kotlinx.coroutines.flow.FlowCollector<*>(){}[0]
+final val kotlinx.coroutines.flow/isActive // kotlinx.coroutines.flow/isActive|@kotlinx.coroutines.flow.FlowCollector<*>{}isActive[0]
+ final fun (kotlinx.coroutines.flow/FlowCollector<*>).<get-isActive>(): kotlin/Boolean // kotlinx.coroutines.flow/isActive.<get-isActive>|<get-isActive>@kotlinx.coroutines.flow.FlowCollector<*>(){}[0]
+final val kotlinx.coroutines/DefaultDelay // kotlinx.coroutines/DefaultDelay|{}DefaultDelay[0]
+ final fun <get-DefaultDelay>(): kotlinx.coroutines/Delay // kotlinx.coroutines/DefaultDelay.<get-DefaultDelay>|<get-DefaultDelay>(){}[0]
+final val kotlinx.coroutines/isActive // kotlinx.coroutines/isActive|@kotlin.coroutines.CoroutineContext{}isActive[0]
+ final fun (kotlin.coroutines/CoroutineContext).<get-isActive>(): kotlin/Boolean // kotlinx.coroutines/isActive.<get-isActive>|<get-isActive>@kotlin.coroutines.CoroutineContext(){}[0]
+final val kotlinx.coroutines/isActive // kotlinx.coroutines/isActive|@kotlinx.coroutines.CoroutineScope{}isActive[0]
+ final fun (kotlinx.coroutines/CoroutineScope).<get-isActive>(): kotlin/Boolean // kotlinx.coroutines/isActive.<get-isActive>|<get-isActive>@kotlinx.coroutines.CoroutineScope(){}[0]
+final val kotlinx.coroutines/job // kotlinx.coroutines/job|@kotlin.coroutines.CoroutineContext{}job[0]
+ final fun (kotlin.coroutines/CoroutineContext).<get-job>(): kotlinx.coroutines/Job // kotlinx.coroutines/job.<get-job>|<get-job>@kotlin.coroutines.CoroutineContext(){}[0]
+
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/cancel() // kotlinx.coroutines/cancel|[email protected](){}[0]
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/cancel(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines/cancel|[email protected](kotlin.coroutines.cancellation.CancellationException?){}[0]
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/cancel(kotlin/Throwable? = ...): kotlin/Boolean // kotlinx.coroutines/cancel|[email protected](kotlin.Throwable?){}[0]
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/cancelChildren() // kotlinx.coroutines/cancelChildren|[email protected](){}[0]
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/cancelChildren(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines/cancelChildren|[email protected](kotlin.coroutines.cancellation.CancellationException?){}[0]
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/cancelChildren(kotlin/Throwable? = ...) // kotlinx.coroutines/cancelChildren|[email protected](kotlin.Throwable?){}[0]
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/ensureActive() // kotlinx.coroutines/ensureActive|[email protected](){}[0]
+final fun (kotlin.coroutines/CoroutineContext).kotlinx.coroutines/newCoroutineContext(kotlin.coroutines/CoroutineContext): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/newCoroutineContext|[email protected](kotlin.coroutines.CoroutineContext){}[0]
+final fun (kotlin.ranges/IntRange).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<kotlin/Int> // kotlinx.coroutines.flow/asFlow|[email protected](){}[0]
+final fun (kotlin.ranges/LongRange).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<kotlin/Long> // kotlinx.coroutines.flow/asFlow|[email protected](){}[0]
+final fun (kotlin/IntArray).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<kotlin/Int> // kotlinx.coroutines.flow/asFlow|[email protected](){}[0]
+final fun (kotlin/LongArray).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<kotlin/Long> // kotlinx.coroutines.flow/asFlow|[email protected](){}[0]
+final fun (kotlinx.coroutines.channels/ReceiveChannel<*>).kotlinx.coroutines.channels/cancelConsumed(kotlin/Throwable?) // kotlinx.coroutines.channels/cancelConsumed|[email protected]<*>(kotlin.Throwable?){}[0]
+final fun (kotlinx.coroutines.channels/ReceiveChannel<*>).kotlinx.coroutines.channels/consumes(): kotlin/Function1<kotlin/Throwable?, kotlin/Unit> // kotlinx.coroutines.channels/consumes|[email protected]<*>(){}[0]
+final fun (kotlinx.coroutines.flow/FlowCollector<*>).kotlinx.coroutines.flow/cancel(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines.flow/cancel|[email protected]<*>(kotlin.coroutines.cancellation.CancellationException?){}[0]
+final fun (kotlinx.coroutines.flow/SharingStarted.Companion).kotlinx.coroutines.flow/WhileSubscribed(kotlin.time/Duration = ..., kotlin.time/Duration = ...): kotlinx.coroutines.flow/SharingStarted // kotlinx.coroutines.flow/WhileSubscribed|[email protected](kotlin.time.Duration;kotlin.time.Duration){}[0]
+final fun (kotlinx.coroutines/CancellableContinuation<*>).kotlinx.coroutines/disposeOnCancellation(kotlinx.coroutines/DisposableHandle) // kotlinx.coroutines/disposeOnCancellation|[email protected]<*>(kotlinx.coroutines.DisposableHandle){}[0]
+final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/cancel(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines/cancel|[email protected](kotlin.coroutines.cancellation.CancellationException?){}[0]
+final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/cancel(kotlin/String, kotlin/Throwable? = ...) // kotlinx.coroutines/cancel|[email protected](kotlin.String;kotlin.Throwable?){}[0]
+final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/ensureActive() // kotlinx.coroutines/ensureActive|[email protected](){}[0]
+final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/launch(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, kotlin/Unit>): kotlinx.coroutines/Job // kotlinx.coroutines/launch|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,kotlin.Unit>){}[0]
+final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/newCoroutineContext(kotlin.coroutines/CoroutineContext): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/newCoroutineContext|[email protected](kotlin.coroutines.CoroutineContext){}[0]
+final fun (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/plus(kotlin.coroutines/CoroutineContext): kotlinx.coroutines/CoroutineScope // kotlinx.coroutines/plus|[email protected](kotlin.coroutines.CoroutineContext){}[0]
+final fun (kotlinx.coroutines/Job).kotlinx.coroutines/cancel(kotlin/String, kotlin/Throwable? = ...) // kotlinx.coroutines/cancel|[email protected](kotlin.String;kotlin.Throwable?){}[0]
+final fun (kotlinx.coroutines/Job).kotlinx.coroutines/cancelChildren() // kotlinx.coroutines/cancelChildren|[email protected](){}[0]
+final fun (kotlinx.coroutines/Job).kotlinx.coroutines/cancelChildren(kotlin.coroutines.cancellation/CancellationException? = ...) // kotlinx.coroutines/cancelChildren|[email protected](kotlin.coroutines.cancellation.CancellationException?){}[0]
+final fun (kotlinx.coroutines/Job).kotlinx.coroutines/cancelChildren(kotlin/Throwable? = ...) // kotlinx.coroutines/cancelChildren|[email protected](kotlin.Throwable?){}[0]
+final fun (kotlinx.coroutines/Job).kotlinx.coroutines/ensureActive() // kotlinx.coroutines/ensureActive|[email protected](){}[0]
+final fun <#A: kotlin/Any> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/onReceiveOrNull(): kotlinx.coroutines.selects/SelectClause1<#A?> // kotlinx.coroutines.channels/onReceiveOrNull|[email protected]<0:0>(){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (kotlinx.coroutines.channels/ReceiveChannel<#A?>).kotlinx.coroutines.channels/filterNotNull(): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/filterNotNull|[email protected]<0:0?>(){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (kotlinx.coroutines.channels/ReceiveChannel<#A?>).kotlinx.coroutines.channels/requireNoNulls(): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/requireNoNulls|[email protected]<0:0?>(){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (kotlinx.coroutines.flow/Flow<#A?>).kotlinx.coroutines.flow/filterNotNull(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/filterNotNull|[email protected]<0:0?>(){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (kotlinx.coroutines.flow/Flow<*>).kotlinx.coroutines.flow/filterIsInstance(kotlin.reflect/KClass<#A>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/filterIsInstance|[email protected]<*>(kotlin.reflect.KClass<0:0>){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/mapIndexedNotNull(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction2<kotlin/Int, #A, #B?>): kotlinx.coroutines.channels/ReceiveChannel<#B> // kotlinx.coroutines.channels/mapIndexedNotNull|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction2<kotlin.Int,0:0,0:1?>){0§<kotlin.Any?>;1§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/mapNotNull(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, #B?>): kotlinx.coroutines.channels/ReceiveChannel<#B> // kotlinx.coroutines.channels/mapNotNull|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,0:1?>){0§<kotlin.Any?>;1§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?, #E: kotlin/Any?, #F: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/combineLatest(kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlinx.coroutines.flow/Flow<#D>, kotlinx.coroutines.flow/Flow<#E>, kotlin.coroutines/SuspendFunction5<#A, #B, #C, #D, #E, #F>): kotlinx.coroutines.flow/Flow<#F> // kotlinx.coroutines.flow/combineLatest|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlinx.coroutines.flow.Flow<0:3>;kotlinx.coroutines.flow.Flow<0:4>;kotlin.coroutines.SuspendFunction5<0:0,0:1,0:2,0:3,0:4,0:5>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>;4§<kotlin.Any?>;5§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?, #E: kotlin/Any?, #F: kotlin/Any?> kotlinx.coroutines.flow/combine(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlinx.coroutines.flow/Flow<#D>, kotlinx.coroutines.flow/Flow<#E>, kotlin.coroutines/SuspendFunction5<#A, #B, #C, #D, #E, #F>): kotlinx.coroutines.flow/Flow<#F> // kotlinx.coroutines.flow/combine|combine(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlinx.coroutines.flow.Flow<0:3>;kotlinx.coroutines.flow.Flow<0:4>;kotlin.coroutines.SuspendFunction5<0:0,0:1,0:2,0:3,0:4,0:5>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>;4§<kotlin.Any?>;5§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?, #E: kotlin/Any?, #F: kotlin/Any?> kotlinx.coroutines.flow/combineTransform(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlinx.coroutines.flow/Flow<#D>, kotlinx.coroutines.flow/Flow<#E>, kotlin.coroutines/SuspendFunction6<kotlinx.coroutines.flow/FlowCollector<#F>, #A, #B, #C, #D, #E, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#F> // kotlinx.coroutines.flow/combineTransform|combineTransform(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlinx.coroutines.flow.Flow<0:3>;kotlinx.coroutines.flow.Flow<0:4>;kotlin.coroutines.SuspendFunction6<kotlinx.coroutines.flow.FlowCollector<0:5>,0:0,0:1,0:2,0:3,0:4,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>;4§<kotlin.Any?>;5§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?, #E: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/combineLatest(kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlinx.coroutines.flow/Flow<#D>, kotlin.coroutines/SuspendFunction4<#A, #B, #C, #D, #E>): kotlinx.coroutines.flow/Flow<#E> // kotlinx.coroutines.flow/combineLatest|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlinx.coroutines.flow.Flow<0:3>;kotlin.coroutines.SuspendFunction4<0:0,0:1,0:2,0:3,0:4>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>;4§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?, #E: kotlin/Any?> kotlinx.coroutines.flow/combine(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlinx.coroutines.flow/Flow<#D>, kotlin.coroutines/SuspendFunction4<#A, #B, #C, #D, #E>): kotlinx.coroutines.flow/Flow<#E> // kotlinx.coroutines.flow/combine|combine(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlinx.coroutines.flow.Flow<0:3>;kotlin.coroutines.SuspendFunction4<0:0,0:1,0:2,0:3,0:4>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>;4§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?, #E: kotlin/Any?> kotlinx.coroutines.flow/combineTransform(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlinx.coroutines.flow/Flow<#D>, kotlin.coroutines/SuspendFunction5<kotlinx.coroutines.flow/FlowCollector<#E>, #A, #B, #C, #D, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#E> // kotlinx.coroutines.flow/combineTransform|combineTransform(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlinx.coroutines.flow.Flow<0:3>;kotlin.coroutines.SuspendFunction5<kotlinx.coroutines.flow.FlowCollector<0:4>,0:0,0:1,0:2,0:3,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>;4§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/combineLatest(kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlin.coroutines/SuspendFunction3<#A, #B, #C, #D>): kotlinx.coroutines.flow/Flow<#D> // kotlinx.coroutines.flow/combineLatest|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlin.coroutines.SuspendFunction3<0:0,0:1,0:2,0:3>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?> kotlinx.coroutines.flow/combine(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlin.coroutines/SuspendFunction3<#A, #B, #C, #D>): kotlinx.coroutines.flow/Flow<#D> // kotlinx.coroutines.flow/combine|combine(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlin.coroutines.SuspendFunction3<0:0,0:1,0:2,0:3>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?> kotlinx.coroutines.flow/combineTransform(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlinx.coroutines.flow/Flow<#C>, kotlin.coroutines/SuspendFunction4<kotlinx.coroutines.flow/FlowCollector<#D>, #A, #B, #C, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#D> // kotlinx.coroutines.flow/combineTransform|combineTransform(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlinx.coroutines.flow.Flow<0:2>;kotlin.coroutines.SuspendFunction4<kotlinx.coroutines.flow.FlowCollector<0:3>,0:0,0:1,0:2,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>;3§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/zip(kotlinx.coroutines.channels/ReceiveChannel<#B>, kotlin.coroutines/CoroutineContext = ..., kotlin/Function2<#A, #B, #C>): kotlinx.coroutines.channels/ReceiveChannel<#C> // kotlinx.coroutines.channels/zip|[email protected]<0:0>(kotlinx.coroutines.channels.ReceiveChannel<0:1>;kotlin.coroutines.CoroutineContext;kotlin.Function2<0:0,0:1,0:2>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/combine(kotlinx.coroutines.flow/Flow<#B>, kotlin.coroutines/SuspendFunction2<#A, #B, #C>): kotlinx.coroutines.flow/Flow<#C> // kotlinx.coroutines.flow/combine|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:1>;kotlin.coroutines.SuspendFunction2<0:0,0:1,0:2>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/combineLatest(kotlinx.coroutines.flow/Flow<#B>, kotlin.coroutines/SuspendFunction2<#A, #B, #C>): kotlinx.coroutines.flow/Flow<#C> // kotlinx.coroutines.flow/combineLatest|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:1>;kotlin.coroutines.SuspendFunction2<0:0,0:1,0:2>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/combineTransform(kotlinx.coroutines.flow/Flow<#B>, kotlin.coroutines/SuspendFunction3<kotlinx.coroutines.flow/FlowCollector<#C>, #A, #B, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#C> // kotlinx.coroutines.flow/combineTransform|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:1>;kotlin.coroutines.SuspendFunction3<kotlinx.coroutines.flow.FlowCollector<0:2>,0:0,0:1,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/zip(kotlinx.coroutines.flow/Flow<#B>, kotlin.coroutines/SuspendFunction2<#A, #B, #C>): kotlinx.coroutines.flow/Flow<#C> // kotlinx.coroutines.flow/zip|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:1>;kotlin.coroutines.SuspendFunction2<0:0,0:1,0:2>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> kotlinx.coroutines.flow/combine(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlin.coroutines/SuspendFunction2<#A, #B, #C>): kotlinx.coroutines.flow/Flow<#C> // kotlinx.coroutines.flow/combine|combine(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlin.coroutines.SuspendFunction2<0:0,0:1,0:2>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> kotlinx.coroutines.flow/combineTransform(kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>, kotlin.coroutines/SuspendFunction3<kotlinx.coroutines.flow/FlowCollector<#C>, #A, #B, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#C> // kotlinx.coroutines.flow/combineTransform|combineTransform(kotlinx.coroutines.flow.Flow<0:0>;kotlinx.coroutines.flow.Flow<0:1>;kotlin.coroutines.SuspendFunction3<kotlinx.coroutines.flow.FlowCollector<0:2>,0:0,0:1,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/distinctBy(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, #B>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/distinctBy|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/flatMap(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, kotlinx.coroutines.channels/ReceiveChannel<#B>>): kotlinx.coroutines.channels/ReceiveChannel<#B> // kotlinx.coroutines.channels/flatMap|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,kotlinx.coroutines.channels.ReceiveChannel<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/map(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, #B>): kotlinx.coroutines.channels/ReceiveChannel<#B> // kotlinx.coroutines.channels/map|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/mapIndexed(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction2<kotlin/Int, #A, #B>): kotlinx.coroutines.channels/ReceiveChannel<#B> // kotlinx.coroutines.channels/mapIndexed|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction2<kotlin.Int,0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/zip(kotlinx.coroutines.channels/ReceiveChannel<#B>): kotlinx.coroutines.channels/ReceiveChannel<kotlin/Pair<#A, #B>> // kotlinx.coroutines.channels/zip|[email protected]<0:0>(kotlinx.coroutines.channels.ReceiveChannel<0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/compose(kotlin/Function1<kotlinx.coroutines.flow/Flow<#A>, kotlinx.coroutines.flow/Flow<#B>>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/compose|[email protected]<0:0>(kotlin.Function1<kotlinx.coroutines.flow.Flow<0:0>,kotlinx.coroutines.flow.Flow<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/concatMap(kotlin/Function1<#A, kotlinx.coroutines.flow/Flow<#B>>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/concatMap|[email protected]<0:0>(kotlin.Function1<0:0,kotlinx.coroutines.flow.Flow<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/distinctUntilChangedBy(kotlin/Function1<#A, #B>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/distinctUntilChangedBy|[email protected]<0:0>(kotlin.Function1<0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/flatMap(kotlin.coroutines/SuspendFunction1<#A, kotlinx.coroutines.flow/Flow<#B>>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/flatMap|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlinx.coroutines.flow.Flow<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/flatMapConcat(kotlin.coroutines/SuspendFunction1<#A, kotlinx.coroutines.flow/Flow<#B>>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/flatMapConcat|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlinx.coroutines.flow.Flow<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/flatMapMerge(kotlin/Int = ..., kotlin.coroutines/SuspendFunction1<#A, kotlinx.coroutines.flow/Flow<#B>>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/flatMapMerge|[email protected]<0:0>(kotlin.Int;kotlin.coroutines.SuspendFunction1<0:0,kotlinx.coroutines.flow.Flow<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/mapLatest(kotlin.coroutines/SuspendFunction1<#A, #B>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/mapLatest|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/runningFold(#B, kotlin.coroutines/SuspendFunction2<#B, #A, #B>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/runningFold|[email protected]<0:0>(0:1;kotlin.coroutines.SuspendFunction2<0:1,0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/scan(#B, kotlin.coroutines/SuspendFunction2<#B, #A, #B>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/scan|[email protected]<0:0>(0:1;kotlin.coroutines.SuspendFunction2<0:1,0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/scanFold(#B, kotlin.coroutines/SuspendFunction2<#B, #A, #B>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/scanFold|[email protected]<0:0>(0:1;kotlin.coroutines.SuspendFunction2<0:1,0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/switchMap(kotlin.coroutines/SuspendFunction1<#A, kotlinx.coroutines.flow/Flow<#B>>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/switchMap|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlinx.coroutines.flow.Flow<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/transformLatest(kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#B>, #A, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/transformLatest|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:1>,0:0,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/transformWhile(kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#B>, #A, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/transformWhile|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:1>,0:0,kotlin.Boolean>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/asFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<kotlinx.coroutines.flow/Flow<#A>>).kotlinx.coroutines.flow/merge(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/merge|[email protected]<kotlinx.coroutines.flow.Flow<0:0>>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin.collections/Iterator<#A>).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/asFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin.coroutines/SuspendFunction0<#A>).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/asFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin.coroutines/SuspendFunction0<#A>).kotlinx.coroutines.intrinsics/startCoroutineCancellable(kotlin.coroutines/Continuation<#A>) // kotlinx.coroutines.intrinsics/startCoroutineCancellable|[email protected]<0:0>(kotlin.coroutines.Continuation<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin.sequences/Sequence<#A>).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/asFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin/Array<#A>).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/asFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlin/Function0<#A>).kotlinx.coroutines.flow/asFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/asFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/broadcast(kotlin/Int = ..., kotlinx.coroutines/CoroutineStart = ...): kotlinx.coroutines.channels/BroadcastChannel<#A> // kotlinx.coroutines.channels/broadcast|[email protected]<0:0>(kotlin.Int;kotlinx.coroutines.CoroutineStart){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/distinct(): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/distinct|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/drop(kotlin/Int, kotlin.coroutines/CoroutineContext = ...): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/drop|[email protected]<0:0>(kotlin.Int;kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/dropWhile(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/dropWhile|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/filter(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/filter|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/filterIndexed(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction2<kotlin/Int, #A, kotlin/Boolean>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/filterIndexed|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction2<kotlin.Int,0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/filterNot(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/filterNot|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/take(kotlin/Int, kotlin.coroutines/CoroutineContext = ...): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/take|[email protected]<0:0>(kotlin.Int;kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/takeWhile(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/takeWhile|[email protected]<0:0>(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/withIndex(kotlin.coroutines/CoroutineContext = ...): kotlinx.coroutines.channels/ReceiveChannel<kotlin.collections/IndexedValue<#A>> // kotlinx.coroutines.channels/withIndex|[email protected]<0:0>(kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.flow/consumeAsFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/consumeAsFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.flow/receiveAsFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/receiveAsFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/buffer(kotlin/Int = ...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/buffer|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/buffer(kotlin/Int = ..., kotlinx.coroutines.channels/BufferOverflow = ...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/buffer|[email protected]<0:0>(kotlin.Int;kotlinx.coroutines.channels.BufferOverflow){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/cache(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/cache|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/cancellable(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/cancellable|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/catch(kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Throwable, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/catch|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Throwable,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/chunked(kotlin/Int): kotlinx.coroutines.flow/Flow<kotlin.collections/List<#A>> // kotlinx.coroutines.flow/chunked|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/concatWith(#A): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/concatWith|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/concatWith(kotlinx.coroutines.flow/Flow<#A>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/concatWith|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/conflate(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/conflate|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/debounce(kotlin.time/Duration): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/debounce|[email protected]<0:0>(kotlin.time.Duration){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/debounce(kotlin/Function1<#A, kotlin.time/Duration>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/debounce|[email protected]<0:0>(kotlin.Function1<0:0,kotlin.time.Duration>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/debounce(kotlin/Function1<#A, kotlin/Long>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/debounce|[email protected]<0:0>(kotlin.Function1<0:0,kotlin.Long>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/debounce(kotlin/Long): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/debounce|[email protected]<0:0>(kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/delayEach(kotlin/Long): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/delayEach|[email protected]<0:0>(kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/delayFlow(kotlin/Long): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/delayFlow|[email protected]<0:0>(kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/distinctUntilChanged(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/distinctUntilChanged|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/distinctUntilChanged(kotlin/Function2<#A, #A, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/distinctUntilChanged|[email protected]<0:0>(kotlin.Function2<0:0,0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/drop(kotlin/Int): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/drop|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/dropWhile(kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/dropWhile|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/flowOn(kotlin.coroutines/CoroutineContext): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flowOn|[email protected]<0:0>(kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/forEach(kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>) // kotlinx.coroutines.flow/forEach|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/launchIn(kotlinx.coroutines/CoroutineScope): kotlinx.coroutines/Job // kotlinx.coroutines.flow/launchIn|[email protected]<0:0>(kotlinx.coroutines.CoroutineScope){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/observeOn(kotlin.coroutines/CoroutineContext): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/observeOn|[email protected]<0:0>(kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onCompletion(kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onCompletion|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Throwable?,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onEach(kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onEach|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onEmpty(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onEmpty|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onErrorResume(kotlinx.coroutines.flow/Flow<#A>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onErrorResume|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onErrorResumeNext(kotlinx.coroutines.flow/Flow<#A>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onErrorResumeNext|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onErrorReturn(#A): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onErrorReturn|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onErrorReturn(#A, kotlin/Function1<kotlin/Throwable, kotlin/Boolean> = ...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onErrorReturn|[email protected]<0:0>(0:0;kotlin.Function1<kotlin.Throwable,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/onStart(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/onStart|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/produceIn(kotlinx.coroutines/CoroutineScope): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.flow/produceIn|[email protected]<0:0>(kotlinx.coroutines.CoroutineScope){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/publish(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/publish|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/publish(kotlin/Int): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/publish|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/publishOn(kotlin.coroutines/CoroutineContext): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/publishOn|[email protected]<0:0>(kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/replay(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/replay|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/replay(kotlin/Int): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/replay|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/retry(kotlin/Long = ..., kotlin.coroutines/SuspendFunction1<kotlin/Throwable, kotlin/Boolean> = ...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/retry|[email protected]<0:0>(kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlin.Throwable,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/retryWhen(kotlin.coroutines/SuspendFunction3<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Throwable, kotlin/Long, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/retryWhen|[email protected]<0:0>(kotlin.coroutines.SuspendFunction3<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Throwable,kotlin.Long,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/runningReduce(kotlin.coroutines/SuspendFunction2<#A, #A, #A>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/runningReduce|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<0:0,0:0,0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/sample(kotlin.time/Duration): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/sample|[email protected]<0:0>(kotlin.time.Duration){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/sample(kotlin/Long): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/sample|[email protected]<0:0>(kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/scanReduce(kotlin.coroutines/SuspendFunction2<#A, #A, #A>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/scanReduce|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<0:0,0:0,0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/shareIn(kotlinx.coroutines/CoroutineScope, kotlinx.coroutines.flow/SharingStarted, kotlin/Int = ...): kotlinx.coroutines.flow/SharedFlow<#A> // kotlinx.coroutines.flow/shareIn|[email protected]<0:0>(kotlinx.coroutines.CoroutineScope;kotlinx.coroutines.flow.SharingStarted;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/skip(kotlin/Int): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/skip|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/startWith(#A): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/startWith|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/startWith(kotlinx.coroutines.flow/Flow<#A>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/startWith|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/stateIn(kotlinx.coroutines/CoroutineScope, kotlinx.coroutines.flow/SharingStarted, #A): kotlinx.coroutines.flow/StateFlow<#A> // kotlinx.coroutines.flow/stateIn|[email protected]<0:0>(kotlinx.coroutines.CoroutineScope;kotlinx.coroutines.flow.SharingStarted;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/subscribe() // kotlinx.coroutines.flow/subscribe|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/subscribe(kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>) // kotlinx.coroutines.flow/subscribe|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/subscribe(kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>, kotlin.coroutines/SuspendFunction1<kotlin/Throwable, kotlin/Unit>) // kotlinx.coroutines.flow/subscribe|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Unit>;kotlin.coroutines.SuspendFunction1<kotlin.Throwable,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/subscribeOn(kotlin.coroutines/CoroutineContext): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/subscribeOn|[email protected]<0:0>(kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/take(kotlin/Int): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/take|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/takeWhile(kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/takeWhile|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/timeout(kotlin.time/Duration): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/timeout|[email protected]<0:0>(kotlin.time.Duration){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/withIndex(): kotlinx.coroutines.flow/Flow<kotlin.collections/IndexedValue<#A>> // kotlinx.coroutines.flow/withIndex|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<kotlinx.coroutines.flow/Flow<#A>>).kotlinx.coroutines.flow/flatten(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flatten|[email protected]<kotlinx.coroutines.flow.Flow<0:0>>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<kotlinx.coroutines.flow/Flow<#A>>).kotlinx.coroutines.flow/flattenConcat(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flattenConcat|[email protected]<kotlinx.coroutines.flow.Flow<0:0>>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<kotlinx.coroutines.flow/Flow<#A>>).kotlinx.coroutines.flow/flattenMerge(kotlin/Int = ...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flattenMerge|[email protected]<kotlinx.coroutines.flow.Flow<0:0>>(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<kotlinx.coroutines.flow/Flow<#A>>).kotlinx.coroutines.flow/merge(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/merge|[email protected]<kotlinx.coroutines.flow.Flow<0:0>>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/MutableSharedFlow<#A>).kotlinx.coroutines.flow/asSharedFlow(): kotlinx.coroutines.flow/SharedFlow<#A> // kotlinx.coroutines.flow/asSharedFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/MutableStateFlow<#A>).kotlinx.coroutines.flow/asStateFlow(): kotlinx.coroutines.flow/StateFlow<#A> // kotlinx.coroutines.flow/asStateFlow|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/cancellable(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/cancellable|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/flowOn(kotlin.coroutines/CoroutineContext): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flowOn|[email protected]<0:0>(kotlin.coroutines.CoroutineContext){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/onSubscription(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Unit>): kotlinx.coroutines.flow/SharedFlow<#A> // kotlinx.coroutines.flow/onSubscription|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/StateFlow<#A>).kotlinx.coroutines.flow/conflate(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/conflate|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/StateFlow<#A>).kotlinx.coroutines.flow/distinctUntilChanged(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/distinctUntilChanged|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.selects/SelectBuilder<#A>).kotlinx.coroutines.selects/onTimeout(kotlin.time/Duration, kotlin.coroutines/SuspendFunction0<#A>) // kotlinx.coroutines.selects/onTimeout|[email protected]<0:0>(kotlin.time.Duration;kotlin.coroutines.SuspendFunction0<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.selects/SelectBuilder<#A>).kotlinx.coroutines.selects/onTimeout(kotlin/Long, kotlin.coroutines/SuspendFunction0<#A>) // kotlinx.coroutines.selects/onTimeout|[email protected]<0:0>(kotlin.Long;kotlin.coroutines.SuspendFunction0<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/CompletableDeferred<#A>).kotlinx.coroutines/completeWith(kotlin/Result<#A>): kotlin/Boolean // kotlinx.coroutines/completeWith|[email protected]<0:0>(kotlin.Result<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines.channels/broadcast(kotlin.coroutines/CoroutineContext = ..., kotlin/Int = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin/Function1<kotlin/Throwable?, kotlin/Unit>? = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.channels/BroadcastChannel<#A> // kotlinx.coroutines.channels/broadcast|[email protected](kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.CoroutineStart;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines.channels/produce(kotlin.coroutines/CoroutineContext = ..., kotlin/Int = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/produce|[email protected](kotlin.coroutines.CoroutineContext;kotlin.Int;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines.channels/produce(kotlin.coroutines/CoroutineContext = ..., kotlin/Int = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin/Function1<kotlin/Throwable?, kotlin/Unit>? = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.channels/ReceiveChannel<#A> // kotlinx.coroutines.channels/produce|[email protected](kotlin.coroutines.CoroutineContext;kotlin.Int;kotlinx.coroutines.CoroutineStart;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/async(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlinx.coroutines/Deferred<#A> // kotlinx.coroutines/async|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.channels/BroadcastChannel(kotlin/Int): kotlinx.coroutines.channels/BroadcastChannel<#A> // kotlinx.coroutines.channels/BroadcastChannel|BroadcastChannel(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.channels/Channel(kotlin/Int = ...): kotlinx.coroutines.channels/Channel<#A> // kotlinx.coroutines.channels/Channel|Channel(kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.channels/Channel(kotlin/Int = ..., kotlinx.coroutines.channels/BufferOverflow = ..., kotlin/Function1<#A, kotlin/Unit>? = ...): kotlinx.coroutines.channels/Channel<#A> // kotlinx.coroutines.channels/Channel|Channel(kotlin.Int;kotlinx.coroutines.channels.BufferOverflow;kotlin.Function1<0:0,kotlin.Unit>?){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/MutableSharedFlow(kotlin/Int = ..., kotlin/Int = ..., kotlinx.coroutines.channels/BufferOverflow = ...): kotlinx.coroutines.flow/MutableSharedFlow<#A> // kotlinx.coroutines.flow/MutableSharedFlow|MutableSharedFlow(kotlin.Int;kotlin.Int;kotlinx.coroutines.channels.BufferOverflow){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/MutableStateFlow(#A): kotlinx.coroutines.flow/MutableStateFlow<#A> // kotlinx.coroutines.flow/MutableStateFlow|MutableStateFlow(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/callbackFlow(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/callbackFlow|callbackFlow(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/channelFlow(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.channels/ProducerScope<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/channelFlow|channelFlow(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.channels.ProducerScope<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/emptyFlow(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/emptyFlow|emptyFlow(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/flow(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flow|flow(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/flowOf(#A): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flowOf|flowOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/flowOf(kotlin/Array<out #A>...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/flowOf|flowOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines.flow/merge(kotlin/Array<out kotlinx.coroutines.flow/Flow<#A>>...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/merge|merge(kotlin.Array<out|kotlinx.coroutines.flow.Flow<0:0>>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines/CompletableDeferred(#A): kotlinx.coroutines/CompletableDeferred<#A> // kotlinx.coroutines/CompletableDeferred|CompletableDeferred(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.coroutines/CompletableDeferred(kotlinx.coroutines/Job? = ...): kotlinx.coroutines/CompletableDeferred<#A> // kotlinx.coroutines/CompletableDeferred|CompletableDeferred(kotlinx.coroutines.Job?){0§<kotlin.Any?>}[0]
+final fun kotlinx.coroutines.channels/consumesAll(kotlin/Array<out kotlinx.coroutines.channels/ReceiveChannel<*>>...): kotlin/Function1<kotlin/Throwable?, kotlin/Unit> // kotlinx.coroutines.channels/consumesAll|consumesAll(kotlin.Array<out|kotlinx.coroutines.channels.ReceiveChannel<*>>...){}[0]
+final fun kotlinx.coroutines.sync/Mutex(kotlin/Boolean = ...): kotlinx.coroutines.sync/Mutex // kotlinx.coroutines.sync/Mutex|Mutex(kotlin.Boolean){}[0]
+final fun kotlinx.coroutines.sync/Semaphore(kotlin/Int, kotlin/Int = ...): kotlinx.coroutines.sync/Semaphore // kotlinx.coroutines.sync/Semaphore|Semaphore(kotlin.Int;kotlin.Int){}[0]
+final fun kotlinx.coroutines/CancellationException(kotlin/String?, kotlin/Throwable?): kotlin.coroutines.cancellation/CancellationException // kotlinx.coroutines/CancellationException|CancellationException(kotlin.String?;kotlin.Throwable?){}[0]
+final fun kotlinx.coroutines/CoroutineScope(kotlin.coroutines/CoroutineContext): kotlinx.coroutines/CoroutineScope // kotlinx.coroutines/CoroutineScope|CoroutineScope(kotlin.coroutines.CoroutineContext){}[0]
+final fun kotlinx.coroutines/Job(kotlinx.coroutines/Job? = ...): kotlinx.coroutines/CompletableJob // kotlinx.coroutines/Job|Job(kotlinx.coroutines.Job?){}[0]
+final fun kotlinx.coroutines/Job0(kotlinx.coroutines/Job? = ...): kotlinx.coroutines/Job // kotlinx.coroutines/Job0|Job0(kotlinx.coroutines.Job?){}[0]
+final fun kotlinx.coroutines/MainScope(): kotlinx.coroutines/CoroutineScope // kotlinx.coroutines/MainScope|MainScope(){}[0]
+final fun kotlinx.coroutines/SupervisorJob(kotlinx.coroutines/Job? = ...): kotlinx.coroutines/CompletableJob // kotlinx.coroutines/SupervisorJob|SupervisorJob(kotlinx.coroutines.Job?){}[0]
+final fun kotlinx.coroutines/SupervisorJob0(kotlinx.coroutines/Job? = ...): kotlinx.coroutines/Job // kotlinx.coroutines/SupervisorJob0|SupervisorJob0(kotlinx.coroutines.Job?){}[0]
+final fun kotlinx.coroutines/handleCoroutineException(kotlin.coroutines/CoroutineContext, kotlin/Throwable) // kotlinx.coroutines/handleCoroutineException|handleCoroutineException(kotlin.coroutines.CoroutineContext;kotlin.Throwable){}[0]
+final inline fun <#A: kotlin/Any?, #B: kotlin/Any> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/mapNotNull(crossinline kotlin.coroutines/SuspendFunction1<#A, #B?>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/mapNotNull|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,0:1?>){0§<kotlin.Any?>;1§<kotlin.Any>}[0]
+final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/BroadcastChannel<#A>).kotlinx.coroutines.channels/consume(kotlin/Function1<kotlinx.coroutines.channels/ReceiveChannel<#A>, #B>): #B // kotlinx.coroutines.channels/consume|[email protected]<0:0>(kotlin.Function1<kotlinx.coroutines.channels.ReceiveChannel<0:0>,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/consume(kotlin/Function1<kotlinx.coroutines.channels/ReceiveChannel<#A>, #B>): #B // kotlinx.coroutines.channels/consume|[email protected]<0:0>(kotlin.Function1<kotlinx.coroutines.channels.ReceiveChannel<0:0>,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/flatMapLatest(crossinline kotlin.coroutines/SuspendFunction1<#A, kotlinx.coroutines.flow/Flow<#B>>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/flatMapLatest|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlinx.coroutines.flow.Flow<0:1>>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/map(crossinline kotlin.coroutines/SuspendFunction1<#A, #B>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/map|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/transform(crossinline kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#B>, #A, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/transform|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:1>,0:0,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/unsafeTransform(crossinline kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#B>, #A, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/unsafeTransform|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:1>,0:0,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ChannelResult<#A>).kotlinx.coroutines.channels/getOrElse(kotlin/Function1<kotlin/Throwable?, #A>): #A // kotlinx.coroutines.channels/getOrElse|[email protected]<0:0>(kotlin.Function1<kotlin.Throwable?,0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ChannelResult<#A>).kotlinx.coroutines.channels/onClosed(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines.channels/ChannelResult<#A> // kotlinx.coroutines.channels/onClosed|[email protected]<0:0>(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ChannelResult<#A>).kotlinx.coroutines.channels/onFailure(kotlin/Function1<kotlin/Throwable?, kotlin/Unit>): kotlinx.coroutines.channels/ChannelResult<#A> // kotlinx.coroutines.channels/onFailure|[email protected]<0:0>(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ChannelResult<#A>).kotlinx.coroutines.channels/onSuccess(kotlin/Function1<#A, kotlin/Unit>): kotlinx.coroutines.channels/ChannelResult<#A> // kotlinx.coroutines.channels/onSuccess|[email protected]<0:0>(kotlin.Function1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/filter(crossinline kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/filter|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/filterNot(crossinline kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/filterNot|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/MutableStateFlow<#A>).kotlinx.coroutines.flow/getAndUpdate(kotlin/Function1<#A, #A>): #A // kotlinx.coroutines.flow/getAndUpdate|[email protected]<0:0>(kotlin.Function1<0:0,0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/MutableStateFlow<#A>).kotlinx.coroutines.flow/update(kotlin/Function1<#A, #A>) // kotlinx.coroutines.flow/update|[email protected]<0:0>(kotlin.Function1<0:0,0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/MutableStateFlow<#A>).kotlinx.coroutines.flow/updateAndGet(kotlin/Function1<#A, #A>): #A // kotlinx.coroutines.flow/updateAndGet|[email protected]<0:0>(kotlin.Function1<0:0,0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/catch(noinline kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Throwable, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/catch|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Throwable,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/retry(kotlin/Long = ..., noinline kotlin.coroutines/SuspendFunction1<kotlin/Throwable, kotlin/Boolean> = ...): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/retry|[email protected]<0:0>(kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlin.Throwable,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/retryWhen(noinline kotlin.coroutines/SuspendFunction3<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Throwable, kotlin/Long, kotlin/Boolean>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/retryWhen|[email protected]<0:0>(kotlin.coroutines.SuspendFunction3<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Throwable,kotlin.Long,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> kotlinx.coroutines.flow.internal/unsafeFlow(crossinline kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow.internal/unsafeFlow|unsafeFlow(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?, #B: kotlin/Any?> kotlinx.coroutines.flow/combine(kotlin.collections/Iterable<kotlinx.coroutines.flow/Flow<#A>>, crossinline kotlin.coroutines/SuspendFunction1<kotlin/Array<#A>, #B>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/combine|combine(kotlin.collections.Iterable<kotlinx.coroutines.flow.Flow<0:0>>;kotlin.coroutines.SuspendFunction1<kotlin.Array<0:0>,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?, #B: kotlin/Any?> kotlinx.coroutines.flow/combine(kotlin/Array<out kotlinx.coroutines.flow/Flow<#A>>..., crossinline kotlin.coroutines/SuspendFunction1<kotlin/Array<#A>, #B>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/combine|combine(kotlin.Array<out|kotlinx.coroutines.flow.Flow<0:0>>...;kotlin.coroutines.SuspendFunction1<kotlin.Array<0:0>,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?, #B: kotlin/Any?> kotlinx.coroutines.flow/combineTransform(kotlin.collections/Iterable<kotlinx.coroutines.flow/Flow<#A>>, crossinline kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#B>, kotlin/Array<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/combineTransform|combineTransform(kotlin.collections.Iterable<kotlinx.coroutines.flow.Flow<0:0>>;kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:1>,kotlin.Array<0:0>,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?, #B: kotlin/Any?> kotlinx.coroutines.flow/combineTransform(kotlin/Array<out kotlinx.coroutines.flow/Flow<#A>>..., crossinline kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#B>, kotlin/Array<#A>, kotlin/Unit>): kotlinx.coroutines.flow/Flow<#B> // kotlinx.coroutines.flow/combineTransform|combineTransform(kotlin.Array<out|kotlinx.coroutines.flow.Flow<0:0>>...;kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:1>,kotlin.Array<0:0>,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.coroutines.flow/Flow<*>).kotlinx.coroutines.flow/filterIsInstance(): kotlinx.coroutines.flow/Flow<#A> // kotlinx.coroutines.flow/filterIsInstance|[email protected]<*>(){0§<kotlin.Any?>}[0]
+final inline fun kotlinx.coroutines.flow.internal/checkIndexOverflow(kotlin/Int): kotlin/Int // kotlinx.coroutines.flow.internal/checkIndexOverflow|checkIndexOverflow(kotlin.Int){}[0]
+final inline fun kotlinx.coroutines/CoroutineExceptionHandler(crossinline kotlin/Function2<kotlin.coroutines/CoroutineContext, kotlin/Throwable, kotlin/Unit>): kotlinx.coroutines/CoroutineExceptionHandler // kotlinx.coroutines/CoroutineExceptionHandler|CoroutineExceptionHandler(kotlin.Function2<kotlin.coroutines.CoroutineContext,kotlin.Throwable,kotlin.Unit>){}[0]
+final inline fun kotlinx.coroutines/Runnable(crossinline kotlin/Function0<kotlin/Unit>): kotlinx.coroutines/Runnable // kotlinx.coroutines/Runnable|Runnable(kotlin.Function0<kotlin.Unit>){}[0]
+final suspend fun (kotlin.collections/Collection<kotlinx.coroutines/Job>).kotlinx.coroutines/joinAll() // kotlinx.coroutines/joinAll|[email protected]<kotlinx.coroutines.Job>(){}[0]
+final suspend fun (kotlinx.coroutines.channels/ProducerScope<*>).kotlinx.coroutines.channels/awaitClose(kotlin/Function0<kotlin/Unit> = ...) // kotlinx.coroutines.channels/awaitClose|[email protected]<*>(kotlin.Function0<kotlin.Unit>){}[0]
+final suspend fun (kotlinx.coroutines.flow/Flow<*>).kotlinx.coroutines.flow/collect() // kotlinx.coroutines.flow/collect|[email protected]<*>(){}[0]
+final suspend fun (kotlinx.coroutines/Job).kotlinx.coroutines/cancelAndJoin() // kotlinx.coroutines/cancelAndJoin|[email protected](){}[0]
+final suspend fun <#A: kotlin/Any, #B: kotlin.collections/MutableCollection<in #A>> (kotlinx.coroutines.channels/ReceiveChannel<#A?>).kotlinx.coroutines.channels/filterNotNullTo(#B): #B // kotlinx.coroutines.channels/filterNotNullTo|[email protected]<0:0?>(0:1){0§<kotlin.Any>;1§<kotlin.collections.MutableCollection<in|0:0>>}[0]
+final suspend fun <#A: kotlin/Any, #B: kotlinx.coroutines.channels/SendChannel<#A>> (kotlinx.coroutines.channels/ReceiveChannel<#A?>).kotlinx.coroutines.channels/filterNotNullTo(#B): #B // kotlinx.coroutines.channels/filterNotNullTo|[email protected]<0:0?>(0:1){0§<kotlin.Any>;1§<kotlinx.coroutines.channels.SendChannel<0:0>>}[0]
+final suspend fun <#A: kotlin/Any> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/receiveOrNull(): #A? // kotlinx.coroutines.channels/receiveOrNull|[email protected]<0:0>(){0§<kotlin.Any>}[0]
+final suspend fun <#A: kotlin/Any?, #B: #A> (kotlinx.coroutines.flow/Flow<#B>).kotlinx.coroutines.flow/reduce(kotlin.coroutines/SuspendFunction2<#A, #B, #A>): #A // kotlinx.coroutines.flow/reduce|[email protected]<0:1>(kotlin.coroutines.SuspendFunction2<0:0,0:1,0:0>){0§<kotlin.Any?>;1§<0:0>}[0]
+final suspend fun <#A: kotlin/Any?, #B: kotlin.collections/MutableCollection<in #A>> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toCollection(#B): #B // kotlinx.coroutines.channels/toCollection|[email protected]<0:0>(0:1){0§<kotlin.Any?>;1§<kotlin.collections.MutableCollection<in|0:0>>}[0]
+final suspend fun <#A: kotlin/Any?, #B: kotlin.collections/MutableCollection<in #A>> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/toCollection(#B): #B // kotlinx.coroutines.flow/toCollection|[email protected]<0:0>(0:1){0§<kotlin.Any?>;1§<kotlin.collections.MutableCollection<in|0:0>>}[0]
+final suspend fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin.collections/MutableMap<in #A, in #B>> (kotlinx.coroutines.channels/ReceiveChannel<kotlin/Pair<#A, #B>>).kotlinx.coroutines.channels/toMap(#C): #C // kotlinx.coroutines.channels/toMap|[email protected]<kotlin.Pair<0:0,0:1>>(0:2){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.collections.MutableMap<in|0:0,in|0:1>>}[0]
+final suspend fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<kotlin/Pair<#A, #B>>).kotlinx.coroutines.channels/toMap(): kotlin.collections/Map<#A, #B> // kotlinx.coroutines.channels/toMap|[email protected]<kotlin.Pair<0:0,0:1>>(){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/FlowCollector<#A>).kotlinx.coroutines.flow.internal/combineInternal(kotlin/Array<out kotlinx.coroutines.flow/Flow<#B>>, kotlin/Function0<kotlin/Array<#B?>?>, kotlin.coroutines/SuspendFunction2<kotlinx.coroutines.flow/FlowCollector<#A>, kotlin/Array<#B>, kotlin/Unit>) // kotlinx.coroutines.flow.internal/combineInternal|[email protected]<0:0>(kotlin.Array<out|kotlinx.coroutines.flow.Flow<0:1>>;kotlin.Function0<kotlin.Array<0:1?>?>;kotlin.coroutines.SuspendFunction2<kotlinx.coroutines.flow.FlowCollector<0:0>,kotlin.Array<0:1>,kotlin.Unit>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?, #B: kotlinx.coroutines.channels/SendChannel<#A>> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toChannel(#B): #B // kotlinx.coroutines.channels/toChannel|[email protected]<0:0>(0:1){0§<kotlin.Any?>;1§<kotlinx.coroutines.channels.SendChannel<0:0>>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlin.collections/Collection<kotlinx.coroutines/Deferred<#A>>).kotlinx.coroutines/awaitAll(): kotlin.collections/List<#A> // kotlinx.coroutines/awaitAll|[email protected]<kotlinx.coroutines.Deferred<0:0>>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/any(): kotlin/Boolean // kotlinx.coroutines.channels/any|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/count(): kotlin/Int // kotlinx.coroutines.channels/count|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/elementAt(kotlin/Int): #A // kotlinx.coroutines.channels/elementAt|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/elementAtOrNull(kotlin/Int): #A? // kotlinx.coroutines.channels/elementAtOrNull|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/first(): #A // kotlinx.coroutines.channels/first|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/firstOrNull(): #A? // kotlinx.coroutines.channels/firstOrNull|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/indexOf(#A): kotlin/Int // kotlinx.coroutines.channels/indexOf|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/last(): #A // kotlinx.coroutines.channels/last|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/lastIndexOf(#A): kotlin/Int // kotlinx.coroutines.channels/lastIndexOf|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/lastOrNull(): #A? // kotlinx.coroutines.channels/lastOrNull|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/maxWith(kotlin/Comparator<in #A>): #A? // kotlinx.coroutines.channels/maxWith|[email protected]<0:0>(kotlin.Comparator<in|0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/minWith(kotlin/Comparator<in #A>): #A? // kotlinx.coroutines.channels/minWith|[email protected]<0:0>(kotlin.Comparator<in|0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/none(): kotlin/Boolean // kotlinx.coroutines.channels/none|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/single(): #A // kotlinx.coroutines.channels/single|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/singleOrNull(): #A? // kotlinx.coroutines.channels/singleOrNull|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toList(): kotlin.collections/List<#A> // kotlinx.coroutines.channels/toList|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toMutableList(): kotlin.collections/MutableList<#A> // kotlinx.coroutines.channels/toMutableList|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toMutableSet(): kotlin.collections/MutableSet<#A> // kotlinx.coroutines.channels/toMutableSet|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/toSet(): kotlin.collections/Set<#A> // kotlinx.coroutines.channels/toSet|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/collectLatest(kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>) // kotlinx.coroutines.flow/collectLatest|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/count(): kotlin/Int // kotlinx.coroutines.flow/count|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/count(kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): kotlin/Int // kotlinx.coroutines.flow/count|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/first(): #A // kotlinx.coroutines.flow/first|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/first(kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): #A // kotlinx.coroutines.flow/first|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/firstOrNull(): #A? // kotlinx.coroutines.flow/firstOrNull|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/firstOrNull(kotlin.coroutines/SuspendFunction1<#A, kotlin/Boolean>): #A? // kotlinx.coroutines.flow/firstOrNull|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Boolean>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/last(): #A // kotlinx.coroutines.flow/last|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/lastOrNull(): #A? // kotlinx.coroutines.flow/lastOrNull|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/single(): #A // kotlinx.coroutines.flow/single|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/singleOrNull(): #A? // kotlinx.coroutines.flow/singleOrNull|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/stateIn(kotlinx.coroutines/CoroutineScope): kotlinx.coroutines.flow/StateFlow<#A> // kotlinx.coroutines.flow/stateIn|[email protected]<0:0>(kotlinx.coroutines.CoroutineScope){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/toList(kotlin.collections/MutableList<#A> = ...): kotlin.collections/List<#A> // kotlinx.coroutines.flow/toList|[email protected]<0:0>(kotlin.collections.MutableList<0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/toSet(kotlin.collections/MutableSet<#A> = ...): kotlin.collections/Set<#A> // kotlinx.coroutines.flow/toSet|[email protected]<0:0>(kotlin.collections.MutableSet<0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/FlowCollector<#A>).kotlinx.coroutines.flow/emitAll(kotlinx.coroutines.channels/ReceiveChannel<#A>) // kotlinx.coroutines.flow/emitAll|[email protected]<0:0>(kotlinx.coroutines.channels.ReceiveChannel<0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/FlowCollector<#A>).kotlinx.coroutines.flow/emitAll(kotlinx.coroutines.flow/Flow<#A>) // kotlinx.coroutines.flow/emitAll|[email protected]<0:0>(kotlinx.coroutines.flow.Flow<0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/awaitAll(kotlin/Array<out kotlinx.coroutines/Deferred<#A>>...): kotlin.collections/List<#A> // kotlinx.coroutines/awaitAll|awaitAll(kotlin.Array<out|kotlinx.coroutines.Deferred<0:0>>...){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/coroutineScope(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A // kotlinx.coroutines/coroutineScope|coroutineScope(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/supervisorScope(kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A // kotlinx.coroutines/supervisorScope|supervisorScope(kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/withContext(kotlin.coroutines/CoroutineContext, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A // kotlinx.coroutines/withContext|withContext(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/withTimeout(kotlin.time/Duration, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A // kotlinx.coroutines/withTimeout|withTimeout(kotlin.time.Duration;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/withTimeout(kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A // kotlinx.coroutines/withTimeout|withTimeout(kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/withTimeoutOrNull(kotlin.time/Duration, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A? // kotlinx.coroutines/withTimeoutOrNull|withTimeoutOrNull(kotlin.time.Duration;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> kotlinx.coroutines/withTimeoutOrNull(kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A? // kotlinx.coroutines/withTimeoutOrNull|withTimeoutOrNull(kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend fun kotlinx.coroutines/awaitCancellation(): kotlin/Nothing // kotlinx.coroutines/awaitCancellation|awaitCancellation(){}[0]
+final suspend fun kotlinx.coroutines/delay(kotlin.time/Duration) // kotlinx.coroutines/delay|delay(kotlin.time.Duration){}[0]
+final suspend fun kotlinx.coroutines/delay(kotlin/Long) // kotlinx.coroutines/delay|delay(kotlin.Long){}[0]
+final suspend fun kotlinx.coroutines/joinAll(kotlin/Array<out kotlinx.coroutines/Job>...) // kotlinx.coroutines/joinAll|joinAll(kotlin.Array<out|kotlinx.coroutines.Job>...){}[0]
+final suspend fun kotlinx.coroutines/yield() // kotlinx.coroutines/yield|yield(){}[0]
+final suspend inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/fold(#B, crossinline kotlin.coroutines/SuspendFunction2<#B, #A, #B>): #B // kotlinx.coroutines.flow/fold|[email protected]<0:0>(0:1;kotlin.coroutines.SuspendFunction2<0:1,0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/BroadcastChannel<#A>).kotlinx.coroutines.channels/consumeEach(kotlin/Function1<#A, kotlin/Unit>) // kotlinx.coroutines.channels/consumeEach|[email protected]<0:0>(kotlin.Function1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/ReceiveChannel<#A>).kotlinx.coroutines.channels/consumeEach(kotlin/Function1<#A, kotlin/Unit>) // kotlinx.coroutines.channels/consumeEach|[email protected]<0:0>(kotlin.Function1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/collect(crossinline kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>) // kotlinx.coroutines.flow/collect|[email protected]<0:0>(kotlin.coroutines.SuspendFunction1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/Flow<#A>).kotlinx.coroutines.flow/collectIndexed(crossinline kotlin.coroutines/SuspendFunction2<kotlin/Int, #A, kotlin/Unit>) // kotlinx.coroutines.flow/collectIndexed|[email protected]<0:0>(kotlin.coroutines.SuspendFunction2<kotlin.Int,0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/count(): kotlin/Int // kotlinx.coroutines.flow/count|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/toList(): kotlin.collections/List<#A> // kotlinx.coroutines.flow/toList|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/toList(kotlin.collections/MutableList<#A>): kotlin/Nothing // kotlinx.coroutines.flow/toList|[email protected]<0:0>(kotlin.collections.MutableList<0:0>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/toSet(): kotlin.collections/Set<#A> // kotlinx.coroutines.flow/toSet|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.flow/SharedFlow<#A>).kotlinx.coroutines.flow/toSet(kotlin.collections/MutableSet<#A>): kotlin/Nothing // kotlinx.coroutines.flow/toSet|[email protected]<0:0>(kotlin.collections.MutableSet<0:0>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.sync/Mutex).kotlinx.coroutines.sync/withLock(kotlin/Any? = ..., kotlin/Function0<#A>): #A // kotlinx.coroutines.sync/withLock|[email protected](kotlin.Any?;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines.sync/Semaphore).kotlinx.coroutines.sync/withPermit(kotlin/Function0<#A>): #A // kotlinx.coroutines.sync/withPermit|[email protected](kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineDispatcher).kotlinx.coroutines/invoke(noinline kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A // kotlinx.coroutines/invoke|[email protected](kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> kotlinx.coroutines.selects/select(crossinline kotlin/Function1<kotlinx.coroutines.selects/SelectBuilder<#A>, kotlin/Unit>): #A // kotlinx.coroutines.selects/select|select(kotlin.Function1<kotlinx.coroutines.selects.SelectBuilder<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> kotlinx.coroutines.selects/selectOld(crossinline kotlin/Function1<kotlinx.coroutines.selects/SelectBuilder<#A>, kotlin/Unit>): #A // kotlinx.coroutines.selects/selectOld|selectOld(kotlin.Function1<kotlinx.coroutines.selects.SelectBuilder<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> kotlinx.coroutines.selects/selectUnbiased(crossinline kotlin/Function1<kotlinx.coroutines.selects/SelectBuilder<#A>, kotlin/Unit>): #A // kotlinx.coroutines.selects/selectUnbiased|selectUnbiased(kotlin.Function1<kotlinx.coroutines.selects.SelectBuilder<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> kotlinx.coroutines.selects/selectUnbiasedOld(crossinline kotlin/Function1<kotlinx.coroutines.selects/SelectBuilder<#A>, kotlin/Unit>): #A // kotlinx.coroutines.selects/selectUnbiasedOld|selectUnbiasedOld(kotlin.Function1<kotlinx.coroutines.selects.SelectBuilder<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun <#A: kotlin/Any?> kotlinx.coroutines/suspendCancellableCoroutine(crossinline kotlin/Function1<kotlinx.coroutines/CancellableContinuation<#A>, kotlin/Unit>): #A // kotlinx.coroutines/suspendCancellableCoroutine|suspendCancellableCoroutine(kotlin.Function1<kotlinx.coroutines.CancellableContinuation<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final suspend inline fun kotlinx.coroutines.selects/whileSelect(crossinline kotlin/Function1<kotlinx.coroutines.selects/SelectBuilder<kotlin/Boolean>, kotlin/Unit>) // kotlinx.coroutines.selects/whileSelect|whileSelect(kotlin.Function1<kotlinx.coroutines.selects.SelectBuilder<kotlin.Boolean>,kotlin.Unit>){}[0]
+final suspend inline fun kotlinx.coroutines/currentCoroutineContext(): kotlin.coroutines/CoroutineContext // kotlinx.coroutines/currentCoroutineContext|currentCoroutineContext(){}[0]
+
+// Targets: [native]
+final val kotlinx.coroutines/IO // kotlinx.coroutines/IO|@kotlinx.coroutines.Dispatchers{}IO[0]
+ final fun (kotlinx.coroutines/Dispatchers).<get-IO>(): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/IO.<get-IO>|<get-IO>@kotlinx.coroutines.Dispatchers(){}[0]
+
+// Targets: [native]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/SendChannel<#A>).kotlinx.coroutines.channels/sendBlocking(#A) // kotlinx.coroutines.channels/sendBlocking|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+
+// Targets: [native]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines.channels/SendChannel<#A>).kotlinx.coroutines.channels/trySendBlocking(#A): kotlinx.coroutines.channels/ChannelResult<kotlin/Unit> // kotlinx.coroutines.channels/trySendBlocking|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+
+// Targets: [native]
+final fun <#A: kotlin/Any?> kotlinx.coroutines/runBlocking(kotlin.coroutines/CoroutineContext = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): #A // kotlinx.coroutines/runBlocking|runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+
+// Targets: [native]
+final fun kotlinx.coroutines/newFixedThreadPoolContext(kotlin/Int, kotlin/String): kotlinx.coroutines/CloseableCoroutineDispatcher // kotlinx.coroutines/newFixedThreadPoolContext|newFixedThreadPoolContext(kotlin.Int;kotlin.String){}[0]
+
+// Targets: [native]
+final fun kotlinx.coroutines/newSingleThreadContext(kotlin/String): kotlinx.coroutines/CloseableCoroutineDispatcher // kotlinx.coroutines/newSingleThreadContext|newSingleThreadContext(kotlin.String){}[0]
+
+// Targets: [js]
+final fun (org.w3c.dom/Window).kotlinx.coroutines/asCoroutineDispatcher(): kotlinx.coroutines/CoroutineDispatcher // kotlinx.coroutines/asCoroutineDispatcher|[email protected](){}[0]
+
+// Targets: [js]
+final fun <#A: kotlin/Any?> (kotlin.js/Promise<#A>).kotlinx.coroutines/asDeferred(): kotlinx.coroutines/Deferred<#A> // kotlinx.coroutines/asDeferred|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+
+// Targets: [js]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/promise(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlin.js/Promise<#A> // kotlinx.coroutines/promise|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+
+// Targets: [js]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/Deferred<#A>).kotlinx.coroutines/asPromise(): kotlin.js/Promise<#A> // kotlinx.coroutines/asPromise|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+
+// Targets: [js]
+final suspend fun (org.w3c.dom/Window).kotlinx.coroutines/awaitAnimationFrame(): kotlin/Double // kotlinx.coroutines/awaitAnimationFrame|[email protected](){}[0]
+
+// Targets: [js]
+final suspend fun <#A: kotlin/Any?> (kotlin.js/Promise<#A>).kotlinx.coroutines/await(): #A // kotlinx.coroutines/await|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+
+// Targets: [wasmJs]
+final fun <#A: kotlin/Any?> (kotlin.js/Promise<kotlin.js/JsAny?>).kotlinx.coroutines/asDeferred(): kotlinx.coroutines/Deferred<#A> // kotlinx.coroutines/asDeferred|[email protected]<kotlin.js.JsAny?>(){0§<kotlin.Any?>}[0]
+
+// Targets: [wasmJs]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/CoroutineScope).kotlinx.coroutines/promise(kotlin.coroutines/CoroutineContext = ..., kotlinx.coroutines/CoroutineStart = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines/CoroutineScope, #A>): kotlin.js/Promise<kotlin.js/JsAny?> // kotlinx.coroutines/promise|[email protected](kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}[0]
+
+// Targets: [wasmJs]
+final fun <#A: kotlin/Any?> (kotlinx.coroutines/Deferred<#A>).kotlinx.coroutines/asPromise(): kotlin.js/Promise<kotlin.js/JsAny?> // kotlinx.coroutines/asPromise|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+
+// Targets: [wasmJs]
+final suspend fun <#A: kotlin/Any?> (kotlin.js/Promise<kotlin.js/JsAny?>).kotlinx.coroutines/await(): #A // kotlinx.coroutines/await|[email protected]<kotlin.js.JsAny?>(){0§<kotlin.Any?>}[0]
diff --git a/kotlinx-coroutines-core/build.gradle.kts b/kotlinx-coroutines-core/build.gradle.kts
index 0c6f65c..9941dcc 100644
--- a/kotlinx-coroutines-core/build.gradle.kts
+++ b/kotlinx-coroutines-core/build.gradle.kts
@@ -18,10 +18,13 @@
Configure source sets structure for kotlinx-coroutines-core:
TARGETS SOURCE SETS
- ------- ----------------------------------------------
- wasmJs \----------> jsAndWasmShared --------------------+
- js / |
- V
+ ------------------------------------------------------------
+ wasmJs \------> jsAndWasmJsShared ----+
+ js / |
+ V
+ wasmWasi --------------------> jsAndWasmShared ----------+
+ |
+ V
jvmCore\ --------> jvm ---------> concurrent -------> common
jdk8 / ^
|
@@ -92,13 +95,8 @@
* All new MM targets are build with optimize = true to have stress tests properly run.
*/
targets.withType(KotlinNativeTargetWithTests::class).configureEach {
- binaries.getTest(DEBUG).apply {
- optimized = true
- }
-
binaries.test("workerTest", listOf(DEBUG)) {
val thisTest = this
- optimized = true
freeCompilerArgs = freeCompilerArgs + listOf("-e", "kotlinx.coroutines.mainBackground")
testRuns.create("workerTest") {
this as KotlinTaskTestRun<*, *>
@@ -164,13 +162,6 @@
minHeapSize = "1g"
maxHeapSize = "1g"
enableAssertions = true
- if (!Idea.active) {
- // We should not set this security manager when `jvmTest`
- // is invoked by IntelliJ IDEA since we need to pass
- // system properties for Lincheck and stress tests.
- // TODO Remove once IDEA is smart enough to select between `jvmTest`/`jvmStressTest`/`jvmLincheckTest` #KTIJ-599
- systemProperty("java.security.manager", "kotlinx.coroutines.TestSecurityManager")
- }
// 'stress' is required to be able to run all subpackage tests like ":jvmTests --tests "*channels*" -Pstress=true"
if (!Idea.active && rootProject.properties["stress"] == null) {
exclude("**/*LincheckTest*")
diff --git a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt
index 56e9608..d2f79c1 100644
--- a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt
+++ b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt
@@ -30,6 +30,7 @@
*
* @suppress **This an internal API and should not be used from general code.**
*/
+@OptIn(InternalForInheritanceCoroutinesApi::class)
@InternalCoroutinesApi
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
diff --git a/kotlinx-coroutines-core/common/src/Annotations.kt b/kotlinx-coroutines-core/common/src/Annotations.kt
index b35da67..5fcca46 100644
--- a/kotlinx-coroutines-core/common/src/Annotations.kt
+++ b/kotlinx-coroutines-core/common/src/Annotations.kt
@@ -80,6 +80,7 @@
* `kotlinx.coroutines`, because their signatures and semantics will change between future releases without any
* warnings and without providing any migration aids.
*/
+@MustBeDocumented
@Retention(value = AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, AnnotationTarget.PROPERTY)
@RequiresOptIn(
@@ -89,3 +90,29 @@
"so stable API could be provided instead"
)
public annotation class InternalCoroutinesApi
+
+/**
+ * Marks declarations that cannot be safely inherited from.
+ */
+@Target(AnnotationTarget.CLASS)
+@RequiresOptIn(
+ level = RequiresOptIn.Level.WARNING, message =
+ "Inheriting from this kotlinx.coroutines API is unstable. " +
+ "Either new methods may be added in the future, which would break the inheritance, " +
+ "or correctly inheriting from it requires fulfilling contracts that may change in the future."
+)
+public annotation class ExperimentalForInheritanceCoroutinesApi
+
+/**
+ * Marks declarations that cannot be safely inherited from.
+ */
+@Target(AnnotationTarget.CLASS)
+@RequiresOptIn(
+ level = RequiresOptIn.Level.WARNING, message =
+ "This is a kotlinx.coroutines API that is not intended to be inherited from, " +
+ "as the library may handle predefined instances of this in a special manner. " +
+ "This will be an error in a future release. " +
+ "If you need to inherit from this, please describe your use case in " +
+ "https://github.com/Kotlin/kotlinx.coroutines/issues, so that we can provide a stable API for inheritance. "
+)
+public annotation class InternalForInheritanceCoroutinesApi
diff --git a/kotlinx-coroutines-core/common/src/Await.kt b/kotlinx-coroutines-core/common/src/Await.kt
index b8a76f1..845f01e 100644
--- a/kotlinx-coroutines-core/common/src/Await.kt
+++ b/kotlinx-coroutines-core/common/src/Await.kt
@@ -99,6 +99,8 @@
var disposer: DisposeHandlersOnCancel?
get() = _disposer.value
set(value) { _disposer.value = value }
+
+ override val onCancelling get() = false
override fun invoke(cause: Throwable?) {
if (cause != null) {
diff --git a/kotlinx-coroutines-core/common/src/Builders.common.kt b/kotlinx-coroutines-core/common/src/Builders.common.kt
index 95c1be8..265d908 100644
--- a/kotlinx-coroutines-core/common/src/Builders.common.kt
+++ b/kotlinx-coroutines-core/common/src/Builders.common.kt
@@ -88,6 +88,7 @@
return coroutine
}
+@OptIn(InternalForInheritanceCoroutinesApi::class)
@Suppress("UNCHECKED_CAST")
private open class DeferredCoroutine<T>(
parentContext: CoroutineContext,
@@ -214,16 +215,13 @@
private const val RESUMED = 2
// Used by withContext when context dispatcher changes
-@PublishedApi
-internal class DispatchedCoroutine<in T> internal constructor(
+internal class DispatchedCoroutine<in T>(
context: CoroutineContext,
uCont: Continuation<T>
) : ScopeCoroutine<T>(context, uCont) {
// this is copy-and-paste of a decision state machine inside AbstractionContinuation
// todo: we may some-how abstract it via inline class
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
- @JvmField
- public val _decision = atomic(UNDECIDED)
+ private val _decision = atomic(UNDECIDED)
private fun trySuspend(): Boolean {
_decision.loop { decision ->
diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt
index 4e3fc8a..e67e7be 100644
--- a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt
+++ b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt
@@ -41,6 +41,8 @@
* +-----------+
* ```
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(InternalForInheritanceCoroutinesApi::class)
public interface CancellableContinuation<in T> : Continuation<T> {
/**
* Returns `true` when this continuation is active -- it has not completed or cancelled yet.
@@ -74,16 +76,21 @@
public fun tryResume(value: T, idempotent: Any? = null): Any?
/**
- * Same as [tryResume] but with [onCancellation] handler that called if and only if the value is not
- * delivered to the caller because of the dispatch in the process, so that atomicity delivery
- * guaranteed can be provided by having a cancellation fallback.
+ * Same as [tryResume] but with an [onCancellation] handler that is called if and only if the value is not
+ * delivered to the caller because of the dispatch in the process.
+ *
+ * The purpose of this function is to enable atomic delivery guarantees: either resumption succeeded, passing
+ * the responsibility for [value] to the continuation, or the [onCancellation] block will be invoked,
+ * allowing one to free the resources in [value].
*
* Implementation note: current implementation always returns RESUME_TOKEN or `null`
*
* @suppress **This is unstable API and it is subject to change.**
*/
@InternalCoroutinesApi
- public fun tryResume(value: T, idempotent: Any?, onCancellation: ((cause: Throwable) -> Unit)?): Any?
+ public fun <R: T> tryResume(
+ value: R, idempotent: Any?, onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?
+ ): Any?
/**
* Tries to resume this continuation with the specified [exception] and returns a non-null object token if successful,
@@ -126,7 +133,7 @@
* Otherwise, the handler will be invoked as soon as this continuation is cancelled.
*
* The installed [handler] should not throw any exceptions.
- * If it does, they will get caught, wrapped into a [CompletionHandlerException] and
+ * If it does, they will get caught, wrapped into a `CompletionHandlerException` and
* processed as an uncaught exception in the context of the current coroutine
* (see [CoroutineExceptionHandler]).
*
@@ -168,27 +175,50 @@
@ExperimentalCoroutinesApi
public fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable)
+ /** @suppress */
+ @Deprecated(
+ "Use the overload that also accepts the `value` and the coroutine context in lambda",
+ level = DeprecationLevel.WARNING,
+ replaceWith = ReplaceWith("resume(value) { cause, _, _ -> onCancellation(cause) }")
+ ) // warning since 1.9.0, was experimental
+ public fun resume(value: T, onCancellation: ((cause: Throwable) -> Unit)?)
+
/**
- * Resumes this continuation with the specified `value` and calls the specified `onCancellation`
- * handler when either resumed too late (when continuation was already cancelled) or, although resumed
- * successfully (before cancellation), the coroutine's job was cancelled before it had a
- * chance to run in its dispatcher, so that the suspended function threw an exception
- * instead of returning this value.
+ * Resumes this continuation with the specified [value], calling the specified [onCancellation] if and only if
+ * the [value] was not successfully used to resume the continuation.
+ *
+ * The [value] can be rejected in two cases (in both of which [onCancellation] will be called):
+ * - Cancellation happened before the handler was resumed;
+ * - The continuation was resumed successfully (before cancellation), but the coroutine's job was cancelled before
+ * it had a chance to run in its dispatcher, and so the suspended function threw an exception instead of returning
+ * this value.
*
* The installed [onCancellation] handler should not throw any exceptions.
- * If it does, they will get caught, wrapped into a [CompletionHandlerException] and
+ * If it does, they will get caught, wrapped into a `CompletionHandlerException`, and
* processed as an uncaught exception in the context of the current coroutine
* (see [CoroutineExceptionHandler]).
*
- * This function shall be used when resuming with a resource that must be closed by
- * code that called the corresponding suspending function, for example:
+ * With this version of [resume], it's possible to pass resources that can not simply be left for the garbage
+ * collector (like file handles, sockets, etc.) and need to be closed explicitly:
*
* ```
- * continuation.resume(resource) {
- * resource.close()
+ * continuation.resume(resourceToResumeWith) { _, resourceToClose, _ ->
+ * resourceToClose.close()
* }
* ```
*
+ * [onCancellation] accepts three arguments:
+ *
+ * - `cause: Throwable` is the exception with which the continuation was cancelled.
+ * - `value` is exactly the same as the [value] passed to [resume] itself.
+ * In the example above, `resourceToResumeWith` is exactly the same as `resourceToClose`; in particular,
+ * one could call `resourceToResumeWith.close()` in the lambda for the same effect.
+ * The reason to reference `resourceToClose` anyway is to avoid a memory allocation due to the lambda
+ * capturing the `resourceToResumeWith` reference.
+ * - `context` is the [context] of this continuation.
+ * Like with `value`, the reason this is available as a lambda parameter, even though it is always possible to
+ * call [context] from the lambda instead, is to allow lambdas to capture less of their environment.
+ *
* A more complete example and further details are given in
* the documentation for the [suspendCancellableCoroutine] function.
*
@@ -196,8 +226,9 @@
* It can be invoked concurrently with the surrounding code.
* There is no guarantee on the execution context of its invocation.
*/
- @ExperimentalCoroutinesApi // since 1.2.0
- public fun resume(value: T, onCancellation: ((cause: Throwable) -> Unit)?)
+ public fun <R: T> resume(
+ value: R, onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?
+ )
}
/**
@@ -274,11 +305,11 @@
*
* ```
* suspendCancellableCoroutine { continuation ->
- * val resource = openResource() // Opens some resource
- * continuation.invokeOnCancellation {
- * resource.close() // Ensures the resource is closed on cancellation
- * }
- * // ...
+ * val resource = openResource() // Opens some resource
+ * continuation.invokeOnCancellation {
+ * resource.close() // Ensures the resource is closed on cancellation
+ * }
+ * // ...
* }
* ```
*
@@ -293,8 +324,10 @@
* override fun onCompleted(resource: T) {
* // Resume coroutine with a value provided by the callback and ensure the resource is closed in case
* // when the coroutine is cancelled before the caller gets a reference to the resource.
- * continuation.resume(resource) {
- * resource.close() // Close the resource on cancellation
+ * continuation.resume(resource) { cause, resourceToClose, context ->
+ * resourceToClose.close() // Close the resource on cancellation
+ * // If we used `resource` instead of `resourceToClose`, this lambda would need to allocate a closure,
+ * // but with `resourceToClose`, the lambda does not capture any of its environment.
* }
* }
* // ...
diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
index b7c6111..c768a6e 100644
--- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
+++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
@@ -25,6 +25,7 @@
/**
* @suppress **This is unstable API and it is subject to change.**
*/
+@OptIn(InternalForInheritanceCoroutinesApi::class)
@PublishedApi
internal open class CancellableContinuationImpl<in T>(
final override val delegate: Continuation<T>,
@@ -146,7 +147,7 @@
assert { parentHandle !== NonDisposableHandle }
val state = _state.value
assert { state !is NotCompleted }
- if (state is CompletedContinuation && state.idempotentResume != null) {
+ if (state is CompletedContinuation<*> && state.idempotentResume != null) {
// Cannot reuse continuation that was resumed with idempotent marker
detachChild()
return false
@@ -169,7 +170,7 @@
when (state) {
is NotCompleted -> error("Not completed")
is CompletedExceptionally -> return // already completed exception or cancelled, nothing to do
- is CompletedContinuation -> {
+ is CompletedContinuation<*> -> {
check(!state.cancelled) { "Must be called at most once" }
val update = state.copy(cancelCause = cause)
if (_state.compareAndSet(state, update)) {
@@ -234,13 +235,6 @@
}
}
- private fun callCancelHandler(handler: InternalCompletionHandler, cause: Throwable?) =
- /*
- * :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
- * because we play type tricks on Kotlin/JS and handler is not necessarily a function there
- */
- callCancelHandlerSafely { handler.invoke(cause) }
-
fun callCancelHandler(handler: CancelHandler, cause: Throwable?) =
callCancelHandlerSafely { handler.invoke(cause) }
@@ -250,9 +244,13 @@
callCancelHandlerSafely { segment.onCancellation(index, cause, context) }
}
- fun callOnCancellation(onCancellation: (cause: Throwable) -> Unit, cause: Throwable) {
+ fun <R> callOnCancellation(
+ onCancellation: (cause: Throwable, value: R, context: CoroutineContext) -> Unit,
+ cause: Throwable,
+ value: R
+ ) {
try {
- onCancellation.invoke(cause)
+ onCancellation.invoke(cause, value, context)
} catch (ex: Throwable) {
// Handler should never fail, if it does -- it is an unhandled exception
handleCoroutineException(
@@ -341,10 +339,7 @@
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)
- )
+ val handle = parent.invokeOnCompletion(handler = ChildContinuation(this))
_parentHandle.compareAndSet(null, handle)
return handle
}
@@ -363,7 +358,14 @@
override fun resumeWith(result: Result<T>) =
resumeImpl(result.toState(this), resumeMode)
+ @Suppress("OVERRIDE_DEPRECATION")
override fun resume(value: T, onCancellation: ((cause: Throwable) -> Unit)?) =
+ resumeImpl(value, resumeMode, onCancellation?.let { { cause, _, _ -> onCancellation(cause) } })
+
+ override fun <R : T> resume(
+ value: R,
+ onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?
+ ) =
resumeImpl(value, resumeMode, onCancellation)
/**
@@ -376,7 +378,7 @@
*
* ```
* invokeOnCancellation { cause ->
- * segment.onCancellation(index, cause)
+ * segment.onCancellation(index, cause)
* }
* ```
*/
@@ -390,7 +392,8 @@
invokeOnCancellationImpl(segment)
}
- override fun invokeOnCancellation(handler: CompletionHandler) = invokeOnCancellation(CancelHandler.UserSupplied(handler))
+ override fun invokeOnCancellation(handler: CompletionHandler) =
+ invokeOnCancellation(CancelHandler.UserSupplied(handler))
internal fun invokeOnCancellationInternal(handler: CancelHandler) = invokeOnCancellationImpl(handler)
@@ -425,7 +428,8 @@
}
return
}
- is CompletedContinuation -> {
+
+ is CompletedContinuation<*> -> {
/*
* Continuation was already completed, and might already have cancel handler.
*/
@@ -466,11 +470,11 @@
dispatch(mode)
}
- private fun resumedState(
+ private fun <R> resumedState(
state: NotCompleted,
- proposedUpdate: Any?,
+ proposedUpdate: R,
resumeMode: Int,
- onCancellation: ((cause: Throwable) -> Unit)?,
+ onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?,
idempotent: Any?
): Any? = when {
proposedUpdate is CompletedExceptionally -> {
@@ -486,10 +490,10 @@
else -> proposedUpdate // simple case -- use the value directly
}
- private fun resumeImpl(
- proposedUpdate: Any?,
+ internal fun <R> resumeImpl(
+ proposedUpdate: R,
resumeMode: Int,
- onCancellation: ((cause: Throwable) -> Unit)? = null
+ onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)? = null
) {
_state.loop { state ->
when (state) {
@@ -500,6 +504,7 @@
dispatchResume(resumeMode) // dispatch resume, but it might get cancelled in process
return // done
}
+
is CancelledContinuation -> {
/*
* If continuation was cancelled, then resume attempt must be ignored,
@@ -508,7 +513,7 @@
*/
if (state.makeResumed()) { // check if trying to resume one (otherwise error)
// call onCancellation
- onCancellation?.let { callOnCancellation(it, state.cause) }
+ onCancellation?.let { callOnCancellation(it, state.cause, proposedUpdate) }
return // done
}
}
@@ -521,10 +526,10 @@
* Similar to [tryResume], but does not actually completes resume (needs [completeResume] call).
* Returns [RESUME_TOKEN] when resumed, `null` when it was already resumed or cancelled.
*/
- private fun tryResumeImpl(
- proposedUpdate: Any?,
+ private fun <R> tryResumeImpl(
+ proposedUpdate: R,
idempotent: Any?,
- onCancellation: ((cause: Throwable) -> Unit)?
+ onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?
): Symbol? {
_state.loop { state ->
when (state) {
@@ -534,7 +539,7 @@
detachChildIfNonResuable()
return RESUME_TOKEN
}
- is CompletedContinuation -> {
+ is CompletedContinuation<*> -> {
return if (idempotent != null && state.idempotentResume === idempotent) {
assert { state.result == proposedUpdate } // "Non-idempotent resume"
RESUME_TOKEN // resumed with the same token -- ok
@@ -570,7 +575,11 @@
override fun tryResume(value: T, idempotent: Any?): Any? =
tryResumeImpl(value, idempotent, onCancellation = null)
- override fun tryResume(value: T, idempotent: Any?, onCancellation: ((cause: Throwable) -> Unit)?): Any? =
+ override fun <R : T> tryResume(
+ value: R,
+ idempotent: Any?,
+ onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?
+ ): Any? =
tryResumeImpl(value, idempotent, onCancellation)
override fun tryResumeWithException(exception: Throwable): Any? =
@@ -595,7 +604,7 @@
@Suppress("UNCHECKED_CAST")
override fun <T> getSuccessfulResult(state: Any?): T =
when (state) {
- is CompletedContinuation -> state.result as T
+ is CompletedContinuation<*> -> state.result as T
else -> state as T
}
@@ -627,8 +636,6 @@
* as seen from the debugger.
* Use [UserSupplied] to create an instance from a lambda.
* We can't avoid defining a separate type, because on JS, you can't inherit from a function type.
- *
- * @see InternalCompletionHandler for a very similar interface, but used for handling completion and not cancellation.
*/
internal interface CancelHandler : NotCompleted {
/**
@@ -664,10 +671,12 @@
}
// Completed with additional metadata
-private data class CompletedContinuation(
- @JvmField val result: Any?,
- @JvmField val cancelHandler: CancelHandler? = null, // installed via invokeOnCancellation
- @JvmField val onCancellation: ((cause: Throwable) -> Unit)? = null, // installed via resume block
+private data class CompletedContinuation<R>(
+ @JvmField val result: R,
+ // installed via `invokeOnCancellation`
+ @JvmField val cancelHandler: CancelHandler? = null,
+ // installed via the `resume` block
+ @JvmField val onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)? = null,
@JvmField val idempotentResume: Any? = null,
@JvmField val cancelCause: Throwable? = null
) {
@@ -675,6 +684,17 @@
fun invokeHandlers(cont: CancellableContinuationImpl<*>, cause: Throwable) {
cancelHandler?.let { cont.callCancelHandler(it, cause) }
- onCancellation?.let { cont.callOnCancellation(it, cause) }
+ onCancellation?.let { cont.callOnCancellation(it, cause, result) }
+ }
+}
+
+// Same as ChildHandleNode, but for cancellable continuation
+private class ChildContinuation(
+ @JvmField val child: CancellableContinuationImpl<*>
+) : JobNode() {
+ override val onCancelling get() = true
+
+ override fun invoke(cause: Throwable?) {
+ child.parentCancelled(child.getContinuationCancellationCause(job))
}
}
diff --git a/kotlinx-coroutines-core/common/src/CloseableCoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/CloseableCoroutineDispatcher.kt
index 1ed0eb1..cc56121 100644
--- a/kotlinx-coroutines-core/common/src/CloseableCoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/common/src/CloseableCoroutineDispatcher.kt
@@ -11,7 +11,7 @@
* might be added to this interface in the future, but is stable for use.
*/
@ExperimentalCoroutinesApi
-public expect abstract class CloseableCoroutineDispatcher() : CoroutineDispatcher {
+public expect abstract class CloseableCoroutineDispatcher() : CoroutineDispatcher, AutoCloseable {
/**
* Initiate the closing sequence of the coroutine dispatcher.
@@ -20,5 +20,5 @@
*
* Invocations of `close` are idempotent and thread-safe.
*/
- public abstract fun close()
+ public abstract override fun close()
}
diff --git a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt
index abb6aea..53c3da8 100644
--- a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt
+++ b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt
@@ -15,10 +15,9 @@
*
* All functions on this interface are **thread-safe** and can
* be safely invoked from concurrent coroutines without external synchronization.
- *
- * **The `CompletableDeferred` interface is not stable for inheritance in 3rd party libraries**,
- * as new methods might be added to this interface in the future, but is stable for use.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(markerClass = InternalForInheritanceCoroutinesApi::class)
public interface CompletableDeferred<T> : Deferred<T> {
/**
* Completes this deferred value with a given [value]. The result is `true` if this deferred was
@@ -73,6 +72,7 @@
/**
* Concrete implementation of [CompletableDeferred].
*/
+@OptIn(InternalForInheritanceCoroutinesApi::class)
@Suppress("UNCHECKED_CAST")
private class CompletableDeferredImpl<T>(
parent: Job?
diff --git a/kotlinx-coroutines-core/common/src/CompletableJob.kt b/kotlinx-coroutines-core/common/src/CompletableJob.kt
index f3ac8dc..911f75f 100644
--- a/kotlinx-coroutines-core/common/src/CompletableJob.kt
+++ b/kotlinx-coroutines-core/common/src/CompletableJob.kt
@@ -10,6 +10,8 @@
* **The `CompletableJob` interface is not stable for inheritance in 3rd party libraries**,
* as new methods might be added to this interface in the future, but is stable for use.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(markerClass = InternalForInheritanceCoroutinesApi::class)
public interface CompletableJob : Job {
/**
* Completes this job. The result is `true` if this job was completed as a result of this invocation and
diff --git a/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt b/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt
index 0a0176e..ea56b15 100644
--- a/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt
+++ b/kotlinx-coroutines-core/common/src/CompletionHandler.common.kt
@@ -25,47 +25,3 @@
*/
// TODO: deprecate. This doesn't seem better than a simple function type.
public typealias CompletionHandler = (cause: Throwable?) -> Unit
-
-/**
- * Essentially the same as just a function from `Throwable?` to `Unit`.
- * The only thing implementors can do is call [invoke].
- * The reason this abstraction exists is to allow providing a readable [toString] in the list of completion handlers
- * as seen from the debugger.
- * Use [UserSupplied] to create an instance from a lambda.
- * We can't avoid defining a separate type, because on JS, you can't inherit from a function type.
- *
- * @see CancelHandler for a very similar interface, but used for handling cancellation and not completion.
- */
-internal interface InternalCompletionHandler {
- /**
- * Signals completion.
- *
- * This function:
- * - Does not throw any exceptions.
- * For [Job] instances that are coroutines, exceptions thrown by this function will be caught, wrapped into
- * [CompletionHandlerException], and passed to [handleCoroutineException], but for those that are not coroutines,
- * they will just be rethrown, potentially crashing unrelated code.
- * - Is fast, non-blocking, and thread-safe.
- * - Can be invoked concurrently with the surrounding code.
- * - Can be invoked from any context.
- *
- * The meaning of `cause` that is passed to the handler is:
- * - It is `null` if the job has completed normally.
- * - It is an instance of [CancellationException] if the job was cancelled _normally_.
- * **It should not be treated as an error**. In particular, it should not be reported to error logs.
- * - Otherwise, the job had _failed_.
- */
- fun invoke(cause: Throwable?)
-
- /**
- * A lambda passed from outside the coroutine machinery.
- *
- * See the requirements for [InternalCompletionHandler.invoke] when implementing this function.
- */
- class UserSupplied(private val handler: (cause: Throwable?) -> Unit) : InternalCompletionHandler {
- /** @suppress */
- override fun invoke(cause: Throwable?) { handler(cause) }
-
- override fun toString() = "InternalCompletionHandler.UserSupplied[${handler.classSimpleName}@$hexAddress]"
- }
-}
diff --git a/kotlinx-coroutines-core/common/src/CompletionState.kt b/kotlinx-coroutines-core/common/src/CompletionState.kt
index b9fefa5..1b05bdb 100644
--- a/kotlinx-coroutines-core/common/src/CompletionState.kt
+++ b/kotlinx-coroutines-core/common/src/CompletionState.kt
@@ -5,17 +5,10 @@
import kotlin.coroutines.*
import kotlin.jvm.*
-internal fun <T> Result<T>.toState(
- onCancellation: ((cause: Throwable) -> Unit)? = null
-): Any? = fold(
- onSuccess = { if (onCancellation != null) CompletedWithCancellation(it, onCancellation) else it },
- onFailure = { CompletedExceptionally(it) }
-)
+internal fun <T> Result<T>.toState(): Any? = getOrElse { CompletedExceptionally(it) }
-internal fun <T> Result<T>.toState(caller: CancellableContinuation<*>): Any? = fold(
- onSuccess = { it },
- onFailure = { CompletedExceptionally(recoverStackTrace(it, caller)) }
-)
+internal fun <T> Result<T>.toState(caller: CancellableContinuation<*>): Any? =
+ getOrElse { CompletedExceptionally(recoverStackTrace(it, caller)) }
@Suppress("RESULT_CLASS_IN_RETURN_TYPE", "UNCHECKED_CAST")
internal fun <T> recoverResult(state: Any?, uCont: Continuation<T>): Result<T> =
@@ -24,11 +17,6 @@
else
Result.success(state as T)
-internal data class CompletedWithCancellation(
- @JvmField val result: Any?,
- @JvmField val onCancellation: (cause: Throwable) -> Unit
-)
-
/**
* Class for an internal state of a job that was cancelled (completed exceptionally).
*
diff --git a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt
index e9d9f19..37b6876 100644
--- a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt
@@ -6,23 +6,56 @@
/**
* Base class to be extended by all coroutine dispatcher implementations.
*
+ * If `kotlinx-coroutines` is used, it is recommended to avoid [ContinuationInterceptor] instances that are not
+ * [CoroutineDispatcher] implementations, as [CoroutineDispatcher] ensures that the
+ * debugging facilities in the [newCoroutineContext] function work properly.
+ *
+ * ## Predefined dispatchers
+ *
* The following standard implementations are provided by `kotlinx.coroutines` as properties on
* the [Dispatchers] object:
*
- * - [Dispatchers.Default] — is used by all standard builders if no dispatcher or any other [ContinuationInterceptor]
- * is specified in their context. It uses a common pool of shared background threads.
+ * - [Dispatchers.Default] is used by all standard builders if no dispatcher or any other [ContinuationInterceptor]
+ * is specified in their context.
+ * It uses a common pool of shared background threads.
* This is an appropriate choice for compute-intensive coroutines that consume CPU resources.
- * - [Dispatchers.IO] — uses a shared pool of on-demand created threads and is designed for offloading of IO-intensive _blocking_
+ * - `Dispatchers.IO` (available on the JVM and Native targets)
+ * uses a shared pool of on-demand created threads and is designed for offloading of IO-intensive _blocking_
* operations (like file I/O and blocking socket I/O).
- * - [Dispatchers.Unconfined] — starts coroutine execution in the current call-frame until the first suspension,
- * whereupon the coroutine builder function returns.
- * The coroutine will later resume in whatever thread used by the
- * corresponding suspending function, without confining it to any specific thread or pool.
+ * - [Dispatchers.Main] represents the UI thread if one is available.
+ * - [Dispatchers.Unconfined] starts coroutine execution in the current call-frame until the first suspension,
+ * at which point the coroutine builder function returns.
+ * When the coroutine is resumed, the thread from which it is resumed will run the coroutine code until the next
+ * suspension, and so on.
* **The `Unconfined` dispatcher should not normally be used in code**.
- * - Private thread pools can be created with [newSingleThreadContext] and [newFixedThreadPoolContext].
- * - An arbitrary [Executor][java.util.concurrent.Executor] can be converted to a dispatcher with the [asCoroutineDispatcher] extension function.
+ * - Calling [limitedParallelism] on any dispatcher creates a view of the dispatcher that limits the parallelism
+ * to the given value.
+ * This allows creating private thread pools without spawning new threads.
+ * For example, `Dispatchers.IO.limitedParallelism(4)` creates a dispatcher that allows running at most
+ * 4 tasks in parallel, reusing the existing IO dispatcher threads.
+ * - When thread pools completely separate from [Dispatchers.Default] and [Dispatchers.IO] are required,
+ * they can be created with `newSingleThreadContext` and `newFixedThreadPoolContext` on the JVM and Native targets.
+ * - An arbitrary `java.util.concurrent.Executor` can be converted to a dispatcher with the
+ * `asCoroutineDispatcher` extension function.
*
- * This class ensures that debugging facilities in [newCoroutineContext] function work properly.
+ * ## Dispatch procedure
+ *
+ * Typically, a dispatch procedure is performed as follows:
+ *
+ * - First, [isDispatchNeeded] is invoked to determine whether the coroutine should be dispatched
+ * or is already in the right context.
+ * - If [isDispatchNeeded] returns `true`, the coroutine is dispatched using the [dispatch] method.
+ * It may take a while for the dispatcher to start the task,
+ * but the [dispatch] method itself may return immediately, before the task has even begun to execute.
+ * - If no dispatch is needed (which is the case for [Dispatchers.Main.immediate][MainCoroutineDispatcher.immediate]
+ * when already on the main thread and for [Dispatchers.Unconfined]),
+ * [dispatch] is typically not called,
+ * and the coroutine is resumed in the thread performing the dispatch procedure,
+ * forming an event loop to prevent stack overflows.
+ * See [Dispatchers.Unconfined] for a description of event loops.
+ *
+ * This behavior may be different on the very first dispatch procedure for a given coroutine, depending on the
+ * [CoroutineStart] parameter of the coroutine builder.
*/
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
@@ -65,14 +98,66 @@
/**
* Creates a view of the current dispatcher that limits the parallelism to the given [value][parallelism].
- * The resulting view uses the original dispatcher for execution, but with the guarantee that
+ * The resulting view uses the original dispatcher for execution but with the guarantee that
* no more than [parallelism] coroutines are executed at the same time.
*
* This method does not impose restrictions on the number of views or the total sum of parallelism values,
* each view controls its own parallelism independently with the guarantee that the effective parallelism
* of all views cannot exceed the actual parallelism of the original dispatcher.
*
- * ### Limitations
+ * The resulting dispatcher does not guarantee that the coroutines will always be dispatched on the same
+ * subset of threads, it only guarantees that at most [parallelism] coroutines are executed at the same time,
+ * and reuses threads from the original dispatchers.
+ * It does not constitute a resource -- it is a _view_ of the underlying dispatcher that can be thrown away
+ * and is not required to be closed.
+ *
+ * ### Example of usage
+ * ```
+ * // Background dispatcher for the application
+ * val dispatcher = newFixedThreadPoolContext(4, "App Background")
+ * // At most 2 threads will be processing images as it is really slow and CPU-intensive
+ * val imageProcessingDispatcher = dispatcher.limitedParallelism(2, "Image processor")
+ * // At most 3 threads will be processing JSON to avoid image processing starvation
+ * val jsonProcessingDispatcher = dispatcher.limitedParallelism(3, "Json processor")
+ * // At most 1 thread will be doing IO
+ * val fileWriterDispatcher = dispatcher.limitedParallelism(1, "File writer")
+ * ```
+ * Note how in this example the application has an executor with 4 threads, but the total sum of all limits
+ * is 6. Still, at most 4 coroutines can be executed simultaneously as each view limits only its own parallelism,
+ * and at most 4 threads can exist in the system.
+ *
+ * Note that this example was structured in such a way that it illustrates the parallelism guarantees.
+ * In practice, it is usually better to use `Dispatchers.IO` or [Dispatchers.Default] instead of creating a
+ * `backgroundDispatcher`.
+ *
+ * ### `limitedParallelism(1)` pattern
+ *
+ * One of the common patterns is confining the execution of specific tasks to a sequential execution in background
+ * with `limitedParallelism(1)` invocation.
+ * For that purpose, the implementation guarantees that tasks are executed sequentially and that a happens-before relation
+ * is established between them:
+ *
+ * ```
+ * val confined = Dispatchers.Default.limitedParallelism(1, "incrementDispatcher")
+ * var counter = 0
+ *
+ * // Invoked from arbitrary coroutines
+ * launch(confined) {
+ * // This increment is sequential and race-free
+ * ++counter
+ * }
+ * ```
+ * Note that there is no guarantee that the underlying system thread will always be the same.
+ *
+ * ### Dispatchers.IO
+ *
+ * `Dispatcher.IO` is considered _elastic_ for the purposes of limited parallelism -- the sum of
+ * views is not restricted by the capacity of `Dispatchers.IO`.
+ * It means that it is safe to replace `newFixedThreadPoolContext(nThreads)` with
+ * `Dispatchers.IO.limitedParallelism(nThreads)` w.r.t. available number of threads.
+ * See `Dispatchers.IO` documentation for more details.
+ *
+ * ### Restrictions and implementation details
*
* The default implementation of `limitedParallelism` does not support direct dispatchers,
* such as executing the given runnable in place during [dispatch] calls.
@@ -80,29 +165,26 @@
* For direct dispatchers, it is recommended to override this method
* and provide a domain-specific implementation or to throw an [UnsupportedOperationException].
*
- * ### Example of usage
- * ```
- * private val backgroundDispatcher = newFixedThreadPoolContext(4, "App Background")
- * // At most 2 threads will be processing images as it is really slow and CPU-intensive
- * private val imageProcessingDispatcher = backgroundDispatcher.limitedParallelism(2)
- * // At most 3 threads will be processing JSON to avoid image processing starvation
- * private val jsonProcessingDispatcher = backgroundDispatcher.limitedParallelism(3)
- * // At most 1 thread will be doing IO
- * private val fileWriterDispatcher = backgroundDispatcher.limitedParallelism(1)
- * ```
- * Note how in this example the application has an executor with 4 threads, but the total sum of all limits
- * is 6. Still, at most 4 coroutines can be executed simultaneously as each view limits only its own parallelism.
+ * Implementations of this method are allowed to return `this` if the current dispatcher already satisfies the parallelism requirement.
+ * For example, `Dispatchers.Main.limitedParallelism(1)` returns `Dispatchers.Main`, because the main dispatcher is already single-threaded.
*
- * Note that this example was structured in such a way that it illustrates the parallelism guarantees.
- * In practice, it is usually better to use [Dispatchers.IO] or [Dispatchers.Default] instead of creating a
- * `backgroundDispatcher`. It is both possible and advised to call `limitedParallelism` on them.
+ * @param name optional name for the resulting dispatcher string representation if a new dispatcher was created.
+ * Implementations are free to ignore this parameter.
+ * @throws IllegalArgumentException if the given [parallelism] is non-positive
+ * @throws UnsupportedOperationException if the current dispatcher does not support limited parallelism views
*/
- @ExperimentalCoroutinesApi
- public open fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ public open fun limitedParallelism(parallelism: Int, name: String? = null): CoroutineDispatcher {
parallelism.checkParallelism()
- return LimitedDispatcher(this, parallelism)
+ return LimitedDispatcher(this, parallelism, name)
}
+ // Was experimental since 1.6.0, deprecated since 1.8.x
+ @Deprecated("Deprecated for good. Override 'limitedParallelism(parallelism: Int, name: String?)' instead",
+ level = DeprecationLevel.HIDDEN,
+ replaceWith = ReplaceWith("limitedParallelism(parallelism, null)")
+ )
+ public open fun limitedParallelism(parallelism: Int): CoroutineDispatcher = limitedParallelism(parallelism, null)
+
/**
* Requests execution of a runnable [block].
* The dispatcher guarantees that [block] will eventually execute, typically by dispatching it to a thread pool,
@@ -156,7 +238,7 @@
public final override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
/*
- * Unconditional cast is safe here: we only return DispatchedContinuation from `interceptContinuation`,
+ * Unconditional cast is safe here: we return only DispatchedContinuation from `interceptContinuation`,
* any ClassCastException can only indicate compiler bug
*/
val dispatched = continuation as DispatchedContinuation<*>
@@ -180,4 +262,3 @@
/** @suppress for nicer debugging */
override fun toString(): String = "$classSimpleName@$hexAddress"
}
-
diff --git a/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt b/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt
index db77c0d..e6f1d9e 100644
--- a/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt
+++ b/kotlinx-coroutines-core/common/src/CoroutineExceptionHandler.kt
@@ -11,6 +11,8 @@
* If there is [CoroutineExceptionHandler] in the context, then it is used. If it throws an exception during handling
* or is absent, all instances of [CoroutineExceptionHandler] found via [ServiceLoader] and
* [Thread.uncaughtExceptionHandler] are invoked.
+ *
+ * @suppress **This is internal API and it is subject to change.**
*/
@InternalCoroutinesApi
public fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
diff --git a/kotlinx-coroutines-core/common/src/CoroutineStart.kt b/kotlinx-coroutines-core/common/src/CoroutineStart.kt
index 640e156..c4c4cea 100644
--- a/kotlinx-coroutines-core/common/src/CoroutineStart.kt
+++ b/kotlinx-coroutines-core/common/src/CoroutineStart.kt
@@ -1,74 +1,344 @@
package kotlinx.coroutines
-import kotlinx.coroutines.CoroutineStart.*
import kotlinx.coroutines.intrinsics.*
import kotlin.coroutines.*
/**
* Defines start options for coroutines builders.
- * It is used in `start` parameter of [launch][CoroutineScope.launch], [async][CoroutineScope.async], and other coroutine builder functions.
+ *
+ * It is used in the `start` parameter of coroutine builder functions like
+ * [launch][CoroutineScope.launch] and [async][CoroutineScope.async]
+ * to describe when and how the coroutine should be dispatched initially.
+ *
+ * This parameter only affects how the coroutine behaves until the code of its body starts executing.
+ * After that, cancellability and dispatching are defined by the behavior of the invoked suspending functions.
*
* The summary of coroutine start options is:
- * - [DEFAULT] -- immediately schedules coroutine for execution according to its context;
- * - [LAZY] -- starts coroutine lazily, only when it is needed;
- * - [ATOMIC] -- atomically (in a non-cancellable way) schedules coroutine for execution according to its context;
- * - [UNDISPATCHED] -- immediately executes coroutine until its first suspension point _in the current thread_.
+ * - [DEFAULT] immediately schedules the coroutine for execution according to its context.
+ * - [LAZY] delays the moment of the initial dispatch until the result of the coroutine is needed.
+ * - [ATOMIC] prevents the coroutine from being cancelled before it starts, ensuring that its code will start
+ * executing in any case.
+ * - [UNDISPATCHED] immediately executes the coroutine until its first suspension point _in the current thread_.
*/
public enum class CoroutineStart {
/**
- * Default -- immediately schedules the coroutine for execution according to its context.
+ * Immediately schedules the coroutine for execution according to its context. This is usually the default option.
*
- * If the [CoroutineDispatcher] of the coroutine context returns `true` from [CoroutineDispatcher.isDispatchNeeded]
- * function as most dispatchers do, then the coroutine code is dispatched for execution later, while the code that
- * invoked the coroutine builder continues execution.
+ * [DEFAULT] uses the default dispatch procedure described in the [CoroutineDispatcher] documentation.
*
- * Note that [Dispatchers.Unconfined] always returns `false` from its [CoroutineDispatcher.isDispatchNeeded]
- * function, so starting a coroutine with [Dispatchers.Unconfined] by [DEFAULT] is the same as using [UNDISPATCHED].
+ * If the coroutine's [Job] is cancelled before it started executing, then it will not start its
+ * execution at all and will be considered [cancelled][Job.isCancelled].
*
- * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
- * execution at all, but will complete with an exception.
+ * Examples:
*
- * Cancellability of a coroutine at suspension points depends on the particular implementation details of
- * suspending functions. Use [suspendCancellableCoroutine] to implement cancellable suspending functions.
+ * ```
+ * // Example of starting a new coroutine that goes through a dispatch
+ * runBlocking {
+ * println("1. About to start a new coroutine.")
+ * // Dispatch the job to execute later.
+ * // The parent coroutine's dispatcher is inherited by default.
+ * // In this case, it's the single thread backing `runBlocking`.
+ * launch { // CoroutineStart.DEFAULT is launch's default start mode
+ * println("3. When the thread is available, we start the coroutine")
+ * }
+ * println("2. The thread keeps doing other work after launching the coroutine")
+ * }
+ * ```
+ *
+ * ```
+ * // Example of starting a new coroutine that doesn't go through a dispatch initially
+ * runBlocking {
+ * println("1. About to start a coroutine not needing a dispatch.")
+ * // Dispatch the job to execute.
+ * // `Dispatchers.Unconfined` is explicitly chosen.
+ * launch(Dispatchers.Unconfined) { // CoroutineStart.DEFAULT is the launch's default start mode
+ * println("2. The body will be executed immediately")
+ * delay(50.milliseconds) // give up the thread to the outer coroutine
+ * println("4. When the thread is next available, this coroutine proceeds further")
+ * }
+ * println("3. After the initial suspension, the thread does other work.")
+ * }
+ * ```
+ *
+ * ```
+ * // Example of cancelling coroutines before they start executing.
+ * runBlocking {
+ * // dispatch the job to execute on this thread later
+ * launch { // CoroutineStart.DEFAULT is the launch's default start mode
+ * println("This code will never execute")
+ * }
+ * cancel() // cancels the current coroutine scope and its children
+ * launch(Dispatchers.Unconfined) {
+ * println("This code will never execute")
+ * }
+ * println("This code will execute.")
+ * }
+ * ```
*/
DEFAULT,
/**
* Starts the coroutine lazily, only when it is needed.
*
- * See the documentation for the corresponding coroutine builders for details
- * (like [launch][CoroutineScope.launch] and [async][CoroutineScope.async]).
+ * Starting a coroutine with [LAZY] only creates the coroutine, but does not schedule it for execution.
+ * When the completion of the coroutine is first awaited
+ * (for example, via [Job.join]) or explicitly [started][Job.start],
+ * the dispatch procedure described in the [CoroutineDispatcher] documentation is performed in the thread
+ * that did it.
*
- * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
- * execution at all, but will complete with an exception.
+ * The details of what counts as waiting can be found in the documentation of the corresponding coroutine builders
+ * like [launch][CoroutineScope.launch] and [async][CoroutineScope.async].
+ *
+ * If the coroutine's [Job] is cancelled before it started executing, then it will not start its
+ * execution at all and will be considered [cancelled][Job.isCancelled].
+ *
+ * **Pitfall**: launching a coroutine with [LAZY] without awaiting or cancelling it at any point means that it will
+ * never be completed, leading to deadlocks and resource leaks.
+ * For example, the following code will deadlock, since [coroutineScope] waits for all of its child coroutines to
+ * complete:
+ * ```
+ * // This code hangs!
+ * coroutineScope {
+ * launch(start = CoroutineStart.LAZY) { }
+ * }
+ * ```
+ *
+ * The behavior of [LAZY] can be described with the following examples:
+ *
+ * ```
+ * // Example of lazily starting a new coroutine that goes through a dispatch
+ * runBlocking {
+ * println("1. About to start a new coroutine.")
+ * // Create a job to execute on `Dispatchers.Default` later.
+ * val job = launch(Dispatchers.Default, start = CoroutineStart.LAZY) {
+ * println("3. Only now does the coroutine start.")
+ * }
+ * delay(10.milliseconds) // try to give the coroutine some time to run
+ * println("2. The coroutine still has not started. Now, we join it.")
+ * job.join()
+ * }
+ * ```
+ *
+ * ```
+ * // Example of lazily starting a new coroutine that doesn't go through a dispatch initially
+ * runBlocking {
+ * println("1. About to lazily start a new coroutine.")
+ * // Create a job to execute on `Dispatchers.Unconfined` later.
+ * val lazyJob = launch(Dispatchers.Unconfined, start = CoroutineStart.LAZY) {
+ * println("3. The coroutine starts on the thread that called `join`.")
+ * }
+ * // We start the job on another thread for illustrative purposes
+ * launch(Dispatchers.Default) {
+ * println("2. We start the lazyJob.")
+ * job.start() // runs lazyJob's code in-place
+ * println("4. Only now does the `start` call return.")
+ * }
+ * }
+ * ```
+ *
+ * ## Alternatives
+ *
+ * The effects of [LAZY] can usually be achieved more idiomatically without it.
+ *
+ * When a coroutine is started with [LAZY] and is stored in a property,
+ * it may be a better choice to use [lazy] instead:
+ *
+ * ```
+ * // instead of `val page = scope.async(start = CoroutineStart.LAZY) { getPage() }`, do
+ * val page by lazy { scope.async { getPage() } }
+ * ```
+ *
+ * This way, the child coroutine is not created at all unless it is needed.
+ * Note that with this, any access to this variable will start the coroutine,
+ * even something like `page.invokeOnCompletion { }` or `page.isActive`.
+ *
+ * If a coroutine is started with [LAZY] and then unconditionally started,
+ * it is more idiomatic to create the coroutine in the exact place where it is started:
+ *
+ * ```
+ * // instead of `val job = scope.launch(start = CoroutineStart.LAZY) { }; job.start()`, do
+ * scope.launch { }
+ * ```
*/
LAZY,
/**
* Atomically (i.e., in a non-cancellable way) schedules the coroutine for execution according to its context.
- * This is similar to [DEFAULT], but the coroutine cannot be cancelled before it starts executing.
*
- * Cancellability of coroutine at suspension points depends on the particular implementation details of
- * suspending functions as in [DEFAULT].
+ * This is similar to [DEFAULT], but the coroutine is guaranteed to start executing even if it was cancelled.
+ * This only affects the behavior until the body of the coroutine starts executing;
+ * inside the body, cancellation will work as usual.
+ *
+ * Like [ATOMIC], [UNDISPATCHED], too, ensures that coroutines will be started in any case.
+ * The difference is that, instead of immediately starting them on the same thread,
+ * [ATOMIC] performs the full dispatch procedure just as [DEFAULT] does.
+ *
+ * Because of this, we can use [ATOMIC] in cases where we want to be certain that some code eventually runs
+ * and uses a specific dispatcher to do that.
+ *
+ * Example:
+ * ```
+ * val mutex = Mutex()
+ *
+ * mutex.lock() // lock the mutex outside the coroutine
+ * // ... // initial portion of the work, protected by the mutex
+ * val job = launch(start = CoroutineStart.ATOMIC) {
+ * // the work must continue in a coroutine, but still under the mutex
+ * println("Coroutine running!")
+ * try {
+ * // this `try` block will be entered in any case because of ATOMIC
+ * println("Starting task...")
+ * delay(10.milliseconds) // throws due to cancellation
+ * println("Finished task.")
+ * } finally {
+ * mutex.unlock() // correctly release the mutex
+ * }
+ * }
+ *
+ * job.cancelAndJoin() // we immediately cancel the coroutine.
+ * mutex.withLock {
+ * println("The lock has been returned correctly!")
+ * }
+ * ```
+ *
+ * Here, we used [ATOMIC] to ensure that a mutex that was acquired outside the coroutine does get released
+ * even if cancellation happens between `lock()` and `launch`.
+ * As a result, the mutex will always be released.
+ *
+ * The behavior of [ATOMIC] can be described with the following examples:
+ *
+ * ```
+ * // Example of cancelling atomically started coroutines
+ * runBlocking {
+ * println("1. Atomically starting a coroutine that goes through a dispatch.")
+ * launch(start = CoroutineStart.ATOMIC) {
+ * check(!isActive) // attempting to suspend later will throw
+ * println("4. The coroutine was cancelled (isActive = $isActive), but starts anyway.")
+ * try {
+ * delay(10.milliseconds) // will throw: the coroutine is cancelled
+ * println("This code will never run.")
+ * } catch (e: CancellationException) {
+ * println("5. Cancellation at later points still works.")
+ * throw e
+ * }
+ * }
+ * println("2. Cancelling this coroutine and all of its children.")
+ * cancel()
+ * launch(Dispatchers.Unconfined, start = CoroutineStart.ATOMIC) {
+ * check(!isActive) // attempting to suspend will throw
+ * println("3. An undispatched coroutine starts.")
+ * }
+ * ensureActive() // we can even crash the current coroutine.
+ * }
+ * ```
+ *
+ * This is a **delicate** API. The coroutine starts execution even if its [Job] is cancelled before starting.
+ * However, the resources used within a coroutine may rely on the cancellation mechanism,
+ * and cannot be used after the [Job] cancellation. For instance, in Android development, updating a UI element
+ * is not allowed if the coroutine's scope, which is tied to the element's lifecycle, has been cancelled.
*/
- @ExperimentalCoroutinesApi // Since 1.0.0, no ETA on stability
+ @DelicateCoroutinesApi
ATOMIC,
/**
- * Immediately executes the coroutine until its first suspension point _in the current thread_ similarly to
- * the coroutine being started using [Dispatchers.Unconfined]. However, when the coroutine is resumed from suspension
- * it is dispatched according to the [CoroutineDispatcher] in its context.
+ * Immediately executes the coroutine until its first suspension point _in the current thread_.
*
- * This is similar to [ATOMIC] in the sense that coroutine starts executing even if it was already cancelled,
- * but the difference is that it starts executing in the same thread.
+ * Starting a coroutine using [UNDISPATCHED] is similar to using [Dispatchers.Unconfined] with [DEFAULT], except:
+ * - Resumptions from later suspensions will properly use the actual dispatcher from the coroutine's context.
+ * Only the code until the first suspension point will be executed immediately.
+ * - Even if the coroutine was cancelled already, its code will still start running, similar to [ATOMIC].
+ * - The coroutine will not form an event loop. See [Dispatchers.Unconfined] for an explanation of event loops.
*
- * Cancellability of coroutine at suspension points depends on the particular implementation details of
- * suspending functions as in [DEFAULT].
+ * This set of behaviors makes [UNDISPATCHED] well-suited for cases where the coroutine has a distinct
+ * initialization phase whose side effects we want to rely on later.
*
- * ### Unconfined event loop
+ * Example:
+ * ```
+ * var tasks = 0
+ * repeat(3) {
+ * launch(start = CoroutineStart.UNDISPATCHED) {
+ * tasks++
+ * try {
+ * println("Waiting for a reply...")
+ * delay(50.milliseconds)
+ * println("Got a reply!")
+ * } finally {
+ * tasks--
+ * }
+ * }
+ * }
+ * // Because of UNDISPATCHED,
+ * // we know that the tasks already ran to their first suspension point,
+ * // so this number is non-zero initially.
+ * while (tasks > 0) {
+ * println("currently active: $tasks")
+ * delay(10.milliseconds)
+ * }
+ * ```
*
- * Unlike [Dispatchers.Unconfined] and [MainCoroutineDispatcher.immediate], nested undispatched coroutines do not form
- * an event loop that otherwise prevents potential stack overflow in case of unlimited nesting.
+ * Here, we implement a publisher-subscriber interaction, where [UNDISPATCHED] ensures that the
+ * subscribers do get registered before the publisher first checks if it can stop emitting values due to
+ * the lack of subscribers.
+ *
+ * ```
+ * // Constant usage of stack space
+ * fun CoroutineScope.factorialWithUnconfined(n: Int): Deferred<Int> =
+ * async(Dispatchers.Unconfined) {
+ * if (n > 0) {
+ * n * factorialWithUnconfined(n - 1).await()
+ * } else {
+ * 1 // replace with `error()` to see the stacktrace
+ * }
+ * }
+ *
+ * // Linearly increasing usage of stack space
+ * fun CoroutineScope.factorialWithUndispatched(n: Int): Deferred<Int> =
+ * async(start = CoroutineStart.UNDISPATCHED) {
+ * if (n > 0) {
+ * n * factorialWithUndispatched(n - 1).await()
+ * } else {
+ * 1 // replace with `error()` to see the stacktrace
+ * }
+ * }
+ * ```
+ *
+ * Calling `factorialWithUnconfined` from this example will result in a constant-size stack,
+ * whereas `factorialWithUndispatched` will lead to `n` recursively nested calls,
+ * resulting in a stack overflow for large values of `n`.
+ *
+ * The behavior of [UNDISPATCHED] can be described with the following examples:
+ *
+ * ```
+ * runBlocking {
+ * println("1. About to start a new coroutine.")
+ * launch(Dispatchers.Default, start = CoroutineStart.UNDISPATCHED) {
+ * println("2. The coroutine is immediately started in the same thread.")
+ * delay(10.milliseconds)
+ * println("4. The execution continues in a Dispatchers.Default thread.")
+ * }
+ * println("3. Execution of the outer coroutine only continues later.")
+ * }
+ * ```
+ *
+ * ```
+ * // Cancellation does not prevent the coroutine from being started
+ * runBlocking {
+ * println("1. First, we cancel this scope.")
+ * cancel()
+ * println("2. Now, we start a new UNDISPATCHED child.")
+ * launch(start = CoroutineStart.UNDISPATCHED) {
+ * check(!isActive) // the child is already cancelled
+ * println("3. We entered the coroutine despite being cancelled.")
+ * }
+ * println("4. Execution of the outer coroutine only continues later.")
+ * }
+ * ```
+ *
+ * **Pitfall**: unlike [Dispatchers.Unconfined] and [MainCoroutineDispatcher.immediate], nested undispatched
+ * coroutines do not form an event loop that otherwise prevents potential stack overflow in case of unlimited
+ * nesting. This property is necessary for the use case of guaranteed initialization, but may be undesirable in
+ * other cases.
+ * See [Dispatchers.Unconfined] for an explanation of event loops.
*/
UNDISPATCHED;
diff --git a/kotlinx-coroutines-core/common/src/Debug.common.kt b/kotlinx-coroutines-core/common/src/Debug.common.kt
index 07ba28b..60b54cf 100644
--- a/kotlinx-coroutines-core/common/src/Debug.common.kt
+++ b/kotlinx-coroutines-core/common/src/Debug.common.kt
@@ -13,11 +13,11 @@
* ```
* class BadResponseCodeException(val responseCode: Int) : Exception(), CopyableThrowable<BadResponseCodeException> {
*
- * override fun createCopy(): BadResponseCodeException {
- * val result = BadResponseCodeException(responseCode)
- * result.initCause(this)
- * return result
- * }
+ * override fun createCopy(): BadResponseCodeException {
+ * val result = BadResponseCodeException(responseCode)
+ * result.initCause(this)
+ * return result
+ * }
* ```
*
* Copy mechanism is used only on JVM, but it might be convenient to implement it in common exceptions,
diff --git a/kotlinx-coroutines-core/common/src/Deferred.kt b/kotlinx-coroutines-core/common/src/Deferred.kt
index afec2cc..867899b 100644
--- a/kotlinx-coroutines-core/common/src/Deferred.kt
+++ b/kotlinx-coroutines-core/common/src/Deferred.kt
@@ -26,10 +26,9 @@
*
* All functions on this interface and on all interfaces derived from it are **thread-safe** and can
* be safely invoked from concurrent coroutines without external synchronization.
- *
- * **`Deferred` interface and all its derived interfaces are not stable for inheritance in 3rd party libraries**,
- * as new methods might be added to this interface in the future, but is stable for use.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(markerClass = InternalForInheritanceCoroutinesApi::class)
public interface Deferred<out T> : Job {
/**
@@ -51,10 +50,10 @@
* The following idiom may be helpful to avoid this:
* ```
* try {
- * deferred.await()
+ * deferred.await()
* } catch (e: CancellationException) {
- * currentCoroutineContext().ensureActive() // throws if the current coroutine was cancelled
- * processException(e) // if this line executes, the exception is the result of `await` itself
+ * currentCoroutineContext().ensureActive() // throws if the current coroutine was cancelled
+ * processException(e) // if this line executes, the exception is the result of `await` itself
* }
* ```
*
diff --git a/kotlinx-coroutines-core/common/src/Dispatchers.common.kt b/kotlinx-coroutines-core/common/src/Dispatchers.common.kt
index 7c34ead..c499a47 100644
--- a/kotlinx-coroutines-core/common/src/Dispatchers.common.kt
+++ b/kotlinx-coroutines-core/common/src/Dispatchers.common.kt
@@ -52,11 +52,11 @@
* For example, the following code:
* ```
* withContext(Dispatchers.Unconfined) {
- * println(1)
- * launch(Dispatchers.Unconfined) { // Nested unconfined
- * println(2)
- * }
- * println(3)
+ * println(1)
+ * launch(Dispatchers.Unconfined) { // Nested unconfined
+ * println(2)
+ * }
+ * println(3)
* }
* println("Done")
* ```
diff --git a/kotlinx-coroutines-core/common/src/EventLoop.common.kt b/kotlinx-coroutines-core/common/src/EventLoop.common.kt
index 914cc8e..84291a1 100644
--- a/kotlinx-coroutines-core/common/src/EventLoop.common.kt
+++ b/kotlinx-coroutines-core/common/src/EventLoop.common.kt
@@ -111,9 +111,9 @@
}
}
- final override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ final override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
parallelism.checkParallelism()
- return this
+ return namedOrThis(name) // Single-threaded, short-circuit
}
open fun shutdown() {}
@@ -256,21 +256,7 @@
// unconfined events take priority
if (processUnconfinedEvent()) return 0
// queue all delayed tasks that are due to be executed
- val delayed = _delayed.value
- if (delayed != null && !delayed.isEmpty) {
- val now = nanoTime()
- while (true) {
- // make sure that moving from delayed to queue removes from delayed only after it is added to queue
- // to make sure that 'isEmpty' and `nextTime` that check both of them
- // do not transiently report that both delayed and queue are empty during move
- delayed.removeFirstIf {
- if (it.timeToExecute(now)) {
- enqueueImpl(it)
- } else
- false
- } ?: break // quit loop when nothing more to remove or enqueueImpl returns false on "isComplete"
- }
- }
+ enqueueDelayedTasks()
// then process one event from queue
val task = dequeue()
if (task != null) {
@@ -283,6 +269,8 @@
final override fun dispatch(context: CoroutineContext, block: Runnable) = enqueue(block)
open fun enqueue(task: Runnable) {
+ // are there some delayed tasks that should execute before this one? If so, move them to the queue first.
+ enqueueDelayedTasks()
if (enqueueImpl(task)) {
// todo: we should unpark only when this delayed task became first in the queue
unpark()
@@ -336,6 +324,25 @@
}
}
+ /** Move all delayed tasks that are due to the main queue. */
+ private fun enqueueDelayedTasks() {
+ val delayed = _delayed.value
+ if (delayed != null && !delayed.isEmpty) {
+ val now = nanoTime()
+ while (true) {
+ // make sure that moving from delayed to queue removes from delayed only after it is added to queue
+ // to make sure that 'isEmpty' and `nextTime` that check both of them
+ // do not transiently report that both delayed and queue are empty during move
+ delayed.removeFirstIf {
+ if (it.timeToExecute(now)) {
+ enqueueImpl(it)
+ } else
+ false
+ } ?: break // quit loop when nothing more to remove or enqueueImpl returns false on "isComplete"
+ }
+ }
+ }
+
private fun closeQueue() {
assert { isCompleted }
_queue.loop { queue ->
diff --git a/kotlinx-coroutines-core/common/src/Exceptions.common.kt b/kotlinx-coroutines-core/common/src/Exceptions.common.kt
index ed20d02..e19c36f 100644
--- a/kotlinx-coroutines-core/common/src/Exceptions.common.kt
+++ b/kotlinx-coroutines-core/common/src/Exceptions.common.kt
@@ -1,7 +1,7 @@
package kotlinx.coroutines
/**
- * This exception gets thrown if an exception is caught while processing [InternalCompletionHandler] invocation for [Job].
+ * This exception gets thrown if an exception is caught while processing [CompletionHandler] invocation for [Job].
*
* @suppress **This an internal API and should not be used from general code.**
*/
diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt
index e9a935c..2c42687 100644
--- a/kotlinx-coroutines-core/common/src/Job.kt
+++ b/kotlinx-coroutines-core/common/src/Job.kt
@@ -99,12 +99,9 @@
*
* All functions on this interface and on all interfaces derived from it are **thread-safe** and can
* be safely invoked from concurrent coroutines without external synchronization.
- *
- * ### Not stable for inheritance
- *
- * **`Job` interface and all its derived interfaces are not stable for inheritance in 3rd party libraries**,
- * as new methods might be added to this interface in the future, but is stable for use.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(markerClass = InternalForInheritanceCoroutinesApi::class)
public interface Job : CoroutineContext.Element {
/**
* Key for [Job] instance in the coroutine context.
@@ -340,10 +337,6 @@
* If the handler would have been invoked earlier if it was registered at that time, then it is invoked immediately,
* unless [invokeImmediately] is set to `false`.
*
- * The handler is scheduled to be invoked once the job is cancelled or is complete.
- * This behavior can be changed by setting the [onCancelling] parameter to `true`.
- * In this case, the handler is invoked as soon as the job becomes _cancelling_ instead.
- *
* The meaning of `cause` that is passed to the handler is:
* - It is `null` if the job has completed normally.
* - It is an instance of [CancellationException] if the job was cancelled _normally_.
@@ -356,12 +349,11 @@
* all the handlers are released when this job completes.
*/
internal fun Job.invokeOnCompletion(
- onCancelling: Boolean = false,
invokeImmediately: Boolean = true,
- handler: InternalCompletionHandler
+ handler: JobNode,
): DisposableHandle = when (this) {
- is JobSupport -> invokeOnCompletionInternal(onCancelling, invokeImmediately, handler)
- else -> invokeOnCompletion(onCancelling, invokeImmediately, handler::invoke)
+ is JobSupport -> invokeOnCompletionInternal(invokeImmediately, handler)
+ else -> invokeOnCompletion(handler.onCancelling, invokeImmediately, handler::invoke)
}
/**
@@ -409,6 +401,7 @@
*/
@InternalCoroutinesApi
@Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases")
+@OptIn(InternalForInheritanceCoroutinesApi::class)
public interface ChildJob : Job {
/**
* Parent is cancelling its child by invoking this method.
@@ -428,6 +421,7 @@
*/
@InternalCoroutinesApi
@Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases")
+@OptIn(InternalForInheritanceCoroutinesApi::class)
public interface ParentJob : Job {
/**
* Child job is using this method to learn its cancellation cause when the parent cancels it with [ChildJob.parentCancelled].
@@ -672,3 +666,11 @@
*/
override fun toString(): String = "NonDisposableHandle"
}
+
+private class DisposeOnCompletion(
+ private val handle: DisposableHandle
+) : JobNode() {
+ override val onCancelling get() = false
+
+ override fun invoke(cause: Throwable?) = handle.dispose()
+}
diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt
index a4a5a29..81ff9d4 100644
--- a/kotlinx-coroutines-core/common/src/JobSupport.kt
+++ b/kotlinx-coroutines-core/common/src/JobSupport.kt
@@ -19,6 +19,7 @@
* @param active when `true` the job is created in _active_ state, when `false` in _new_ state. See [Job] for details.
* @suppress **This is unstable API and it is subject to change.**
*/
+@OptIn(InternalForInheritanceCoroutinesApi::class)
@Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases")
public open class JobSupport constructor(active: Boolean) : Job, ChildJob, ParentJob {
final override val key: CoroutineContext.Key<*> get() = Job
@@ -121,7 +122,6 @@
*/
// Note: use shared objects while we have no listeners
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
private val _state = atomic<Any?>(if (active) EMPTY_ACTIVE else EMPTY_NEW)
private val _parentHandle = atomic<ChildHandle?>(null)
@@ -145,7 +145,6 @@
return
}
parent.start() // make sure the parent is started
- @Suppress("DEPRECATION")
val handle = parent.attachChild(this)
parentHandle = handle
// now check our state _after_ registering (see tryFinalizeSimpleState order of actions)
@@ -161,12 +160,7 @@
* If final state of the job is [Incomplete], then it is boxed into [IncompleteStateBox]
* and should be [unboxed][unboxState] before returning to user code.
*/
- internal val state: Any? get() {
- _state.loop { state -> // helper loop on state (complete in-progress atomic operations)
- if (state !is OpDescriptor) return state
- state.perform(this)
- }
- }
+ internal val state: Any? get() = _state.value
/**
* @suppress **This is unstable API and it is subject to change.**
@@ -326,7 +320,8 @@
private fun notifyCancelling(list: NodeList, cause: Throwable) {
// first cancel our own children
onCancelling(cause)
- notifyHandlers<JobCancellingNode>(list, cause)
+ list.close(LIST_CANCELLATION_PERMISSION)
+ notifyHandlers(list, cause) { it.onCancelling }
// then cancel parent
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
}
@@ -357,17 +352,21 @@
return parent.childCancelled(cause) || isCancellation
}
- private fun NodeList.notifyCompletion(cause: Throwable?) =
- notifyHandlers<JobNode>(this, cause)
+ private fun NodeList.notifyCompletion(cause: Throwable?) {
+ close(LIST_ON_COMPLETION_PERMISSION)
+ notifyHandlers(this, cause) { true }
+ }
- private inline fun <reified T: JobNode> notifyHandlers(list: NodeList, cause: Throwable?) {
+ private inline fun notifyHandlers(list: NodeList, cause: Throwable?, predicate: (JobNode) -> Boolean) {
var exception: Throwable? = null
- list.forEach<T> { node ->
- try {
- node.invoke(cause)
- } catch (ex: Throwable) {
- exception?.apply { addSuppressed(ex) } ?: run {
- exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)
+ list.forEach { node ->
+ if (node is JobNode && predicate(node)) {
+ try {
+ node.invoke(cause)
+ } catch (ex: Throwable) {
+ exception?.apply { addSuppressed(ex) } ?: run {
+ exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)
+ }
}
}
}
@@ -445,93 +444,113 @@
public final override fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle =
invokeOnCompletionInternal(
- onCancelling = false,
invokeImmediately = true,
- handler = InternalCompletionHandler.UserSupplied(handler)
+ node = InvokeOnCompletion(handler),
)
public final override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle =
invokeOnCompletionInternal(
- onCancelling = onCancelling,
invokeImmediately = invokeImmediately,
- handler = InternalCompletionHandler.UserSupplied(handler)
+ node = if (onCancelling) {
+ InvokeOnCancelling(handler)
+ } else {
+ InvokeOnCompletion(handler)
+ }
)
internal fun invokeOnCompletionInternal(
- onCancelling: Boolean,
invokeImmediately: Boolean,
- handler: InternalCompletionHandler
+ node: JobNode
): DisposableHandle {
+ node.job = this
// Create node upfront -- for common cases it just initializes JobNode.job field,
// for user-defined handlers it allocates a JobNode object that we might not need, but this is Ok.
- val node: JobNode = makeNode(handler, onCancelling)
+ val added = tryPutNodeIntoList(node) { state, list ->
+ if (node.onCancelling) {
+ /**
+ * We are querying whether the job was already cancelled when we entered this block.
+ * We can't naively attempt to add the node to the list, because a lot of time could pass between
+ * notifying the cancellation handlers (and thus closing the list, forcing us to retry)
+ * and reaching a final state.
+ *
+ * Alternatively, we could also try to add the node to the list first and then read the latest state
+ * to check for an exception, but that logic would need to manually handle the final state, which is
+ * less straightforward.
+ */
+ val rootCause = (state as? Finishing)?.rootCause
+ if (rootCause == null) {
+ /**
+ * There is no known root cause yet, so we can add the node to the list of state handlers.
+ *
+ * If this call fails, because of the bitmask, this means one of the two happened:
+ * - [notifyCancelling] was already called.
+ * This means that the job is already being cancelled: otherwise, with what exception would we
+ * notify the handler?
+ * So, we can retry the operation: either the state is already final, or the `rootCause` check
+ * above will give a different result.
+ * - [notifyCompletion] was already called.
+ * This means that the job is already complete.
+ * We can retry the operation and will observe the final state.
+ */
+ list.addLast(node, LIST_CANCELLATION_PERMISSION or LIST_ON_COMPLETION_PERMISSION)
+ } else {
+ /**
+ * The root cause is known, so we can invoke the handler immediately and avoid adding it.
+ */
+ if (invokeImmediately) node.invoke(rootCause)
+ return NonDisposableHandle
+ }
+ } else {
+ /**
+ * The non-[onCancelling]-handlers are interested in completions only, so it's safe to add them at
+ * any time before [notifyCompletion] is called (which closes the list).
+ *
+ * If the list *is* closed, on a retry, we'll observe the final state, as [notifyCompletion] is only
+ * called after the state transition.
+ */
+ list.addLast(node, LIST_ON_COMPLETION_PERMISSION)
+ }
+ }
+ when {
+ added -> return node
+ invokeImmediately -> node.invoke((state as? CompletedExceptionally)?.cause)
+ }
+ return NonDisposableHandle
+ }
+
+ /**
+ * Puts [node] into the current state's list of completion handlers.
+ *
+ * Returns `false` if the state is already complete and doesn't accept new handlers.
+ * Returns `true` if the handler was successfully added to the list.
+ *
+ * [tryAdd] is invoked when the state is [Incomplete] and the list is not `null`, to decide on the specific
+ * behavior in this case. It must return
+ * - `true` if the element was successfully added to the list
+ * - `false` if the operation needs to be retried
+ */
+ private inline fun tryPutNodeIntoList(
+ node: JobNode,
+ tryAdd: (Incomplete, NodeList) -> Boolean
+ ): Boolean {
loopOnState { state ->
when (state) {
is Empty -> { // EMPTY_X state -- no completion handlers
if (state.isActive) {
- // try move to SINGLE state
- if (_state.compareAndSet(state, node)) return node
+ // try to move to the SINGLE state
+ if (_state.compareAndSet(state, node)) return true
} else
promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
}
- is Incomplete -> {
- val list = state.list
- if (list == null) { // SINGLE/SINGLE+
- promoteSingleToNodeList(state as JobNode)
- } else {
- var rootCause: Throwable? = null
- var handle: DisposableHandle = NonDisposableHandle
- if (onCancelling && state is Finishing) {
- synchronized(state) {
- // check if we are installing cancellation handler on job that is being cancelled
- rootCause = state.rootCause // != null if cancelling job
- // We add node to the list in two cases --- either the job is not being cancelled
- // or we are adding a child to a coroutine that is not completing yet
- if (rootCause == null || handler is ChildHandleNode && !state.isCompleting) {
- // Note: add node the list while holding lock on state (make sure it cannot change)
- if (!addLastAtomic(state, list, node)) return@loopOnState // retry
- // just return node if we don't have to invoke handler (not cancelling yet)
- if (rootCause == null) return node
- // otherwise handler is invoked immediately out of the synchronized section & handle returned
- handle = node
- }
- }
- }
- if (rootCause != null) {
- // Note: attachChild uses invokeImmediately, so it gets invoked when adding to cancelled job
- if (invokeImmediately) handler.invoke(rootCause)
- return handle
- } else {
- if (addLastAtomic(state, list, node)) return node
- }
- }
+ is Incomplete -> when (val list = state.list) {
+ null -> promoteSingleToNodeList(state as JobNode)
+ else -> if (tryAdd(state, list)) return true
}
- else -> { // is complete
- // :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
- // because we play type tricks on Kotlin/JS and handler is not necessarily a function there
- if (invokeImmediately) handler.invoke((state as? CompletedExceptionally)?.cause)
- return NonDisposableHandle
- }
+ else -> return false
}
}
}
- private fun makeNode(handler: InternalCompletionHandler, onCancelling: Boolean): JobNode {
- val node = if (onCancelling) {
- (handler as? JobCancellingNode)
- ?: InvokeOnCancelling(handler)
- } else {
- (handler as? JobNode)
- ?.also { assert { it !is JobCancellingNode } }
- ?: InvokeOnCompletion(handler)
- }
- node.job = this
- return node
- }
-
- private fun addLastAtomic(expect: Any, list: NodeList, node: JobNode) =
- list.addLastIf(node) { this.state === expect }
-
private fun promoteEmptyToNodeList(state: Empty) {
// try to promote it to LIST state with the corresponding state
val list = NodeList()
@@ -588,6 +607,7 @@
private inner class SelectOnJoinCompletionHandler(
private val select: SelectInstance<*>
) : JobNode() {
+ override val onCancelling: Boolean get() = false
override fun invoke(cause: Throwable?) {
select.trySelect(this@JobSupport, Unit)
}
@@ -887,13 +907,13 @@
// atomically transition to finishing & completing state
val finishing = state as? Finishing ?: Finishing(list, false, null)
// must synchronize updates to finishing state
- var notifyRootCause: Throwable? = null
+ val notifyRootCause: Throwable?
synchronized(finishing) {
// check if this state is already completing
if (finishing.isCompleting) return COMPLETING_ALREADY
// mark as completing
finishing.isCompleting = true
- // if we need to promote to finishing then atomically do it here.
+ // if we need to promote to finishing, then atomically do it here.
// We do it as early is possible while still holding the lock. This ensures that we cancelImpl asap
// (if somebody else is faster) and we synchronize all the threads on this finishing lock asap.
if (finishing !== state) {
@@ -907,12 +927,21 @@
// If it just becomes cancelling --> must process cancelling notifications
notifyRootCause = finishing.rootCause.takeIf { !wasCancelling }
}
- // process cancelling notification here -- it cancels all the children _before_ we start to to wait them (sic!!!)
+ // process cancelling notification here -- it cancels all the children _before_ we start to wait them (sic!!!)
notifyRootCause?.let { notifyCancelling(list, it) }
// now wait for children
- val child = firstChild(state)
+ // we can't close the list yet: while there are active children, adding new ones is still allowed.
+ val child = list.nextChild()
if (child != null && tryWaitForChild(finishing, child, proposedUpdate))
return COMPLETING_WAITING_CHILDREN
+ // turns out, there are no children to await, so we close the list.
+ list.close(LIST_CHILD_PERMISSION)
+ // some children could have sneaked into the list, so we try waiting for them again.
+ // it would be more correct to re-open the list (otherwise, we get non-linearizable behavior),
+ // but it's too difficult with the current lock-free list implementation.
+ val anotherChild = list.nextChild()
+ if (anotherChild != null && tryWaitForChild(finishing, anotherChild, proposedUpdate))
+ return COMPLETING_WAITING_CHILDREN
// otherwise -- we have not children left (all were already cancelled?)
return finalizeFinishingState(finishing, proposedUpdate)
}
@@ -920,9 +949,6 @@
private val Any?.exceptionOrNull: Throwable?
get() = (this as? CompletedExceptionally)?.cause
- private fun firstChild(state: Incomplete) =
- state as? ChildHandleNode ?: state.list?.nextChild()
-
// return false when there is no more incomplete children to wait
// ## IMPORTANT INVARIANT: Only one thread can be concurrently invoking this method.
private tailrec fun tryWaitForChild(state: Finishing, child: ChildHandleNode, proposedUpdate: Any?): Boolean {
@@ -938,11 +964,25 @@
// ## IMPORTANT INVARIANT: Only one thread can be concurrently invoking this method.
private fun continueCompleting(state: Finishing, lastChild: ChildHandleNode, proposedUpdate: Any?) {
assert { this.state === state } // consistency check -- it cannot change while we are waiting for children
- // figure out if we need to wait for next child
+ // figure out if we need to wait for the next child
val waitChild = lastChild.nextChild()
- // try wait for next child
+ // try to wait for the next child
if (waitChild != null && tryWaitForChild(state, waitChild, proposedUpdate)) return // waiting for next child
- // no more children to wait -- try update state
+ // no more children to await, so *maybe* we can complete the job; for that, we stop accepting new children.
+ // potentially, the list can be closed for children more than once: if we detect that there are no more
+ // children, attempt to close the list, and then new children sneak in, this whole logic will be
+ // repeated, including closing the list.
+ state.list.close(LIST_CHILD_PERMISSION)
+ // did any new children sneak in?
+ val waitChildAgain = lastChild.nextChild()
+ if (waitChildAgain != null && tryWaitForChild(state, waitChildAgain, proposedUpdate)) {
+ // yes, so now we have to wait for them!
+ // ideally, we should re-open the list,
+ // but it's too difficult with the current lock-free list implementation,
+ // so we'll live with non-linearizable behavior for now.
+ return
+ }
+ // no more children, now we are sure; try to update the state
val finalState = finalizeFinishingState(state, proposedUpdate)
afterCompletion(finalState)
}
@@ -962,7 +1002,7 @@
when (val state = [email protected]) {
is ChildHandleNode -> yield(state.childJob)
is Incomplete -> state.list?.let { list ->
- list.forEach<ChildHandleNode> { yield(it.childJob) }
+ list.forEach { if (it is ChildHandleNode) yield(it.childJob) }
}
}
}
@@ -971,14 +1011,75 @@
public final override fun attachChild(child: ChildJob): ChildHandle {
/*
* Note: This function attaches a special ChildHandleNode node object. This node object
- * is handled in a special way on completion on the coroutine (we wait for all of them) and
- * is handled specially by invokeOnCompletion itself -- it adds this node to the list even
- * if the job is already cancelling. For cancelling state child is attached under state lock.
- * It's required to properly wait all children before completion and provide linearizable hierarchy view:
- * If child is attached when the job is already being cancelled, such child will receive immediate notification on
- * cancellation, but parent *will* wait for that child before completion and will handle its exception.
+ * is handled in a special way on completion on the coroutine (we wait for all of them) and also
+ * can't be added simply with `invokeOnCompletionInternal` -- we add this node to the list even
+ * if the job is already cancelling.
+ * It's required to properly await all children before completion and provide a linearizable hierarchy view:
+ * If the child is attached when the job is already being cancelled, such a child will receive
+ * an immediate notification on cancellation,
+ * but the parent *will* wait for that child before completion and will handle its exception.
*/
- return invokeOnCompletion(onCancelling = true, handler = ChildHandleNode(child)) as ChildHandle
+ val node = ChildHandleNode(child).also { it.job = this }
+ val added = tryPutNodeIntoList(node) { _, list ->
+ // First, try to add a child along the cancellation handlers
+ val addedBeforeCancellation = list.addLast(
+ node,
+ LIST_ON_COMPLETION_PERMISSION or LIST_CHILD_PERMISSION or LIST_CANCELLATION_PERMISSION
+ )
+ if (addedBeforeCancellation) {
+ // The child managed to be added before the parent started to cancel or complete. Success.
+ true
+ } else {
+ /* Either cancellation or completion already happened, the child was not added.
+ * Now we need to try adding it just for completion. */
+ val addedBeforeCompletion = list.addLast(
+ node,
+ LIST_CHILD_PERMISSION or LIST_ON_COMPLETION_PERMISSION
+ )
+ /*
+ * Whether or not we managed to add the child before the parent completed, we need to investigate:
+ * why didn't we manage to add it before cancellation?
+ * If it's because cancellation happened in the meantime, we need to notify the child about it.
+ * We check the latest state because the original state with which we started may not have had
+ * the information about the cancellation yet.
+ */
+ val rootCause = when (val latestState = this.state) {
+ is Finishing -> {
+ // The state is still incomplete, so we need to notify the child about the completion cause.
+ latestState.rootCause
+ }
+ else -> {
+ /** Since the list is already closed for [onCancelling], the job is either Finishing or
+ * already completed. We need to notify the child about the completion cause. */
+ assert { latestState !is Incomplete }
+ (latestState as? CompletedExceptionally)?.cause
+ }
+ }
+ /**
+ * We must cancel the child if the parent was cancelled already, even if we successfully attached,
+ * as this child didn't make it before [notifyCancelling] and won't be notified that it should be
+ * cancelled.
+ *
+ * And if the parent wasn't cancelled and the previous [LockFreeLinkedListNode.addLast] failed because
+ * the job is in its final state already, we won't be able to attach anyway, so we must just invoke
+ * the handler and return.
+ */
+ node.invoke(rootCause)
+ if (addedBeforeCompletion) {
+ /** The root cause can't be null: since the earlier addition to the list failed, this means that
+ * the job was already cancelled or completed. */
+ assert { rootCause != null }
+ true
+ } else {
+ /** No sense in retrying: we know it won't succeed, and we already invoked the handler. */
+ return NonDisposableHandle
+ }
+ }
+ }
+ if (added) return node
+ /** We can only end up here if [tryPutNodeIntoList] detected a final state. */
+ node.invoke((state as? CompletedExceptionally)?.cause)
+ return NonDisposableHandle
}
/**
@@ -1162,6 +1263,7 @@
private val child: ChildHandleNode,
private val proposedUpdate: Any?
) : JobNode() {
+ override val onCancelling get() = false
override fun invoke(cause: Throwable?) {
parent.continueCompleting(state, child, proposedUpdate)
}
@@ -1182,7 +1284,7 @@
return parent.getCancellationException()
}
- protected override fun nameString(): String =
+ override fun nameString(): String =
"AwaitContinuation"
}
@@ -1276,6 +1378,7 @@
private inner class SelectOnAwaitCompletionHandler(
private val select: SelectInstance<*>
) : JobNode() {
+ override val onCancelling get() = false
override fun invoke(cause: Throwable?) {
val state = [email protected]
val result = if (state is CompletedExceptionally) state else state.unboxState()
@@ -1307,11 +1410,17 @@
private val EMPTY_NEW = Empty(false)
private val EMPTY_ACTIVE = Empty(true)
+// bit mask
+private const val LIST_ON_COMPLETION_PERMISSION = 1
+private const val LIST_CHILD_PERMISSION = 2
+private const val LIST_CANCELLATION_PERMISSION = 4
+
private class Empty(override val isActive: Boolean) : Incomplete {
override val list: NodeList? get() = null
override fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}"
}
+@OptIn(InternalForInheritanceCoroutinesApi::class)
@PublishedApi // for a custom job in the test module
internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob {
init { initParentJob(parent) }
@@ -1348,15 +1457,45 @@
val list: NodeList? // is null only for Empty and JobNode incomplete state objects
}
-internal abstract class JobNode : LockFreeLinkedListNode(), InternalCompletionHandler, DisposableHandle, Incomplete {
+internal abstract class JobNode : LockFreeLinkedListNode(), DisposableHandle, Incomplete {
/**
- * Initialized by [JobSupport.makeNode].
+ * Initialized by [JobSupport.invokeOnCompletionInternal].
*/
lateinit var job: JobSupport
+
+ /**
+ * If `false`, [invoke] will be called once the job is cancelled or is complete.
+ * If `true`, [invoke] is invoked as soon as the job becomes _cancelling_ instead, and if that doesn't happen,
+ * it will be called once the job is cancelled or is complete.
+ */
+ abstract val onCancelling: Boolean
override val isActive: Boolean get() = true
override val list: NodeList? get() = null
+
override fun dispose() = job.removeNode(this)
override fun toString() = "$classSimpleName@$hexAddress[job@${job.hexAddress}]"
+ /**
+ * Signals completion.
+ *
+ * This function:
+ * - Does not throw any exceptions.
+ * For [Job] instances that are coroutines, exceptions thrown by this function will be caught, wrapped into
+ * [CompletionHandlerException], and passed to [handleCoroutineException], but for those that are not coroutines,
+ * they will just be rethrown, potentially crashing unrelated code.
+ * - Is fast, non-blocking, and thread-safe.
+ * - Can be invoked concurrently with the surrounding code.
+ * - Can be invoked from any context.
+ *
+ * The meaning of `cause` that is passed to the handler is:
+ * - It is `null` if the job has completed normally.
+ * - It is an instance of [CancellationException] if the job was cancelled _normally_.
+ * **It should not be treated as an error**. In particular, it should not be reported to error logs.
+ * - Otherwise, the job had _failed_.
+ *
+ * [CompletionHandler] is the user-visible interface for supplying custom implementations of [invoke]
+ * (see [InvokeOnCompletion] and [InvokeOnCancelling]).
+ */
+ abstract fun invoke(cause: Throwable?)
}
internal class NodeList : LockFreeLinkedListHead(), Incomplete {
@@ -1368,9 +1507,11 @@
append(state)
append("}[")
var first = true
- [email protected]<JobNode> { node ->
- if (first) first = false else append(", ")
- append(node)
+ [email protected] { node ->
+ if (node is JobNode) {
+ if (first) first = false else append(", ")
+ append(node)
+ }
}
append("]")
}
@@ -1379,7 +1520,7 @@
if (DEBUG) getString("Active") else super.toString()
}
-internal class InactiveNodeList(
+private class InactiveNodeList(
override val list: NodeList
) : Incomplete {
override val isActive: Boolean get() = false
@@ -1387,20 +1528,23 @@
}
private class InvokeOnCompletion(
- private val handler: InternalCompletionHandler
+ private val handler: CompletionHandler
) : JobNode() {
+ override val onCancelling get() = false
override fun invoke(cause: Throwable?) = handler.invoke(cause)
}
private class ResumeOnCompletion(
private val continuation: Continuation<Unit>
) : JobNode() {
+ override val onCancelling get() = false
override fun invoke(cause: Throwable?) = continuation.resume(Unit)
}
private class ResumeAwaitOnCompletion<T>(
private val continuation: CancellableContinuationImpl<T>
) : JobNode() {
+ override val onCancelling get() = false
override fun invoke(cause: Throwable?) {
val state = job.state
assert { state !is Incomplete }
@@ -1415,46 +1559,24 @@
}
}
-internal class DisposeOnCompletion(
- private val handle: DisposableHandle
-) : JobNode() {
- override fun invoke(cause: Throwable?) = handle.dispose()
-}
-
// -------- invokeOnCancellation nodes
-/**
- * Marker for node that shall be invoked on in _cancelling_ state.
- * **Note: may be invoked multiple times.**
- */
-internal abstract class JobCancellingNode : JobNode()
-
private class InvokeOnCancelling(
- private val handler: InternalCompletionHandler
-) : JobCancellingNode() {
+ private val handler: CompletionHandler
+) : JobNode() {
// delegate handler shall be invoked at most once, so here is an additional flag
- private val _invoked = atomic(0) // todo: replace with atomic boolean after migration to recent atomicFu
+ private val _invoked = atomic(false)
+ override val onCancelling get() = true
override fun invoke(cause: Throwable?) {
- if (_invoked.compareAndSet(0, 1)) handler.invoke(cause)
+ if (_invoked.compareAndSet(expect = false, update = true)) handler.invoke(cause)
}
}
-internal class ChildHandleNode(
+private class ChildHandleNode(
@JvmField val childJob: ChildJob
-) : JobCancellingNode(), ChildHandle {
+) : JobNode(), ChildHandle {
override val parent: Job get() = job
+ override val onCancelling: Boolean get() = true
override fun invoke(cause: Throwable?) = childJob.parentCancelled(job)
override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
}
-
-// Same as ChildHandleNode, but for cancellable continuation
-@PublishedApi
-internal class ChildContinuation(
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
- @JvmField val child: CancellableContinuationImpl<*>
-) : JobCancellingNode() {
- override fun invoke(cause: Throwable?) {
- child.parentCancelled(child.getContinuationCancellationCause(job))
- }
-}
-
diff --git a/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt
index 8644474..a6adc38 100644
--- a/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/common/src/MainCoroutineDispatcher.kt
@@ -21,17 +21,17 @@
* Example of usage:
* ```
* suspend fun updateUiElement(val text: String) {
- * /*
- * * If it is known that updateUiElement can be invoked both from the Main thread and from other threads,
- * * `immediate` dispatcher is used as a performance optimization to avoid unnecessary dispatch.
- * *
- * * In that case, when `updateUiElement` is invoked from the Main thread, `uiElement.text` will be
- * * invoked immediately without any dispatching, otherwise, the `Dispatchers.Main` dispatch cycle will be triggered.
- * */
- * withContext(Dispatchers.Main.immediate) {
- * uiElement.text = text
- * }
- * // Do context-independent logic such as logging
+ * /*
+ * * If it is known that updateUiElement can be invoked both from the Main thread and from other threads,
+ * * `immediate` dispatcher is used as a performance optimization to avoid unnecessary dispatch.
+ * *
+ * * In that case, when `updateUiElement` is invoked from the Main thread, `uiElement.text` will be
+ * * invoked immediately without any dispatching, otherwise, the `Dispatchers.Main` dispatch cycle will be triggered.
+ * */
+ * withContext(Dispatchers.Main.immediate) {
+ * uiElement.text = text
+ * }
+ * // Do context-independent logic such as logging
* }
* ```
*
@@ -49,10 +49,10 @@
*/
override fun toString(): String = toStringInternalImpl() ?: "$classSimpleName@$hexAddress"
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
parallelism.checkParallelism()
// MainCoroutineDispatcher is single-threaded -- short-circuit any attempts to limit it
- return this
+ return namedOrThis(name)
}
/**
diff --git a/kotlinx-coroutines-core/common/src/NonCancellable.kt b/kotlinx-coroutines-core/common/src/NonCancellable.kt
index afc1f6a..25c4f6f 100644
--- a/kotlinx-coroutines-core/common/src/NonCancellable.kt
+++ b/kotlinx-coroutines-core/common/src/NonCancellable.kt
@@ -21,6 +21,7 @@
* 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.
*/
+@OptIn(InternalForInheritanceCoroutinesApi::class)
@Suppress("DeprecatedCallableAddReplaceWith")
public object NonCancellable : AbstractCoroutineContextElement(Job), Job {
diff --git a/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt b/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt
index 3eea46c..e950dcb 100644
--- a/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt
+++ b/kotlinx-coroutines-core/common/src/SchedulerTask.common.kt
@@ -1,11 +1,16 @@
package kotlinx.coroutines
+/**
+ * A [Runnable] that's especially optimized for running in [Dispatchers.Default] on the JVM.
+ *
+ * Replacing a [SchedulerTask] with a [Runnable] should not lead to any change in observable behavior.
+ *
+ * An arbitrary [Runnable], once it is dispatched by [Dispatchers.Default], gets wrapped into a class that
+ * stores the submission time, the execution context, etc.
+ * For [Runnable] instances that we know are only going to be executed in dispatch procedures, we can avoid the
+ * overhead of separately allocating a wrapper, and instead have the [Runnable] contain the required fields
+ * on construction.
+ *
+ * When running outside the standard dispatchers, these new fields are just dead weight.
+ */
internal expect abstract class SchedulerTask internal constructor() : Runnable
-
-internal expect interface SchedulerTaskContext
-
-@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
-internal expect val SchedulerTask.taskContext: SchedulerTaskContext
-
-@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
-internal expect inline fun SchedulerTaskContext.afterTask()
diff --git a/kotlinx-coroutines-core/common/src/Timeout.kt b/kotlinx-coroutines-core/common/src/Timeout.kt
index 930b39c..c631365 100644
--- a/kotlinx-coroutines-core/common/src/Timeout.kt
+++ b/kotlinx-coroutines-core/common/src/Timeout.kt
@@ -27,7 +27,7 @@
* even right before the return from inside the timeout [block]. Keep this in mind if you open or acquire some
* resource inside the [block] that needs closing or release outside the block.
* See the
- * [Asynchronous timeout and resources][https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources]
+ * [Asynchronous timeout and resources](https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources)
* section of the coroutines guide for details.
*
* > Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher].
@@ -59,7 +59,7 @@
* even right before the return from inside the timeout [block]. Keep this in mind if you open or acquire some
* resource inside the [block] that needs closing or release outside the block.
* See the
- * [Asynchronous timeout and resources][https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources]
+ * [Asynchronous timeout and resources](https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources)
* section of the coroutines guide for details.
*
* > Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher].
@@ -86,7 +86,7 @@
* even right before the return from inside the timeout [block]. Keep this in mind if you open or acquire some
* resource inside the [block] that needs closing or release outside the block.
* See the
- * [Asynchronous timeout and resources][https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources]
+ * [Asynchronous timeout and resources](https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources)
* section of the coroutines guide for details.
*
* > Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher].
@@ -127,7 +127,7 @@
* even right before the return from inside the timeout [block]. Keep this in mind if you open or acquire some
* resource inside the [block] that needs closing or release outside the block.
* See the
- * [Asynchronous timeout and resources][https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources]
+ * [Asynchronous timeout and resources](https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#asynchronous-timeout-and-resources)
* section of the coroutines guide for details.
*
* > Implementation note: how the time is tracked exactly is an implementation detail of the context's [CoroutineDispatcher].
diff --git a/kotlinx-coroutines-core/common/src/Unconfined.kt b/kotlinx-coroutines-core/common/src/Unconfined.kt
index 46ea4ea..2e16f95 100644
--- a/kotlinx-coroutines-core/common/src/Unconfined.kt
+++ b/kotlinx-coroutines-core/common/src/Unconfined.kt
@@ -8,8 +8,7 @@
*/
internal object Unconfined : CoroutineDispatcher() {
- @ExperimentalCoroutinesApi
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
throw UnsupportedOperationException("limitedParallelism is not supported for Dispatchers.Unconfined")
}
diff --git a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt
index eba767d..f5d1426 100644
--- a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt
@@ -1,4 +1,4 @@
-@file:Suppress("DEPRECATION")
+@file:Suppress("DEPRECATION", "DEPRECATION_ERROR")
package kotlinx.coroutines.channels
@@ -10,36 +10,10 @@
import kotlin.coroutines.intrinsics.*
/**
- * Broadcasts all elements of the channel.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
- *
- * The kind of the resulting channel depends on the specified [capacity] parameter:
- * when `capacity` is positive (1 by default), but less than [UNLIMITED] -- uses [BroadcastChannel] with a buffer of given capacity,
- * when `capacity` is [CONFLATED] -- uses [ConflatedBroadcastChannel] that conflates back-to-back sends;
- * Note that resulting channel behaves like [ConflatedBroadcastChannel] but is not an instance of [ConflatedBroadcastChannel].
- * otherwise -- throws [IllegalArgumentException].
- *
- * ### Cancelling broadcast
- *
- * **To stop broadcasting from the underlying channel call [cancel][BroadcastChannel.cancel] on the result.**
- *
- * Do not use [close][BroadcastChannel.close] on the resulting channel.
- * It causes eventual failure of the broadcast coroutine and cancellation of the underlying channel, too,
- * but it is not as prompt.
- *
- * ### Future replacement
- *
- * 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.
- *
- * **Note: This API is obsolete since 1.5.0.** It is deprecated with warning 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].
+ * @suppress obsolete since 1.5.0, WARNING since 1.7.0, ERROR since 1.9.0
*/
@ObsoleteCoroutinesApi
-@Deprecated(level = DeprecationLevel.WARNING, message = "BroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
+@Deprecated(level = DeprecationLevel.ERROR, message = "BroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
public fun <E> ReceiveChannel<E>.broadcast(
capacity: Int = 1,
start: CoroutineStart = CoroutineStart.LAZY
@@ -56,60 +30,10 @@
}
/**
- * Launches new coroutine to produce a stream of values by sending them to a broadcast channel
- * and returns a reference to the coroutine as a [BroadcastChannel]. The resulting
- * object can be used to [subscribe][BroadcastChannel.openSubscription] to elements produced by this coroutine.
- *
- * The scope of the coroutine contains [ProducerScope] interface, which implements
- * both [CoroutineScope] and [SendChannel], so that coroutine can invoke
- * [send][SendChannel.send] directly. The channel is [closed][SendChannel.close]
- * when the coroutine completes.
- *
- * Coroutine context is inherited from a [CoroutineScope], additional context elements can be specified with [context] argument.
- * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used.
- * The parent job is inherited from a [CoroutineScope] as well, but it can also be overridden
- * with corresponding [context] element.
- *
- * Uncaught exceptions in this coroutine close the channel with this exception as a cause and
- * the resulting channel becomes _failed_, so that any attempt to receive from such a channel throws exception.
- *
- * The kind of the resulting channel depends on the specified [capacity] parameter:
- * - when `capacity` is positive (1 by default), but less than [UNLIMITED] -- uses [BroadcastChannel] with a buffer of given capacity,
- * - when `capacity` is [CONFLATED] -- uses [ConflatedBroadcastChannel] that conflates back-to-back sends;
- * Note that resulting channel behaves like [ConflatedBroadcastChannel] but is not an instance of [ConflatedBroadcastChannel].
- * - otherwise -- throws [IllegalArgumentException].
- *
- * **Note:** By default, the coroutine does not start until the first subscriber appears via [BroadcastChannel.openSubscription]
- * as [start] parameter has a value of [CoroutineStart.LAZY] by default.
- * This ensures that the first subscriber does not miss any sent elements.
- * However, later subscribers may miss elements.
- *
- * See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
- *
- * ### Cancelling broadcast
- *
- * **To stop broadcasting from the underlying channel call [cancel][BroadcastChannel.cancel] on the result.**
- *
- * Do not use [close][BroadcastChannel.close] on the resulting channel.
- * It causes failure of the `send` operation in broadcast coroutine and would not cancel it if the
- * coroutine is doing something else.
- *
- * ### Future replacement
- *
- * This API is obsolete since 1.5.0 and deprecated with warning since 1.7.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 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).
- * @param start coroutine start option. The default value is [CoroutineStart.LAZY].
- * @param onCompletion optional completion handler for the producer coroutine (see [Job.invokeOnCompletion]).
- * @param block the coroutine code.
+ * @suppress obsolete since 1.5.0, WARNING since 1.7.0, ERROR since 1.9.0
*/
@ObsoleteCoroutinesApi
-@Deprecated(level = DeprecationLevel.WARNING, message = "BroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
+@Deprecated(level = DeprecationLevel.ERROR, message = "BroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
public fun <E> CoroutineScope.broadcast(
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = 1,
diff --git a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt
index b90f17e..1f07391 100644
--- a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt
@@ -1,4 +1,4 @@
-@file:Suppress("FunctionName", "DEPRECATION")
+@file:Suppress("FunctionName", "DEPRECATION", "DEPRECATION_ERROR")
package kotlinx.coroutines.channels
@@ -10,62 +10,35 @@
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.selects.*
-import kotlin.native.concurrent.*
/**
- * Broadcast channel is a non-blocking primitive for communication between the sender and multiple receivers
- * that subscribe for the elements using [openSubscription] function and unsubscribe using [ReceiveChannel.cancel]
- * function.
- *
- * See `BroadcastChannel()` factory function for the description of available
- * broadcast channel implementations.
- *
- * **Note: This API is obsolete since 1.5.0 and deprecated for removal since 1.7.0**
- * It is replaced with [SharedFlow][kotlinx.coroutines.flow.SharedFlow].
+ * @suppress obsolete since 1.5.0, WARNING since 1.7.0, ERROR since 1.9.0
*/
@ObsoleteCoroutinesApi
-@Deprecated(level = DeprecationLevel.WARNING, message = "BroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
+@Deprecated(level = DeprecationLevel.ERROR, message = "BroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
public interface BroadcastChannel<E> : SendChannel<E> {
/**
- * Subscribes to this [BroadcastChannel] and returns a channel to receive elements from it.
- * The resulting channel shall be [cancelled][ReceiveChannel.cancel] to unsubscribe from this
- * broadcast channel.
+ * @suppress
*/
public fun openSubscription(): ReceiveChannel<E>
/**
- * Cancels reception of remaining elements from this channel with an optional cause.
- * This function closes the channel with
- * the specified cause (unless it was already closed), removes all buffered sent elements from it,
- * and [cancels][ReceiveChannel.cancel] all open subscriptions.
- * A cause can be used to specify an error message or to provide other details on
- * a cancellation reason for debugging purposes.
+ * @suppress
*/
public fun cancel(cause: CancellationException? = null)
/**
- * @suppress This method has bad semantics when cause is not a [CancellationException]. Use [cancel].
+ * @suppress
*/
@Deprecated(level = DeprecationLevel.HIDDEN, message = "Binary compatibility only")
public fun cancel(cause: Throwable? = null): Boolean
}
/**
- * Creates a broadcast channel with the specified buffer capacity.
- *
- * The resulting channel type depends on the specified [capacity] parameter:
- *
- * - when `capacity` positive, but less than [UNLIMITED] -- creates `ArrayBroadcastChannel` with a buffer of given capacity.
- * **Note:** this channel looses all items that have been sent to it until the first subscriber appears;
- * - when `capacity` is [CONFLATED] -- creates [ConflatedBroadcastChannel] that conflates back-to-back sends;
- * - when `capacity` is [BUFFERED] -- creates `ArrayBroadcastChannel` with a default capacity.
- * - otherwise -- throws [IllegalArgumentException].
- *
- * **Note: This API is obsolete since 1.5.0 and deprecated for removal since 1.7.0**
- * It is replaced with [SharedFlow][kotlinx.coroutines.flow.SharedFlow] and [StateFlow][kotlinx.coroutines.flow.StateFlow].
+ * @suppress obsolete since 1.5.0, WARNING since 1.7.0, ERROR since 1.9.0
*/
@ObsoleteCoroutinesApi
-@Deprecated(level = DeprecationLevel.WARNING, message = "BroadcastChannel is deprecated in the favour of SharedFlow and StateFlow, and is no longer supported")
+@Deprecated(level = DeprecationLevel.ERROR, message = "BroadcastChannel is deprecated in the favour of SharedFlow and StateFlow, and is no longer supported")
public fun <E> BroadcastChannel(capacity: Int): BroadcastChannel<E> =
when (capacity) {
0 -> throw IllegalArgumentException("Unsupported 0 capacity for BroadcastChannel")
@@ -76,49 +49,28 @@
}
/**
- * Broadcasts the most recently sent element (aka [value]) to all [openSubscription] subscribers.
- *
- * Back-to-send sent elements are _conflated_ -- only the most recently sent value is received,
- * while previously sent elements **are lost**.
- * Every subscriber immediately receives the most recently sent element.
- * Sender to this broadcast channel never suspends and [trySend] always succeeds.
- *
- * A secondary constructor can be used to create an instance of this class that already holds a value.
- * This channel is also created by `BroadcastChannel(Channel.CONFLATED)` factory function invocation.
- *
- * In this implementation, [opening][openSubscription] and [closing][ReceiveChannel.cancel] subscription
- * takes linear time in the number of subscribers.
- *
- * **Note: This API is obsolete since 1.5.0 and deprecated for removal since 1.7.0**
- * It is replaced with [SharedFlow][kotlinx.coroutines.flow.StateFlow].
+ * @suppress obsolete since 1.5.0, WARNING since 1.7.0, ERROR since 1.9.0
*/
@ObsoleteCoroutinesApi
-@Deprecated(level = DeprecationLevel.WARNING, message = "ConflatedBroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
+@Deprecated(level = DeprecationLevel.ERROR, message = "ConflatedBroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
public class ConflatedBroadcastChannel<E> private constructor(
private val broadcast: BroadcastChannelImpl<E>
) : BroadcastChannel<E> by broadcast {
public constructor(): this(BroadcastChannelImpl<E>(capacity = CONFLATED))
/**
- * Creates an instance of this class that already holds a value.
- *
- * It is as a shortcut to creating an instance with a default constructor and
- * immediately sending an element: `ConflatedBroadcastChannel().apply { offer(value) }`.
+ * @suppress
*/
public constructor(value: E) : this() {
trySend(value)
}
/**
- * The most recently sent element to this channel.
- *
- * Access to this property throws [IllegalStateException] when this class is constructed without
- * initial value and no value was sent yet or if it was [closed][close] without a cause.
- * It throws the original [close][SendChannel.close] cause exception if the channel has _failed_.
+ * @suppress
*/
public val value: E get() = broadcast.value
+
/**
- * The most recently sent element to this channel or `null` when this class is constructed without
- * initial value and no value was sent yet or if it was [closed][close].
+ * @suppress
*/
public val valueOrNull: E? get() = broadcast.valueOrNull
}
diff --git a/kotlinx-coroutines-core/common/src/channels/BufferOverflow.kt b/kotlinx-coroutines-core/common/src/channels/BufferOverflow.kt
index d4c131e..ecb02d8 100644
--- a/kotlinx-coroutines-core/common/src/channels/BufferOverflow.kt
+++ b/kotlinx-coroutines-core/common/src/channels/BufferOverflow.kt
@@ -6,24 +6,34 @@
*
* - [SUSPEND] — the upstream that is [sending][SendChannel.send] or
* is [emitting][kotlinx.coroutines.flow.FlowCollector.emit] a value is **suspended** while the buffer is full.
- * - [DROP_OLDEST] — drop **the oldest** value in the buffer on overflow, add the new value to the buffer, do not suspend.
- * - [DROP_LATEST] — drop **the latest** value that is being added to the buffer right now on buffer overflow
- * (so that buffer contents stay the same), do not suspend.
+ * - [DROP_OLDEST] — **the oldest** value in the buffer is dropped on overflow, and the new value is added,
+ * all without suspending.
+ * - [DROP_LATEST] — the buffer remains unchanged on overflow, and the value that we were going to add
+ * gets discarded, all without suspending.
*/
public enum class BufferOverflow {
/**
* Suspend on buffer overflow.
+ *
+ * Use this to create backpressure, forcing the producers to slow down creation of new values in response to
+ * consumers not being able to process the incoming values in time.
+ * [SUSPEND] is a good choice when all elements must eventually be processed.
*/
SUSPEND,
/**
* Drop **the oldest** value in the buffer on overflow, add the new value to the buffer, do not suspend.
+ *
+ * Use this in scenarios when only the last few values are important and skipping the processing of severely
+ * outdated ones is desirable.
*/
DROP_OLDEST,
/**
- * Drop **the latest** value that is being added to the buffer right now on buffer overflow
- * (so that buffer contents stay the same), do not suspend.
+ * Leave the buffer unchanged on overflow, dropping the value that we were going to add, do not suspend.
+ *
+ * This option can be used in rare advanced scenarios where all elements that are expected to enter the buffer are
+ * equal, so it is not important which of them get thrown away.
*/
DROP_LATEST
}
diff --git a/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt b/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt
index fb6846e..f94a9e9 100644
--- a/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt
@@ -7,16 +7,13 @@
import kotlinx.coroutines.channels.ChannelResult.Companion.closed
import kotlinx.coroutines.channels.ChannelResult.Companion.failure
import kotlinx.coroutines.channels.ChannelResult.Companion.success
-import kotlinx.coroutines.flow.internal.*
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.selects.*
import kotlinx.coroutines.selects.TrySelectDetailedResult.*
-import kotlin.contracts.*
import kotlin.coroutines.*
import kotlin.js.*
import kotlin.jvm.*
import kotlin.math.*
-import kotlin.random.*
import kotlin.reflect.*
/**
@@ -658,7 +655,7 @@
}
is ReceiveCatching<*> -> {
this as ReceiveCatching<E>
- cont.tryResume0(success(element), onUndeliveredElement?.bindCancellationFun(element, cont.context))
+ cont.tryResume0(success(element), onUndeliveredElement?.bindCancellationFunResult())
}
is BufferedChannel<*>.BufferedChannelIterator -> {
this as BufferedChannel<E>.BufferedChannelIterator
@@ -666,7 +663,7 @@
}
is CancellableContinuation<*> -> { // `receive()`
this as CancellableContinuation<E>
- tryResume0(element, onUndeliveredElement?.bindCancellationFun(element, context))
+ tryResume0(element, onUndeliveredElement?.bindCancellationFun())
}
else -> error("Unexpected receiver type: $this")
}
@@ -728,7 +725,7 @@
// not dispatched yet. In case `onUndeliveredElement` is
// specified, we need to invoke it in the latter case.
onElementRetrieved = { element ->
- val onCancellation = onUndeliveredElement?.bindCancellationFun(element, cont.context)
+ val onCancellation = onUndeliveredElement?.bindCancellationFun()
cont.resume(element, onCancellation)
},
onClosed = { onClosedReceiveOnNoWaiterSuspend(cont) },
@@ -772,7 +769,7 @@
segment, index, r,
waiter = waiter,
onElementRetrieved = { element ->
- cont.resume(success(element), onUndeliveredElement?.bindCancellationFun(element, cont.context))
+ cont.resume(success(element), onUndeliveredElement?.bindCancellationFunResult())
},
onClosed = { onClosedReceiveCatchingOnNoWaiterSuspend(cont) }
)
@@ -1563,7 +1560,9 @@
@Suppress("UNCHECKED_CAST")
private val onUndeliveredElementReceiveCancellationConstructor: OnCancellationConstructor? = onUndeliveredElement?.let {
{ select: SelectInstance<*>, _: Any?, element: Any? ->
- { if (element !== CHANNEL_CLOSED) onUndeliveredElement.callUndeliveredElement(element as E, select.context) }
+ { _, _, _ ->
+ if (element !== CHANNEL_CLOSED) onUndeliveredElement.callUndeliveredElement(element as E, select.context)
+ }
}
}
@@ -1580,8 +1579,8 @@
* [CancellableContinuation] and [SelectInstance].
*
* Roughly, [hasNext] is a [receive] sibling, while [next] simply
- * returns the already retrieved element. From the implementation
- * side, [receiveResult] stores the element retrieved by [hasNext]
+ * returns the already retrieved element and [hasNext] being idempotent.
+ * From the implementation side, [receiveResult] stores the element retrieved by [hasNext]
* (or a special [CHANNEL_CLOSED] token if the channel is closed).
*
* The [invoke] function is a [CancelHandler] implementation,
@@ -1614,8 +1613,10 @@
private var continuation: CancellableContinuationImpl<Boolean>? = null
// `hasNext()` is just a special receive operation.
- override suspend fun hasNext(): Boolean =
- receiveImpl( // <-- this is an inline function
+ override suspend fun hasNext(): Boolean {
+ return if (this.receiveResult !== NO_RECEIVE_RESULT && this.receiveResult !== CHANNEL_CLOSED) {
+ true
+ } else receiveImpl( // <-- this is an inline function
// Do not create a continuation until it is required;
// it is created later via [onNoWaiterSuspend], if needed.
waiter = null,
@@ -1636,6 +1637,7 @@
// The tail-call optimization is applied here.
onNoWaiterSuspend = { segm, i, r -> return hasNextOnNoWaiterSuspend(segm, i, r) }
)
+ }
private fun onClosedHasNext(): Boolean {
this.receiveResult = CHANNEL_CLOSED
@@ -1664,7 +1666,7 @@
onElementRetrieved = { element ->
this.receiveResult = element
this.continuation = null
- cont.resume(true, onUndeliveredElement?.bindCancellationFun(element, cont.context))
+ cont.resume(true, onUndeliveredElement?.bindCancellationFun(element))
},
onClosed = { onClosedHasNextNoWaiterSuspend() }
)
@@ -1714,7 +1716,7 @@
// Try to resume this `hasNext()`. Importantly, the receiver coroutine
// may be cancelled after it is successfully resumed but not dispatched yet.
// In case `onUndeliveredElement` is specified, we need to invoke it in the latter case.
- return cont.tryResume0(true, onUndeliveredElement?.bindCancellationFun(element, cont.context))
+ return cont.tryResume0(true, onUndeliveredElement?.bindCancellationFun(element))
}
fun tryResumeHasNextOnClosedChannel() {
@@ -2761,6 +2763,34 @@
segment = segment.next!!
}
}
+
+ private fun OnUndeliveredElement<E>.bindCancellationFunResult() = ::onCancellationChannelResultImplDoNotCall
+
+ /**
+ * Do not call directly. Go through [bindCancellationFunResult] to ensure the callback isn't null.
+ * [bindCancellationFunResult] could have just returned a lambda as well, but there would be a risk of that
+ * lambda capturing the environment.
+ */
+ private fun onCancellationChannelResultImplDoNotCall(
+ cause: Throwable, element: ChannelResult<E>, context: CoroutineContext
+ ) {
+ onUndeliveredElement!!.callUndeliveredElement(element.getOrNull()!!, context)
+ }
+
+ private fun OnUndeliveredElement<E>.bindCancellationFun(element: E):
+ (Throwable, Any?, CoroutineContext) -> Unit =
+ { _: Throwable, _, context: CoroutineContext -> callUndeliveredElement(element, context) }
+
+ private fun OnUndeliveredElement<E>.bindCancellationFun() = ::onCancellationImplDoNotCall
+
+ /**
+ * Do not call directly. Go through [bindCancellationFun] to ensure the callback isn't null.
+ * [bindCancellationFun] could have just returned a lambda as well, but there would be a risk of that
+ * lambda capturing the environment.
+ */
+ private fun onCancellationImplDoNotCall(cause: Throwable, element: E, context: CoroutineContext) {
+ onUndeliveredElement!!.callUndeliveredElement(element, context)
+ }
}
/**
@@ -2920,7 +2950,7 @@
*/
private fun <T> CancellableContinuation<T>.tryResume0(
value: T,
- onCancellation: ((cause: Throwable) -> Unit)? = null
+ onCancellation: ((cause: Throwable, value: T, context: CoroutineContext) -> Unit)? = null
): Boolean =
tryResume(value, null, onCancellation).let { token ->
if (token != null) {
diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt
index af906ef..830d786 100644
--- a/kotlinx-coroutines-core/common/src/channels/Channel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt
@@ -116,12 +116,12 @@
* ```
* val events = Channel<Event>(UNLIMITED)
* callbackBasedApi.registerCallback { event ->
- * events.trySend(event)
- * .onClosed { /* channel is already closed, but the callback hasn't stopped yet */ }
+ * events.trySend(event)
+ * .onClosed { /* channel is already closed, but the callback hasn't stopped yet */ }
* }
*
* val uiUpdater = uiScope.launch(Dispatchers.Main) {
- * events.consume { /* handle events */ }
+ * events.consume { /* handle events */ }
* }
* // Stop the callback after the channel is closed or cancelled
* events.invokeOnClose { callbackBasedApi.stop() }
@@ -145,7 +145,7 @@
* 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.
+ * 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
@@ -323,7 +323,7 @@
* 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.
+ * 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.
@@ -475,6 +475,9 @@
override fun toString(): String = "Closed($cause)"
}
+ /**
+ * @suppress **This is internal API and it is subject to change.**
+ */
@InternalCoroutinesApi
public companion object {
private val failed = Failed()
diff --git a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt
index f8ea04c..15534b0 100644
--- a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt
@@ -49,10 +49,43 @@
}
/**
- * 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.
+ * Executes the [block] and then [cancels][ReceiveChannel.cancel] the channel.
*
- * The operation is _terminal_.
+ * It is guaranteed that, after invoking this operation, the channel will be [cancelled][ReceiveChannel.cancel], so
+ * the operation is _terminal_.
+ * If the [block] finishes with an exception, that exception will be used for cancelling the channel and rethrown.
+ *
+ * This function is useful for building more complex terminal operators while ensuring that the producers stop sending
+ * new elements to the channel.
+ *
+ * Example:
+ * ```
+ * suspend fun <E> ReceiveChannel<E>.consumeFirst(): E =
+ * consume { return receive() }
+ * // Launch a coroutine that constantly sends new values
+ * val channel = produce(Dispatchers.Default) {
+ * var i = 0
+ * while (true) {
+ * // Will fail with a `CancellationException`
+ * // after `consumeFirst` finishes.
+ * send(i++)
+ * }
+ * }
+ * // Grab the first value and discard everything else
+ * val firstElement = channel.consumeFirst()
+ * check(firstElement == 0)
+ * // *Note*: some elements could be lost in the channel!
+ * ```
+ *
+ * In this example, the channel will get closed, and the producer coroutine will finish its work after the first
+ * element is obtained.
+ * If `consumeFirst` was implemented as `for (e in this) { return e }` instead, the producer coroutine would be active
+ * until it was cancelled some other way.
+ *
+ * [consume] does not guarantee that new elements will not enter the channel after [block] finishes executing, so
+ * some channel elements may be lost.
+ * Use the `onUndeliveredElement` parameter of a manually created [Channel] to define what should happen with these
+ * elements during [ReceiveChannel.cancel].
*/
public inline fun <E, R> ReceiveChannel<E>.consume(block: ReceiveChannel<E>.() -> R): R {
contract {
@@ -70,12 +103,58 @@
}
/**
- * Performs the given [action] for each received element and [cancels][ReceiveChannel.cancel]
- * the channel after the execution of the block.
- * If you need to iterate over the channel without consuming it, a regular `for` loop should be used instead.
+ * Performs the given [action] for each received element and [cancels][ReceiveChannel.cancel] the channel afterward.
+ *
+ * This function stops processing elements when either the channel is [closed][SendChannel.close],
+ * the coroutine in which the collection is performed gets cancelled and there are no readily available elements in the
+ * channel's buffer,
+ * [action] fails with an exception,
+ * or an early return from [action] happens.
+ * If the [action] finishes with an exception, that exception will be used for cancelling the channel and rethrown.
+ * If the channel is [closed][SendChannel.close] with a cause, this cause will be rethrown from [consumeEach].
+ *
+ * When the channel does not need to be closed after iterating over its elements,
+ * a regular `for` loop (`for (element in channel)`) should be used instead.
*
* The operation is _terminal_.
- * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
+ * This function [consumes][ReceiveChannel.consume] the elements of the original [ReceiveChannel].
+ *
+ * This function is useful in cases when this channel is only expected to have a single consumer that decides when
+ * the producer may stop.
+ * Example:
+ *
+ * ```
+ * val channel = Channel<Int>(1)
+ * // Launch several procedures that create values
+ * repeat(5) {
+ * launch(Dispatchers.Default) {
+ * while (true) {
+ * channel.send(Random.nextInt(40, 50))
+ * }
+ * }
+ * }
+ * // Launch the exclusive consumer
+ * val result = run {
+ * channel.consumeEach {
+ * if (it == 42) {
+ * println("Found the answer")
+ * return@run it // forcibly stop collection
+ * }
+ * }
+ * // *Note*: some elements could be lost in the channel!
+ * }
+ * check(result == 42)
+ * ```
+ *
+ * In this example, several coroutines put elements into a single channel, and a single consumer processes the elements.
+ * Once it finds the elements it's looking for, it stops [consumeEach] by making an early return.
+ *
+ * **Pitfall**: even though the name says "each", some elements could be left unprocessed if they are added after
+ * this function decided to close the channel.
+ * In this case, the elements will simply be lost.
+ * If the elements of the channel are resources that must be closed (like file handles, sockets, etc.),
+ * an `onUndeliveredElement` must be passed to the [Channel] on construction.
+ * It will be called for each element left in the channel at the point of cancellation.
*/
public suspend inline fun <E> ReceiveChannel<E>.consumeEach(action: (E) -> Unit): Unit =
consume {
@@ -83,10 +162,31 @@
}
/**
- * Returns a [List] containing all elements.
+ * Returns a [List] containing all the elements sent to this channel, preserving their order.
+ *
+ * This function will attempt to receive elements and put them into the list until the channel is
+ * [closed][SendChannel.close].
+ * Calling [toList] on channels that are not eventually closed is always incorrect:
+ * - It will suspend indefinitely if the channel is not closed, but no new elements arrive.
+ * - If new elements do arrive and the channel is not eventually closed, [toList] will use more and more memory
+ * until exhausting it.
+ *
+ * If the channel is [closed][SendChannel.close] with a cause, [toList] will rethrow that cause.
*
* The operation is _terminal_.
* This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
+ *
+ * Example:
+ * ```
+ * val values = listOf(1, 5, 2, 9, 3, 3, 1)
+ * // start a new coroutine that creates a channel,
+ * // sends elements to it, and closes it
+ * // once the coroutine's body finishes
+ * val channel = produce {
+ * values.forEach { send(it) }
+ * }
+ * check(channel.toList() == values)
+ * ```
*/
public suspend fun <E> ReceiveChannel<E>.toList(): List<E> = buildList {
consumeEach {
diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBufferedChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBufferedChannel.kt
index 5c7f151..0805c7f 100644
--- a/kotlinx-coroutines-core/common/src/channels/ConflatedBufferedChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBufferedChannel.kt
@@ -1,13 +1,9 @@
package kotlinx.coroutines.channels
-import kotlinx.atomicfu.*
import kotlinx.coroutines.channels.BufferOverflow.*
-import kotlinx.coroutines.channels.ChannelResult.Companion.closed
import kotlinx.coroutines.channels.ChannelResult.Companion.success
import kotlinx.coroutines.internal.*
-import kotlinx.coroutines.internal.OnUndeliveredElement
import kotlinx.coroutines.selects.*
-import kotlin.coroutines.*
/**
* This is a special [BufferedChannel] extension that supports [DROP_OLDEST] and [DROP_LATEST]
diff --git a/kotlinx-coroutines-core/common/src/channels/Deprecated.kt b/kotlinx-coroutines-core/common/src/channels/Deprecated.kt
index acb8daf..463adcb 100644
--- a/kotlinx-coroutines-core/common/src/channels/Deprecated.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Deprecated.kt
@@ -18,7 +18,7 @@
* Safe to remove in 1.9.0 as was inline before.
*/
@ObsoleteCoroutinesApi
-@Suppress("DEPRECATION")
+@Suppress("DEPRECATION_ERROR")
@Deprecated(level = DeprecationLevel.ERROR, message = "BroadcastChannel is deprecated in the favour of SharedFlow and is no longer supported")
public inline fun <E, R> BroadcastChannel<E>.consume(block: ReceiveChannel<E>.() -> R): R {
val channel = openSubscription()
diff --git a/kotlinx-coroutines-core/common/src/channels/Produce.kt b/kotlinx-coroutines-core/common/src/channels/Produce.kt
index 3dd0bb4..7a95559 100644
--- a/kotlinx-coroutines-core/common/src/channels/Produce.kt
+++ b/kotlinx-coroutines-core/common/src/channels/Produce.kt
@@ -19,18 +19,21 @@
}
/**
- * Suspends the current coroutine until the channel is either [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel]
- * and invokes the given [block] before resuming the coroutine.
+ * Suspends the current coroutine until the channel is either
+ * [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel].
*
- * This suspending function is cancellable: if the [Job] of the current coroutine is cancelled while this
- * suspending function is waiting, this function immediately resumes with [CancellationException].
+ * The given [block] will be executed unconditionally before this function returns.
+ * `awaitClose { cleanup() }` is a convenient shorthand for the often useful form
+ * `try { awaitClose() } finally { cleanup() }`.
+ *
+ * This function can only be invoked directly inside the same coroutine that is its receiver.
+ * Specifying the receiver of [awaitClose] explicitly is most probably a mistake.
+ *
+ * This suspending function is cancellable: if the [Job] of the current coroutine is [cancelled][CoroutineScope.cancel]
+ * while this suspending function is waiting, this function immediately resumes with [CancellationException].
* There is a **prompt cancellation guarantee**: even if this function is ready to return, but was cancelled
* while suspended, [CancellationException] will be thrown. See [suspendCancellableCoroutine] for low-level details.
*
- * Note that when the producer channel is cancelled, this function resumes with a cancellation exception.
- * Therefore, in case of cancellation, no code after the call to this function will be executed.
- * That's why this function takes a lambda parameter.
- *
* Example of usage:
* ```
* val callbackEventsStream = produce {
@@ -38,6 +41,21 @@
* awaitClose { disposable.dispose() }
* }
* ```
+ *
+ * Internally, [awaitClose] is implemented using [SendChannel.invokeOnClose].
+ * Currently, every channel can have at most one [SendChannel.invokeOnClose] handler.
+ * This means that calling [awaitClose] several times in a row or combining it with other [SendChannel.invokeOnClose]
+ * invocations is prohibited.
+ * An [IllegalStateException] will be thrown if this rule is broken.
+ *
+ * **Pitfall**: when used in [produce], if the channel is [cancelled][ReceiveChannel.cancel], [awaitClose] can either
+ * return normally or throw a [CancellationException] due to a race condition.
+ * The reason is that, for [produce], cancelling the channel and cancelling the coroutine of the [ProducerScope] is
+ * done simultaneously.
+ *
+ * @throws IllegalStateException if invoked from outside the [ProducerScope] (by leaking `this` outside the producer
+ * coroutine).
+ * @throws IllegalStateException if this channel already has a [SendChannel.invokeOnClose] handler registered.
*/
public suspend fun ProducerScope<*>.awaitClose(block: () -> Unit = {}) {
check(kotlin.coroutines.coroutineContext[Job] === this) { "awaitClose() can only be invoked from the producer context" }
@@ -58,35 +76,169 @@
* object can be used to [receive][ReceiveChannel.receive] elements produced by this coroutine.
*
* The scope of the coroutine contains the [ProducerScope] interface, which implements
- * both [CoroutineScope] and [SendChannel], so that the coroutine can invoke
- * [send][SendChannel.send] directly. The channel is [closed][SendChannel.close]
- * when the coroutine completes.
- * The running coroutine is cancelled when its receive channel is [cancelled][ReceiveChannel.cancel].
+ * both [CoroutineScope] and [SendChannel], so that the coroutine can invoke [send][SendChannel.send] directly.
+ *
+ * The kind of the resulting channel depends on the specified [capacity] parameter.
+ * See the [Channel] interface documentation for details.
+ * By default, an unbuffered channel is created.
+ *
+ * ### Behavior on termination
+ *
+ * The channel is [closed][SendChannel.close] when the coroutine completes.
+ *
+ * ```
+ * val values = listOf(1, 2, 3, 4)
+ * val channel = produce<Int> {
+ * for (value in values) {
+ * send(value)
+ * }
+ * }
+ * check(channel.toList() == values)
+ * ```
+ *
+ * The running coroutine is cancelled when the channel is [cancelled][ReceiveChannel.cancel].
+ *
+ * ```
+ * val channel = produce<Int> {
+ * send(1)
+ * send(2)
+ * try {
+ * send(3) // will throw CancellationException
+ * } catch (e: CancellationException) {
+ * println("The channel was cancelled!)
+ * throw e // always rethrow CancellationException
+ * }
+ * }
+ * check(channel.receive() == 1)
+ * check(channel.receive() == 2)
+ * channel.cancel()
+ * ```
+ *
+ * If this coroutine finishes with an exception, it will close the channel with that exception as the cause and
+ * the resulting channel will become _failed_, so after receiving all the existing elements, all further attempts
+ * to receive from it will throw the exception with which the coroutine finished.
+ *
+ * ```
+ * val produceJob = Job()
+ * // create and populate a channel with a buffer
+ * val channel = produce<Int>(produceJob, capacity = Channel.UNLIMITED) {
+ * repeat(5) { send(it) }
+ * throw TestException()
+ * }
+ * produceJob.join() // wait for `produce` to fail
+ * check(produceJob.isCancelled == true)
+ * // prints 0, 1, 2, 3, 4, then throws `TestException`
+ * for (value in channel) { println(value) }
+ * ```
+ *
+ * When the coroutine is cancelled via structured concurrency and not the `cancel` function,
+ * the channel does not automatically close until the coroutine completes,
+ * so it is possible that some elements will be sent even after the coroutine is cancelled:
+ *
+ * ```
+ * val parentScope = CoroutineScope(Dispatchers.Default)
+ * val channel = parentScope.produce<Int>(capacity = Channel.UNLIMITED) {
+ * repeat(5) {
+ * send(it)
+ * }
+ * parentScope.cancel()
+ * // suspending after this point would fail, but sending succeeds
+ * send(-1)
+ * }
+ * for (c in channel) {
+ * println(c) // 0, 1, 2, 3, 4, -1
+ * } // throws a `CancellationException` exception after reaching -1
+ * ```
+ *
+ * Note that cancelling `produce` via structured concurrency closes the channel with a cause,
+ * making it a _failed_ channel.
+ *
+ * The behavior around coroutine cancellation and error handling is experimental and may change in a future release.
+ *
+ * ### Coroutine context
*
* The coroutine context is inherited from this [CoroutineScope]. Additional context elements can be specified with the [context] argument.
* If the context does not have any dispatcher or other [ContinuationInterceptor], then [Dispatchers.Default] is used.
* The parent job is inherited from the [CoroutineScope] as well, but it can also be overridden
* with a corresponding [context] element.
*
- * Any uncaught exception in this coroutine will close the channel with this exception as the cause and
- * the resulting channel will become _failed_, so that any attempt to receive from it thereafter will throw an exception.
- *
- * The kind of the resulting channel depends on the specified [capacity] parameter.
- * See the [Channel] interface documentation for details.
- *
* See [newCoroutineContext] for a description of debugging facilities available for newly created coroutines.
*
+ * ### Undelivered elements
+ *
+ * Some values that [produce] creates may be lost:
+ *
+ * ```
+ * val channel = produce(Dispatchers.Default, capacity = 5) {
+ * repeat(100) {
+ * send(it)
+ * println("Sent $it")
+ * }
+ * }
+ * channel.cancel() // no elements can be received after this!
+ * ```
+ *
+ * There is no way to recover these lost elements.
+ * If this is unsuitable, please create a [Channel] manually and pass the `onUndeliveredElement` callback to the
+ * constructor: [Channel(onUndeliveredElement = ...)][Channel].
+ *
+ * ### Usage example
+ *
+ * ```
+ * /* Generate random integers until we find the square root of 9801.
+ * To calculate whether the given number is that square root,
+ * use several coroutines that separately process these integers.
+ * Alternatively, we may randomly give up during value generation.
+ * `produce` is used to generate the integers and put them into a
+ * channel, from which the square-computing coroutines take them. */
+ * val parentScope = CoroutineScope(SupervisorJob())
+ * val channel = parentScope.produce<Int>(
+ * Dispatchers.IO,
+ * capacity = 16 // buffer of size 16
+ * ) {
+ * // this code will run on Dispatchers.IO
+ * while (true) {
+ * val request = run {
+ * // simulate waiting for the next request
+ * delay(5.milliseconds)
+ * val randomInt = Random.nextInt(-1, 100)
+ * if (randomInt == -1) {
+ * // external termination request received
+ * println("Producer: no longer accepting requests")
+ * return@produce
+ * }
+ * println("Producer: sending a request ($randomInt)")
+ * randomInt
+ * }
+ * send(request)
+ * }
+ * }
+ * // Launch consumers
+ * repeat(4) {
+ * launch(Dispatchers.Default) {
+ * for (request in channel) {
+ * // simulate processing a request
+ * delay(25.milliseconds)
+ * println("Consumer $it: received a request ($request)")
+ * if (request * request == 9801) {
+ * println("Consumer $it found the square root of 9801!")
+ * /* the work is done, the producer may finish.
+ * the internal termination request will cancel
+ * the producer on the next suspension point. */
+ * channel.cancel()
+ * }
+ * }
+ * }
+ * }
+ * ```
+ *
* **Note: This is an experimental api.** Behaviour of producers that work as children in a parent scope with respect
* to cancellation and error handling may change in the future.
- *
- * @param context additional to [CoroutineScope.coroutineContext] context of the coroutine.
- * @param capacity capacity of the channel's buffer (no buffer by default).
- * @param block the coroutine code.
*/
@ExperimentalCoroutinesApi
public fun <E> CoroutineScope.produce(
context: CoroutineContext = EmptyCoroutineContext,
- capacity: Int = 0,
+ capacity: Int = Channel.RENDEZVOUS,
@BuilderInference block: suspend ProducerScope<E>.() -> Unit
): ReceiveChannel<E> =
produce(context, capacity, BufferOverflow.SUSPEND, CoroutineStart.DEFAULT, onCompletion = null, block = block)
diff --git a/kotlinx-coroutines-core/common/src/flow/Channels.kt b/kotlinx-coroutines-core/common/src/flow/Channels.kt
index 7bfa499..2d509ad 100644
--- a/kotlinx-coroutines-core/common/src/flow/Channels.kt
+++ b/kotlinx-coroutines-core/common/src/flow/Channels.kt
@@ -132,25 +132,6 @@
}
/**
- * Represents the given broadcast channel as a hot flow.
- * Every flow collector will trigger a new broadcast channel subscription.
- *
- * ### Cancellation semantics
- * 1) Flow consumer is cancelled when the original channel is cancelled.
- * 2) Flow consumer completes normally when the original channel completes (~is closed) normally.
- * 3) If the flow consumer fails with an exception, subscription is cancelled.
- */
-@Suppress("DEPRECATION")
-@Deprecated(
- level = DeprecationLevel.ERROR,
- message = "'BroadcastChannel' is obsolete and all corresponding operators are deprecated " +
- "in the favour of StateFlow and SharedFlow"
-) // Since 1.5.0, ERROR since 1.7.0, was @FlowPreview, safe to remove in 1.8.0
-public fun <T> BroadcastChannel<T>.asFlow(): Flow<T> = flow {
- emitAll(openSubscription())
-}
-
-/**
* Creates a [produce] coroutine that collects the given flow.
*
* This transformation is **stateful**, it launches a [produce] coroutine
diff --git a/kotlinx-coroutines-core/common/src/flow/Flow.kt b/kotlinx-coroutines-core/common/src/flow/Flow.kt
index 44e5d7f..e3173f4 100644
--- a/kotlinx-coroutines-core/common/src/flow/Flow.kt
+++ b/kotlinx-coroutines-core/common/src/flow/Flow.kt
@@ -102,13 +102,13 @@
*
* ```
* val myFlow = flow {
- * // GlobalScope.launch { // is prohibited
- * // launch(Dispatchers.IO) { // is prohibited
- * // withContext(CoroutineName("myFlow")) { // is prohibited
- * emit(1) // OK
- * coroutineScope {
- * emit(2) // OK -- still the same coroutine
- * }
+ * // GlobalScope.launch { // is prohibited
+ * // launch(Dispatchers.IO) { // is prohibited
+ * // withContext(CoroutineName("myFlow")) { // is prohibited
+ * emit(1) // OK
+ * coroutineScope {
+ * emit(2) // OK -- still the same coroutine
+ * }
* }
* ```
*
@@ -119,11 +119,11 @@
*
* If you are looking for performance and are sure that no concurrent emits and context jumps will happen,
* the [flow] builder can be used alongside a [coroutineScope] or [supervisorScope] instead:
- * - Scoped primitive should be used to provide a [CoroutineScope].
- * - Changing the context of emission is prohibited, no matter whether it is `withContext(ctx)` or
- * a builder argument (e.g. `launch(ctx)`).
- * - Collecting another flow from a separate context is allowed, but it has the same effect as
- * applying the [flowOn] operator to that flow, which is more efficient.
+ * - Scoped primitive should be used to provide a [CoroutineScope].
+ * - Changing the context of emission is prohibited, no matter whether it is `withContext(ctx)` or
+ * a builder argument (e.g. `launch(ctx)`).
+ * - Collecting another flow from a separate context is allowed, but it has the same effect as
+ * applying the [flowOn] operator to that flow, which is more efficient.
*
* ### Exception transparency
*
diff --git a/kotlinx-coroutines-core/common/src/flow/Migration.kt b/kotlinx-coroutines-core/common/src/flow/Migration.kt
index da36bf6..45b8abc 100644
--- a/kotlinx-coroutines-core/common/src/flow/Migration.kt
+++ b/kotlinx-coroutines-core/common/src/flow/Migration.kt
@@ -90,7 +90,7 @@
* }
* }
* ```
- * Opposed to subscribeOn, it it **possible** to use multiple `flowOn` operators in the one flow
+ * Opposed to subscribeOn, it is **possible** to use multiple `flowOn` operators in the one flow
* @suppress
*/
@Deprecated(message = "Use 'flowOn' instead", level = DeprecationLevel.ERROR)
@@ -443,8 +443,8 @@
level = DeprecationLevel.ERROR,
message = "Flow analogue of 'publish()' is 'shareIn'. \n" +
"publish().connect() is the default strategy (no extra call is needed), \n" +
- "publish().autoConnect() translates to 'started = SharingStared.Lazily' argument, \n" +
- "publish().refCount() translates to 'started = SharingStared.WhileSubscribed()' argument.",
+ "publish().autoConnect() translates to 'started = SharingStarted.Lazily' argument, \n" +
+ "publish().refCount() translates to 'started = SharingStarted.WhileSubscribed()' argument.",
replaceWith = ReplaceWith("this.shareIn(scope, 0)")
)
public fun <T> Flow<T>.publish(): Flow<T> = noImpl()
@@ -454,8 +454,8 @@
level = DeprecationLevel.ERROR,
message = "Flow analogue of 'publish(bufferSize)' is 'buffer' followed by 'shareIn'. \n" +
"publish().connect() is the default strategy (no extra call is needed), \n" +
- "publish().autoConnect() translates to 'started = SharingStared.Lazily' argument, \n" +
- "publish().refCount() translates to 'started = SharingStared.WhileSubscribed()' argument.",
+ "publish().autoConnect() translates to 'started = SharingStarted.Lazily' argument, \n" +
+ "publish().refCount() translates to 'started = SharingStarted.WhileSubscribed()' argument.",
replaceWith = ReplaceWith("this.buffer(bufferSize).shareIn(scope, 0)")
)
public fun <T> Flow<T>.publish(bufferSize: Int): Flow<T> = noImpl()
@@ -465,8 +465,8 @@
level = DeprecationLevel.ERROR,
message = "Flow analogue of 'replay()' is 'shareIn' with unlimited replay. \n" +
"replay().connect() is the default strategy (no extra call is needed), \n" +
- "replay().autoConnect() translates to 'started = SharingStared.Lazily' argument, \n" +
- "replay().refCount() translates to 'started = SharingStared.WhileSubscribed()' argument.",
+ "replay().autoConnect() translates to 'started = SharingStarted.Lazily' argument, \n" +
+ "replay().refCount() translates to 'started = SharingStarted.WhileSubscribed()' argument.",
replaceWith = ReplaceWith("this.shareIn(scope, Int.MAX_VALUE)")
)
public fun <T> Flow<T>.replay(): Flow<T> = noImpl()
@@ -476,8 +476,8 @@
level = DeprecationLevel.ERROR,
message = "Flow analogue of 'replay(bufferSize)' is 'shareIn' with the specified replay parameter. \n" +
"replay().connect() is the default strategy (no extra call is needed), \n" +
- "replay().autoConnect() translates to 'started = SharingStared.Lazily' argument, \n" +
- "replay().refCount() translates to 'started = SharingStared.WhileSubscribed()' argument.",
+ "replay().autoConnect() translates to 'started = SharingStarted.Lazily' argument, \n" +
+ "replay().refCount() translates to 'started = SharingStarted.WhileSubscribed()' argument.",
replaceWith = ReplaceWith("this.shareIn(scope, bufferSize)")
)
public fun <T> Flow<T>.replay(bufferSize: Int): Flow<T> = noImpl()
@@ -485,7 +485,7 @@
/** @suppress */
@Deprecated(
level = DeprecationLevel.ERROR,
- message = "Flow analogue of 'cache()' is 'shareIn' with unlimited replay and 'started = SharingStared.Lazily' argument'",
- replaceWith = ReplaceWith("this.shareIn(scope, Int.MAX_VALUE, started = SharingStared.Lazily)")
+ message = "Flow analogue of 'cache()' is 'shareIn' with unlimited replay and 'started = SharingStarted.Lazily' argument'",
+ replaceWith = ReplaceWith("this.shareIn(scope, started = SharingStarted.Lazily, replay = Int.MAX_VALUE)")
)
public fun <T> Flow<T>.cache(): Flow<T> = noImpl()
diff --git a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt
index 55caefd..4f19641 100644
--- a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt
+++ b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt
@@ -86,11 +86,11 @@
* It has the following important differences:
*
* - `SharedFlow` is simpler, because it does not have to implement all the [Channel] APIs, which allows
- * for faster and simpler implementation.
+ * for faster and simpler implementation.
* - `SharedFlow` supports configurable replay and buffer overflow strategy.
* - `SharedFlow` has a clear separation into a read-only `SharedFlow` interface and a [MutableSharedFlow].
* - `SharedFlow` cannot be closed like `BroadcastChannel` and can never represent a failure.
- * All errors and completion signals should be explicitly _materialized_ if needed.
+ * All errors and completion signals should be explicitly _materialized_ if needed.
*
* To migrate [BroadcastChannel] usage to [SharedFlow], start by replacing usages of the `BroadcastChannel(capacity)`
* constructor with `MutableSharedFlow(0, extraBufferCapacity=capacity)` (broadcast channel does not replay
@@ -119,6 +119,8 @@
* might be added to this interface in the future, but is stable for use.
* Use the `MutableSharedFlow(replay, ...)` constructor function to create an implementation.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(ExperimentalForInheritanceCoroutinesApi::class)
public interface SharedFlow<out T> : Flow<T> {
/**
* A snapshot of the replay cache.
@@ -138,7 +140,7 @@
* ```
* val flow = MutableSharedFlow<Int>()
* launch(start = CoroutineStart.UNDISPATCHED) {
- * flow.collect { println(1) }
+ * flow.collect { println(1) }
* }
* flow.emit(1)
* ```
@@ -170,6 +172,8 @@
* might be added to this interface in the future, but is stable for use.
* Use the `MutableSharedFlow(...)` constructor function to create an implementation.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(ExperimentalForInheritanceCoroutinesApi::class)
public interface MutableSharedFlow<T> : SharedFlow<T>, FlowCollector<T> {
/**
* Emits a [value] to this shared flow, suspending on buffer overflow.
@@ -309,6 +313,7 @@
}
}
+@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
internal open class SharedFlowImpl<T>(
private val replay: Int,
private val bufferCapacity: Int,
diff --git a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt
index dbb9507..ab48dbc 100644
--- a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt
+++ b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt
@@ -90,12 +90,12 @@
* for faster, garbage-free implementation, unlike `ConflatedBroadcastChannel` implementation that
* allocates objects on each emitted value.
* - `StateFlow` always has a value which can be safely read at any time via [value] property.
- * Unlike `ConflatedBroadcastChannel`, there is no way to create a state flow without a value.
+ * Unlike `ConflatedBroadcastChannel`, there is no way to create a state flow without a value.
* - `StateFlow` has a clear separation into a read-only `StateFlow` interface and a [MutableStateFlow].
* - `StateFlow` conflation is based on equality like [distinctUntilChanged] operator,
- * unlike conflation in `ConflatedBroadcastChannel` that is based on reference identity.
+ * unlike conflation in `ConflatedBroadcastChannel` that is based on reference identity.
* - `StateFlow` cannot be closed like `ConflatedBroadcastChannel` and can never represent a failure.
- * All errors and completion signals should be explicitly _materialized_ if needed.
+ * All errors and completion signals should be explicitly _materialized_ if needed.
*
* `StateFlow` is designed to better cover typical use-cases of keeping track of state changes in time, taking
* more pragmatic design choices for the sake of convenience.
@@ -130,6 +130,8 @@
* might be added to this interface in the future, but is stable for use.
* Use the `MutableStateFlow(value)` constructor function to create an implementation.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(ExperimentalForInheritanceCoroutinesApi::class)
public interface StateFlow<out T> : SharedFlow<T> {
/**
* The current value of this state flow.
@@ -151,6 +153,8 @@
* might be added to this interface in the future, but is stable for use.
* Use the `MutableStateFlow()` constructor function to create an implementation.
*/
+@OptIn(ExperimentalSubclassOptIn::class)
+@SubclassOptInRequired(ExperimentalForInheritanceCoroutinesApi::class)
public interface MutableStateFlow<T> : StateFlow<T>, MutableSharedFlow<T> {
/**
* The current value of this state flow.
@@ -305,6 +309,7 @@
}
}
+@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
private class StateFlowImpl<T>(
initialState: Any // T | NULL
) : AbstractSharedFlow<StateFlowSlot>(), MutableStateFlow<T>, CancellableFlow<T>, FusibleFlow<T> {
diff --git a/kotlinx-coroutines-core/common/src/flow/internal/AbstractSharedFlow.kt b/kotlinx-coroutines-core/common/src/flow/internal/AbstractSharedFlow.kt
index da239ff..6831ad7 100644
--- a/kotlinx-coroutines-core/common/src/flow/internal/AbstractSharedFlow.kt
+++ b/kotlinx-coroutines-core/common/src/flow/internal/AbstractSharedFlow.kt
@@ -1,5 +1,6 @@
package kotlinx.coroutines.flow.internal
+import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.internal.*
@@ -113,6 +114,7 @@
*
* To avoid that (especially in a more complex scenarios), we do not conflate subscription updates.
*/
+@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
private class SubscriptionCountStateFlow(initialValue: Int) : StateFlow<Int>,
SharedFlowImpl<Int>(1, Int.MAX_VALUE, BufferOverflow.DROP_OLDEST)
{
diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt b/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt
index d40a92d..dc3b709 100644
--- a/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt
+++ b/kotlinx-coroutines-core/common/src/flow/operators/Limit.kt
@@ -123,7 +123,7 @@
val collector = object : FlowCollector<T> {
override suspend fun emit(value: T) {
// Note: we are checking predicate first, then throw. If the predicate does suspend (calls emit, for example)
- // the the resulting code is never tail-suspending and produces a state-machine
+ // the resulting code is never tail-suspending and produces a state-machine
if (!predicate(value)) {
throw AbortFlowException(this)
}
diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
index 55c7f13..92ebeab 100644
--- a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
+++ b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
@@ -92,11 +92,11 @@
/*
* This is a fuseable implementation of the following operator:
* channelFlow {
- * forEach { flow ->
- * launch {
- * flow.collect { send(it) }
- * }
- * }
+ * forEach { flow ->
+ * launch {
+ * flow.collect { send(it) }
+ * }
+ * }
* }
*/
return ChannelLimitedFlowMerge(this)
diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Share.kt b/kotlinx-coroutines-core/common/src/flow/operators/Share.kt
index a413c90..5ab761a 100644
--- a/kotlinx-coroutines-core/common/src/flow/operators/Share.kt
+++ b/kotlinx-coroutines-core/common/src/flow/operators/Share.kt
@@ -363,6 +363,7 @@
public fun <T> MutableStateFlow<T>.asStateFlow(): StateFlow<T> =
ReadonlyStateFlow(this, null)
+@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
private class ReadonlySharedFlow<T>(
flow: SharedFlow<T>,
@Suppress("unused")
@@ -372,6 +373,7 @@
fuseSharedFlow(context, capacity, onBufferOverflow)
}
+@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
private class ReadonlyStateFlow<T>(
flow: StateFlow<T>,
@Suppress("unused")
@@ -397,6 +399,7 @@
public fun <T> SharedFlow<T>.onSubscription(action: suspend FlowCollector<T>.() -> Unit): SharedFlow<T> =
SubscribedSharedFlow(this, action)
+@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
private class SubscribedSharedFlow<T>(
private val sharedFlow: SharedFlow<T>,
private val action: suspend FlowCollector<T>.() -> Unit
diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt
index 2f43cfa..f3c9be1 100644
--- a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt
+++ b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt
@@ -129,3 +129,38 @@
emit(accumulator as T)
}
}
+
+/**
+ * Splits the given flow into a flow of non-overlapping lists each not exceeding the given [size] but never empty.
+ * The last emitted list may have fewer elements than the given size.
+ *
+ * Example of usage:
+ * ```
+ * flowOf("a", "b", "c", "d", "e")
+ * .chunked(2) // ["a", "b"], ["c", "d"], ["e"]
+ * .map { it.joinToString(separator = "") }
+ * .collect {
+ * println(it) // Prints "ab", "cd", e"
+ * }
+ * ```
+ *
+ * @throws IllegalArgumentException if [size] is not positive.
+ */
+@ExperimentalCoroutinesApi
+public fun <T> Flow<T>.chunked(size: Int): Flow<List<T>> {
+ require(size >= 1) { "Expected positive chunk size, but got $size" }
+ return flow {
+ var result: ArrayList<T>? = null // Do not preallocate anything
+ collect { value ->
+ // Allocate if needed
+ val acc = result ?: ArrayList<T>(size).also { result = it }
+ acc.add(value)
+ if (acc.size == size) {
+ emit(acc)
+ // Cleanup, but don't allocate -- it might've been the case this is the last element
+ result = null
+ }
+ }
+ result?.let { emit(it) }
+ }
+}
diff --git a/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt b/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt
index 5add0fd..f55970e 100644
--- a/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt
+++ b/kotlinx-coroutines-core/common/src/flow/terminal/Collect.kt
@@ -40,7 +40,7 @@
* .launchIn(uiScope)
* ```
*
- * Note that the resulting value of [launchIn] is not used and the provided scope takes care of cancellation.
+ * In this example, note that the `job` returned by [launchIn] is not used, and the provided scope takes care of cancellation.
*/
public fun <T> Flow<T>.launchIn(scope: CoroutineScope): Job = scope.launch {
collect() // tail-call
diff --git a/kotlinx-coroutines-core/common/src/internal/Atomic.kt b/kotlinx-coroutines-core/common/src/internal/Atomic.kt
deleted file mode 100644
index 60e06ec..0000000
--- a/kotlinx-coroutines-core/common/src/internal/Atomic.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE")
-
-package kotlinx.coroutines.internal
-
-import kotlinx.atomicfu.atomic
-import kotlinx.coroutines.*
-import kotlin.jvm.*
-
-/**
- * The most abstract operation that can be in process. Other threads observing an instance of this
- * class in the fields of their object shall invoke [perform] to help.
- *
- * @suppress **This is unstable API and it is subject to change.**
- */
-public abstract class OpDescriptor {
- /**
- * Returns `null` is operation was performed successfully or some other
- * object that indicates the failure reason.
- */
- abstract fun perform(affected: Any?): Any?
-
- /**
- * Returns reference to atomic operation that this descriptor is a part of or `null`
- * if not a part of any [AtomicOp].
- */
- abstract val atomicOp: AtomicOp<*>?
-
- override fun toString(): String = "$classSimpleName@$hexAddress" // debug
-}
-
-@JvmField
-internal val NO_DECISION: Any = Symbol("NO_DECISION")
-
-/**
- * Descriptor for multi-word atomic operation.
- * Based on paper
- * ["A Practical Multi-Word Compare-and-Swap Operation"](https://www.cl.cam.ac.uk/research/srg/netos/papers/2002-casn.pdf)
- * by Timothy L. Harris, Keir Fraser and Ian A. Pratt.
- *
- * Note: parts of atomic operation must be globally ordered. Otherwise, this implementation will produce
- * `StackOverflowError`.
- *
- * @suppress **This is unstable API and it is subject to change.**
- */
-@InternalCoroutinesApi
-public abstract class AtomicOp<in T> : OpDescriptor() {
- private val _consensus = atomic<Any?>(NO_DECISION)
-
- override val atomicOp: AtomicOp<*> get() = this
-
- private fun decide(decision: Any?): Any? {
- assert { decision !== NO_DECISION }
- val current = _consensus.value
- if (current !== NO_DECISION) return current
- if (_consensus.compareAndSet(NO_DECISION, decision)) return decision
- return _consensus.value
- }
-
- abstract fun prepare(affected: T): Any? // `null` if Ok, or failure reason
-
- abstract fun complete(affected: T, failure: Any?) // failure != null if failed to prepare op
-
- // returns `null` on success
- @Suppress("UNCHECKED_CAST")
- final override fun perform(affected: Any?): Any? {
- // make decision on status
- var decision = this._consensus.value
- if (decision === NO_DECISION) {
- decision = decide(prepare(affected as T))
- }
- // complete operation
- complete(affected as T, decision)
- return decision
- }
-}
diff --git a/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt b/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt
index 417e9a8..0be8a10 100644
--- a/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt
+++ b/kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt
@@ -21,11 +21,11 @@
internal expect annotation class BenignDataRace()
// Used **only** as a workaround for #3820 in StateFlow. Do not use anywhere else
-internal expect class WorkaroundAtomicReference<T>(value: T) {
- public fun get(): T
- public fun set(value: T)
- public fun getAndSet(value: T): T
- public fun compareAndSet(expected: T, value: T): Boolean
+internal expect class WorkaroundAtomicReference<V>(value: V) {
+ public fun get(): V
+ public fun set(value: V)
+ public fun getAndSet(value: V): V
+ public fun compareAndSet(expected: V, value: V): Boolean
}
@Suppress("UNUSED_PARAMETER", "EXTENSION_SHADOWED_BY_MEMBER")
diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt
index e21f1ed..26e7c5a 100644
--- a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt
+++ b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt
@@ -9,10 +9,8 @@
@JvmField
internal val REUSABLE_CLAIMED = Symbol("REUSABLE_CLAIMED")
-@PublishedApi
internal class DispatchedContinuation<in T>(
@JvmField internal val dispatcher: CoroutineDispatcher,
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
@JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation {
@JvmField
@@ -188,7 +186,6 @@
get() = this
override fun resumeWith(result: Result<T>) {
- val context = continuation.context
val state = result.toState()
if (dispatcher.isDispatchNeeded(context)) {
_state = state
@@ -196,7 +193,7 @@
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_ATOMIC) {
- withCoroutineContext(this.context, countOrElement) {
+ withCoroutineContext(context, countOrElement) {
continuation.resumeWith(result)
}
}
@@ -206,11 +203,8 @@
// We inline it to save an entry on the stack in cases where it shows (unconfined dispatcher)
// It is used only in Continuation<T>.resumeCancellableWith
@Suppress("NOTHING_TO_INLINE")
- internal inline fun resumeCancellableWith(
- result: Result<T>,
- noinline onCancellation: ((cause: Throwable) -> Unit)?
- ) {
- val state = result.toState(onCancellation)
+ internal inline fun resumeCancellableWith(result: Result<T>) {
+ val state = result.toState()
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
@@ -224,15 +218,6 @@
}
}
- // takeState had already cleared the state so we cancel takenState here
- override fun cancelCompletedResult(takenState: Any?, cause: Throwable) {
- // It is Ok to call onCancellation here without try/catch around it, since this function only faces
- // a "bound" cancellation handler that performs the safe call to the user-specified code.
- if (takenState is CompletedWithCancellation) {
- takenState.onCancellation(cause)
- }
- }
-
// inline here is to save us an entry on the stack for the sake of better stacktraces
@Suppress("NOTHING_TO_INLINE")
internal inline fun resumeCancelled(state: Any?): Boolean {
@@ -273,9 +258,8 @@
@InternalCoroutinesApi
public fun <T> Continuation<T>.resumeCancellableWith(
result: Result<T>,
- onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
- is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
+ is DispatchedContinuation -> resumeCancellableWith(result)
else -> resumeWith(result)
}
diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt
index 5a38d0d..309685b 100644
--- a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt
+++ b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt
@@ -43,10 +43,8 @@
internal val Int.isCancellableMode get() = this == MODE_CANCELLABLE || this == MODE_CANCELLABLE_REUSABLE
internal val Int.isReusableMode get() = this == MODE_CANCELLABLE_REUSABLE
-@PublishedApi
internal abstract class DispatchedTask<in T> internal constructor(
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
- @JvmField public var resumeMode: Int
+ @JvmField var resumeMode: Int
) : SchedulerTask() {
internal abstract val delegate: Continuation<T>
@@ -78,7 +76,6 @@
final override fun run() {
assert { resumeMode != MODE_UNINITIALIZED } // should have been set before dispatching
- val taskContext = this.taskContext
var fatalException: Throwable? = null
try {
val delegate = delegate as DispatchedContinuation<T>
@@ -109,8 +106,7 @@
// This instead of runCatching to have nicer stacktrace and debug experience
fatalException = e
} finally {
- val result = runCatching { taskContext.afterTask() }
- handleFatalException(fatalException, result.exceptionOrNull())
+ fatalException?.let { handleFatalException(it) }
}
}
@@ -132,15 +128,9 @@
* Fatal exception handling can be intercepted with [CoroutineExceptionHandler] element in the context of
* a failed coroutine, but such exceptions should be reported anyway.
*/
- internal fun handleFatalException(exception: Throwable?, finallyException: Throwable?) {
- if (exception === null && finallyException === null) return
- if (exception !== null && finallyException !== null) {
- exception.addSuppressed(finallyException)
- }
-
- val cause = exception ?: finallyException
+ internal fun handleFatalException(exception: Throwable) {
val reason = CoroutinesInternalError("Fatal exception in coroutines machinery for $this. " +
- "Please read KDoc to 'handleFatalException' method and report this incident to maintainers", cause!!)
+ "Please read KDoc to 'handleFatalException' method and report this incident to maintainers", exception)
handleCoroutineException(this.delegate.context, reason)
}
}
@@ -205,7 +195,7 @@
* This exception doesn't happen normally, only if we have a bug in implementation.
* Report it as a fatal exception.
*/
- handleFatalException(e, null)
+ handleFatalException(e)
} finally {
eventLoop.decrementUseCount(unconfined = true)
}
diff --git a/kotlinx-coroutines-core/common/src/internal/LimitedDispatcher.kt b/kotlinx-coroutines-core/common/src/internal/LimitedDispatcher.kt
index 0c8e077..eb51961 100644
--- a/kotlinx-coroutines-core/common/src/internal/LimitedDispatcher.kt
+++ b/kotlinx-coroutines-core/common/src/internal/LimitedDispatcher.kt
@@ -21,7 +21,8 @@
*/
internal class LimitedDispatcher(
private val dispatcher: CoroutineDispatcher,
- private val parallelism: Int
+ private val parallelism: Int,
+ private val name: String?
) : CoroutineDispatcher(), Delay by (dispatcher as? Delay ?: DefaultDelay) {
// Atomic is necessary here for the sake of K/N memory ordering,
@@ -33,11 +34,10 @@
// A separate object that we can synchronize on for K/N
private val workerAllocationLock = SynchronizedObject()
- @ExperimentalCoroutinesApi
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
parallelism.checkParallelism()
- if (parallelism >= this.parallelism) return this
- return super.limitedParallelism(parallelism)
+ if (parallelism >= this.parallelism) return namedOrThis(name)
+ return super.limitedParallelism(parallelism, name)
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
@@ -95,6 +95,8 @@
}
}
+ override fun toString() = name ?: "$dispatcher.limitedParallelism($parallelism)"
+
/**
* A worker that polls the queue and runs tasks until there are no more of them.
*
@@ -125,5 +127,9 @@
}
}
-// Save a few bytecode ops
internal fun Int.checkParallelism() = require(this >= 1) { "Expected positive parallelism level, but got $this" }
+
+internal fun CoroutineDispatcher.namedOrThis(name: String?): CoroutineDispatcher {
+ if (name != null) return NamedDispatcher(this, name)
+ return this
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/common/src/internal/LocalAtomics.common.kt b/kotlinx-coroutines-core/common/src/internal/LocalAtomics.common.kt
index 7124f74..aea07be 100644
--- a/kotlinx-coroutines-core/common/src/internal/LocalAtomics.common.kt
+++ b/kotlinx-coroutines-core/common/src/internal/LocalAtomics.common.kt
@@ -5,13 +5,11 @@
* where atomicfu doesn't support its tranformations.
*
* Have `Local` prefix to avoid AFU clashes during star-imports
+ *
+ * TODO: remove after https://youtrack.jetbrains.com/issue/KT-62423/
*/
internal expect class LocalAtomicInt(value: Int) {
fun get(): Int
fun set(value: Int)
fun decrementAndGet(): Int
}
-
-internal inline var LocalAtomicInt.value
- get() = get()
- set(value) = set(value)
diff --git a/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt b/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt
index 456a3b7..32209fc 100644
--- a/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt
+++ b/kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt
@@ -2,24 +2,24 @@
package kotlinx.coroutines.internal
-import kotlinx.coroutines.*
-import kotlin.jvm.*
-
/** @suppress **This is unstable API and it is subject to change.** */
public expect open class LockFreeLinkedListNode() {
public val isRemoved: Boolean
public val nextNode: LockFreeLinkedListNode
public val prevNode: LockFreeLinkedListNode
- public fun addLast(node: LockFreeLinkedListNode)
+ public fun addLast(node: LockFreeLinkedListNode, permissionsBitmask: Int): Boolean
public fun addOneIfEmpty(node: LockFreeLinkedListNode): Boolean
- public inline fun addLastIf(node: LockFreeLinkedListNode, crossinline condition: () -> Boolean): Boolean
public open fun remove(): Boolean
+ /**
+ * Closes the list for anything that requests the permission [forbiddenElementsBit].
+ * Only a single permission can be forbidden at a time, but this isn't checked.
+ */
+ public fun close(forbiddenElementsBit: Int)
}
/** @suppress **This is unstable API and it is subject to change.** */
public expect open class LockFreeLinkedListHead() : LockFreeLinkedListNode {
- public val isEmpty: Boolean
- public inline fun <reified T : LockFreeLinkedListNode> forEach(block: (T) -> Unit)
+ public inline fun forEach(block: (LockFreeLinkedListNode) -> Unit)
public final override fun remove(): Nothing
}
diff --git a/kotlinx-coroutines-core/common/src/internal/NamedDispatcher.kt b/kotlinx-coroutines-core/common/src/internal/NamedDispatcher.kt
new file mode 100644
index 0000000..72dbd65
--- /dev/null
+++ b/kotlinx-coroutines-core/common/src/internal/NamedDispatcher.kt
@@ -0,0 +1,25 @@
+package kotlinx.coroutines.internal
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.DefaultDelay
+import kotlin.coroutines.*
+
+/**
+ * Wrapping dispatcher that has a nice user-supplied `toString()` representation
+ */
+internal class NamedDispatcher(
+ private val dispatcher: CoroutineDispatcher,
+ private val name: String
+) : CoroutineDispatcher(), Delay by (dispatcher as? Delay ?: DefaultDelay) {
+
+ override fun isDispatchNeeded(context: CoroutineContext): Boolean = dispatcher.isDispatchNeeded(context)
+
+ override fun dispatch(context: CoroutineContext, block: Runnable) = dispatcher.dispatch(context, block)
+
+ @InternalCoroutinesApi
+ override fun dispatchYield(context: CoroutineContext, block: Runnable) = dispatcher.dispatchYield(context, block)
+
+ override fun toString(): String {
+ return name
+ }
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/common/src/internal/OnUndeliveredElement.kt b/kotlinx-coroutines-core/common/src/internal/OnUndeliveredElement.kt
index 11d7d0b..5ed99d3 100644
--- a/kotlinx-coroutines-core/common/src/internal/OnUndeliveredElement.kt
+++ b/kotlinx-coroutines-core/common/src/internal/OnUndeliveredElement.kt
@@ -29,9 +29,6 @@
}
}
-internal fun <E> OnUndeliveredElement<E>.bindCancellationFun(element: E, context: CoroutineContext): (Throwable) -> Unit =
- { _: Throwable -> callUndeliveredElement(element, context) }
-
/**
* Internal exception that is thrown when [OnUndeliveredElement] handler in
* a [kotlinx.coroutines.channels.Channel] throws an exception.
diff --git a/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt b/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt
index 463599b..a76364d 100644
--- a/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt
+++ b/kotlinx-coroutines-core/common/src/internal/ProbesSupport.common.kt
@@ -3,3 +3,5 @@
import kotlin.coroutines.*
internal expect inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T>
+
+internal expect inline fun <T> probeCoroutineResumed(completion: Continuation<T>): Unit
diff --git a/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt b/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt
index 1ba8a71..2f9a434 100644
--- a/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt
+++ b/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt
@@ -8,6 +8,8 @@
/**
* Use this function to start coroutine in a cancellable way, so that it can be cancelled
* while waiting to be dispatched.
+ *
+ * @suppress **This is internal API and it is subject to change.**
*/
@InternalCoroutinesApi
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
@@ -20,11 +22,9 @@
*/
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
receiver: R, completion: Continuation<T>,
- onCancellation: ((cause: Throwable) -> Unit)? = null
-) =
- runSafely(completion) {
- createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
- }
+) = runSafely(completion) {
+ createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit))
+}
/**
* Similar to [startCoroutineCancellable], but for already created coroutine.
diff --git a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt
index 3e086e3..0d2e040 100644
--- a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt
+++ b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt
@@ -6,38 +6,19 @@
import kotlin.coroutines.intrinsics.*
/**
- * Use this function to restart a coroutine directly from inside of [suspendCoroutine],
- * when the code is already in the context of this coroutine.
- * It does not use [ContinuationInterceptor] and does not update the context of the current thread.
- */
-internal fun <T> (suspend () -> T).startCoroutineUnintercepted(completion: Continuation<T>) {
- startDirect(completion) { actualCompletion ->
- startCoroutineUninterceptedOrReturn(actualCompletion)
- }
-}
-
-/**
* Use this function to start a new coroutine in [CoroutineStart.UNDISPATCHED] mode —
* immediately execute the coroutine in the current thread until the next suspension.
* It does not use [ContinuationInterceptor], but updates the context of the current thread for the new coroutine.
*/
internal fun <R, T> (suspend (R) -> T).startCoroutineUndispatched(receiver: R, completion: Continuation<T>) {
- startDirect(completion) { actualCompletion ->
- withCoroutineContext(completion.context, null) {
- startCoroutineUninterceptedOrReturn(receiver, actualCompletion)
- }
- }
-}
-
-/**
- * Starts the given [block] immediately in the current stack-frame until the first suspension point.
- * This method supports debug probes and thus can intercept completion, thus completion is provided
- * as the parameter of [block].
- */
-private inline fun <T> startDirect(completion: Continuation<T>, block: (Continuation<T>) -> Any?) {
val actualCompletion = probeCoroutineCreated(completion)
val value = try {
- block(actualCompletion)
+ /* The code below is started immediately in the current stack-frame
+ * and runs until the first suspension point. */
+ withCoroutineContext(actualCompletion.context, null) {
+ probeCoroutineResumed(actualCompletion)
+ startCoroutineUninterceptedOrReturn(receiver, actualCompletion)
+ }
} catch (e: Throwable) {
actualCompletion.resumeWithException(e)
return
diff --git a/kotlinx-coroutines-core/common/src/selects/Select.kt b/kotlinx-coroutines-core/common/src/selects/Select.kt
index 7e685af..a13338c 100644
--- a/kotlinx-coroutines-core/common/src/selects/Select.kt
+++ b/kotlinx-coroutines-core/common/src/selects/Select.kt
@@ -105,7 +105,8 @@
@LowPriorityInOverloadResolution
@Deprecated(
message = "Replaced with the same extension function",
- level = DeprecationLevel.ERROR, replaceWith = ReplaceWith(expression = "onTimeout", imports = ["kotlinx.coroutines.selects.onTimeout"])
+ level = DeprecationLevel.ERROR,
+ replaceWith = ReplaceWith(expression = "onTimeout", imports = ["kotlinx.coroutines.selects.onTimeout"])
) // Since 1.7.0, was experimental
public fun onTimeout(timeMillis: Long, block: suspend () -> R): Unit = onTimeout(timeMillis, block)
}
@@ -123,6 +124,8 @@
* 4) the function that specifies how the internal result provided via
* [SelectInstance.trySelect] or [SelectInstance.selectInRegistrationPhase]
* should be processed in case of this `select` cancellation while dispatching.
+ *
+ * @suppress **This is unstable API, and it is subject to change.**
*/
@InternalCoroutinesApi
public sealed interface SelectClause {
@@ -137,6 +140,8 @@
* the specified clause object. In case of channels, the registration logic
* coincides with the plain `send/receive` operation with the only difference that
* the `select` instance is stored as a waiter instead of continuation.
+ *
+ * @suppress **This is unstable API, and it is subject to change.**
*/
@InternalCoroutinesApi
public typealias RegistrationFunction = (clauseObject: Any, select: SelectInstance<*>, param: Any?) -> Unit
@@ -146,6 +151,8 @@
* or [SelectInstance.trySelect] should be processed. For example, both [ReceiveChannel.onReceive] and
* [ReceiveChannel.onReceiveCatching] clauses perform exactly the same synchronization logic,
* but differ when the channel has been discovered in the closed or cancelled state.
+ *
+ * @suppress **This is unstable API, and it is subject to change.**
*/
@InternalCoroutinesApi
public typealias ProcessResultFunction = (clauseObject: Any, param: Any?, clauseResult: Any?) -> Any?
@@ -155,9 +162,12 @@
* or [SelectInstance.selectInRegistrationPhase], should be processed in case of this `select`
* cancellation while dispatching. Unfortunately, we cannot pass this function only in [SelectInstance.trySelect],
* as [SelectInstance.selectInRegistrationPhase] can be called when the coroutine is already cancelled.
+ *
+ * @suppress **This is unstable API, and it is subject to change.**
*/
@InternalCoroutinesApi
-public typealias OnCancellationConstructor = (select: SelectInstance<*>, param: Any?, internalResult: Any?) -> (Throwable) -> Unit
+public typealias OnCancellationConstructor = (select: SelectInstance<*>, param: Any?, internalResult: Any?) ->
+ (Throwable, Any?, CoroutineContext) -> Unit
/**
* Clause for [select] expression without additional parameters that does not select any value.
@@ -171,6 +181,7 @@
) : SelectClause0 {
override val processResFunc: ProcessResultFunction = DUMMY_PROCESS_RESULT_FUNCTION
}
+
private val DUMMY_PROCESS_RESULT_FUNCTION: ProcessResultFunction = { _, _, _ -> null }
/**
@@ -234,7 +245,8 @@
*/
public fun selectInRegistrationPhase(internalResult: Any?)
}
-internal interface SelectInstanceInternal<R>: SelectInstance<R>, Waiter
+
+internal interface SelectInstanceInternal<R> : SelectInstance<R>, Waiter
@PublishedApi
internal open class SelectImplementation<R>(
@@ -346,6 +358,7 @@
* The state of this `select` operation. See the description above for details.
*/
private val state = atomic<Any>(STATE_REG)
+
/**
* Returns `true` if this `select` instance is in the REGISTRATION phase;
* otherwise, returns `false`.
@@ -354,12 +367,14 @@
get() = state.value.let {
it === STATE_REG || it is List<*>
}
+
/**
* Returns `true` if this `select` is already selected;
* thus, other parties are bound to fail when making a rendezvous with it.
*/
private val isSelected
get() = state.value is SelectImplementation<*>.ClauseData
+
/**
* Returns `true` if this `select` is cancelled.
*/
@@ -447,8 +462,10 @@
override fun SelectClause0.invoke(block: suspend () -> R) =
ClauseData(clauseObject, regFunc, processResFunc, PARAM_CLAUSE_0, block, onCancellationConstructor).register()
+
override fun <Q> SelectClause1<Q>.invoke(block: suspend (Q) -> R) =
ClauseData(clauseObject, regFunc, processResFunc, null, block, onCancellationConstructor).register()
+
override fun <P, Q> SelectClause2<P, Q>.invoke(param: P, block: suspend (Q) -> R) =
ClauseData(clauseObject, regFunc, processResFunc, param, block, onCancellationConstructor).register()
@@ -525,7 +542,7 @@
*
* ```
* disposeOnCompletion {
- * segment.onCancellation(index, null)
+ * segment.onCancellation(index, null)
* }
* ```
*/
@@ -549,7 +566,7 @@
* this function performs registration of such clauses. After that, it atomically stores
* the continuation into the [state] field if there is no more clause to be re-registered.
*/
- private suspend fun waitUntilSelected() = suspendCancellableCoroutine<Unit> sc@ { cont ->
+ private suspend fun waitUntilSelected() = suspendCancellableCoroutine<Unit> sc@{ cont ->
// Update the state.
state.loop { curState ->
when {
@@ -762,7 +779,7 @@
/**
* Each `select` clause is internally represented with a [ClauseData] instance.
- */
+ */
internal inner class ClauseData(
@JvmField val clauseObject: Any, // the object of this `select` clause: Channel, Mutex, Job, ...
private val regFunc: RegistrationFunction,
@@ -771,8 +788,10 @@
private val block: Any, // the user-specified block, which should be called if this clause becomes selected
@JvmField val onCancellationConstructor: OnCancellationConstructor?
) {
- @JvmField var disposableHandleOrSegment: Any? = null
- @JvmField var indexInSegment: Int = -1
+ @JvmField
+ var disposableHandleOrSegment: Any? = null
+ @JvmField
+ var indexInSegment: Int = -1
/**
* Tries to register the specified [select] instance in [clauseObject] and check
@@ -843,8 +862,11 @@
}
}
-private fun CancellableContinuation<Unit>.tryResume(onCancellation: ((cause: Throwable) -> Unit)?): Boolean {
- val token = tryResume(Unit, null, onCancellation) ?: return false
+private fun CancellableContinuation<Unit>.tryResume(
+ onCancellation: ((cause: Throwable, value: Any?, context: CoroutineContext) -> Unit)?
+): Boolean {
+ val token =
+ tryResume(Unit, null, onCancellation) ?: return false
completeResume(token)
return true
}
@@ -854,6 +876,7 @@
private const val TRY_SELECT_REREGISTER = 1
private const val TRY_SELECT_CANCELLED = 2
private const val TRY_SELECT_ALREADY_SELECTED = 3
+
// trySelectDetailed(..) results.
internal enum class TrySelectDetailedResult {
SUCCESSFUL, REREGISTER, CANCELLED, ALREADY_SELECTED
@@ -870,9 +893,11 @@
private val STATE_REG = Symbol("STATE_REG")
private val STATE_COMPLETED = Symbol("STATE_COMPLETED")
private val STATE_CANCELLED = Symbol("STATE_CANCELLED")
+
// As the selection result is nullable, we use this special
// marker for the absence of result.
private val NO_RESULT = Symbol("NO_RESULT")
+
// We use this marker parameter objects to distinguish
// SelectClause[0,1,2] and invoke the user-specified block correctly.
internal val PARAM_CLAUSE_0 = Symbol("PARAM_CLAUSE_0")
diff --git a/kotlinx-coroutines-core/common/src/sync/Mutex.kt b/kotlinx-coroutines-core/common/src/sync/Mutex.kt
index 45bd383..093c367 100644
--- a/kotlinx-coroutines-core/common/src/sync/Mutex.kt
+++ b/kotlinx-coroutines-core/common/src/sync/Mutex.kt
@@ -5,6 +5,7 @@
import kotlinx.coroutines.internal.*
import kotlinx.coroutines.selects.*
import kotlin.contracts.*
+import kotlin.coroutines.CoroutineContext
import kotlin.jvm.*
/**
@@ -126,7 +127,7 @@
}
-internal open class MutexImpl(locked: Boolean) : SemaphoreImpl(1, if (locked) 1 else 0), Mutex {
+internal open class MutexImpl(locked: Boolean) : SemaphoreAndMutexImpl(1, if (locked) 1 else 0), Mutex {
/**
* After the lock is acquired, the corresponding owner is stored in this field.
* The [unlock] operation checks the owner and either re-sets it to [NO_OWNER],
@@ -137,7 +138,7 @@
private val onSelectCancellationUnlockConstructor: OnCancellationConstructor =
{ _: SelectInstance<*>, owner: Any?, _: Any? ->
- { unlock(owner) }
+ { _, _, _ -> unlock(owner) }
}
override val isLocked: Boolean get() =
@@ -242,16 +243,21 @@
return this
}
+ @OptIn(InternalForInheritanceCoroutinesApi::class)
private inner class CancellableContinuationWithOwner(
@JvmField
val cont: CancellableContinuationImpl<Unit>,
@JvmField
val owner: Any?
) : CancellableContinuation<Unit> by cont, Waiter by cont {
- override fun tryResume(value: Unit, idempotent: Any?, onCancellation: ((cause: Throwable) -> Unit)?): Any? {
+ override fun <R : Unit> tryResume(
+ value: R,
+ idempotent: Any?,
+ onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?
+ ): Any? {
assert { [email protected] === NO_OWNER }
- val token = cont.tryResume(value, idempotent) {
- assert { [email protected] { it === NO_OWNER ||it === owner } }
+ val token = cont.tryResume(value, idempotent) { _, _, _ ->
+ assert { [email protected] { it === NO_OWNER || it === owner } }
[email protected] = owner
unlock(owner)
}
@@ -262,7 +268,10 @@
return token
}
- override fun resume(value: Unit, onCancellation: ((cause: Throwable) -> Unit)?) {
+ override fun <R : Unit> resume(
+ value: R,
+ onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)?
+ ) {
assert { [email protected] === NO_OWNER }
[email protected] = owner
cont.resume(value) { unlock(owner) }
diff --git a/kotlinx-coroutines-core/common/src/sync/Semaphore.kt b/kotlinx-coroutines-core/common/src/sync/Semaphore.kt
index bf568f3..7cc13f8 100644
--- a/kotlinx-coroutines-core/common/src/sync/Semaphore.kt
+++ b/kotlinx-coroutines-core/common/src/sync/Semaphore.kt
@@ -87,7 +87,7 @@
}
@Suppress("UNCHECKED_CAST")
-internal open class SemaphoreImpl(private val permits: Int, acquiredPermits: Int) : Semaphore {
+internal open class SemaphoreAndMutexImpl(private val permits: Int, acquiredPermits: Int) {
/*
The queue of waiting acquirers is essentially an infinite array based on the list of segments
(see `SemaphoreSegment`); each segment contains a fixed number of slots. To determine a slot for each enqueue
@@ -144,11 +144,11 @@
* cannot be greater than 2^31 in any real application.
*/
private val _availablePermits = atomic(permits - acquiredPermits)
- override val availablePermits: Int get() = max(_availablePermits.value, 0)
+ val availablePermits: Int get() = max(_availablePermits.value, 0)
- private val onCancellationRelease = { _: Throwable -> release() }
+ private val onCancellationRelease = { _: Throwable, _: Unit, _: CoroutineContext -> release() }
- override fun tryAcquire(): Boolean {
+ fun tryAcquire(): Boolean {
while (true) {
// Get the current number of available permits.
val p = _availablePermits.value
@@ -167,7 +167,7 @@
}
}
- override suspend fun acquire() {
+ suspend fun acquire() {
// Decrement the number of available permits.
val p = decPermits()
// Is the permit acquired?
@@ -239,7 +239,7 @@
}
}
- override fun release() {
+ fun release() {
while (true) {
// Increment the number of available permits.
val p = _availablePermits.getAndIncrement()
@@ -346,12 +346,16 @@
} else false
}
is SelectInstance<*> -> {
- trySelect(this@SemaphoreImpl, Unit)
+ trySelect(this@SemaphoreAndMutexImpl, Unit)
}
else -> error("unexpected: $this")
}
}
+private class SemaphoreImpl(
+ permits: Int, acquiredPermits: Int
+): SemaphoreAndMutexImpl(permits, acquiredPermits), Semaphore
+
private fun createSegment(id: Long, prev: SemaphoreSegment?) = SemaphoreSegment(id, prev, 0)
private class SemaphoreSegment(id: Long, prev: SemaphoreSegment?, pointers: Int) : Segment<SemaphoreSegment>(id, prev, pointers) {
diff --git a/kotlinx-coroutines-core/common/test/AwaitTest.kt b/kotlinx-coroutines-core/common/test/AwaitTest.kt
index ca427df..2004285 100644
--- a/kotlinx-coroutines-core/common/test/AwaitTest.kt
+++ b/kotlinx-coroutines-core/common/test/AwaitTest.kt
@@ -353,6 +353,7 @@
fun testAwaitAllDelegates() = runTest {
expect(1)
val deferred = CompletableDeferred<String>()
+ @OptIn(InternalForInheritanceCoroutinesApi::class)
val delegate = object : Deferred<String> by deferred {}
launch {
expect(3)
@@ -367,6 +368,7 @@
fun testCancelAwaitAllDelegate() = runTest {
expect(1)
val deferred = CompletableDeferred<String>()
+ @OptIn(InternalForInheritanceCoroutinesApi::class)
val delegate = object : Deferred<String> by deferred {}
launch {
expect(3)
diff --git a/kotlinx-coroutines-core/common/test/CancellableResumeOldTest.kt b/kotlinx-coroutines-core/common/test/CancellableResumeOldTest.kt
new file mode 100644
index 0000000..501d033
--- /dev/null
+++ b/kotlinx-coroutines-core/common/test/CancellableResumeOldTest.kt
@@ -0,0 +1,291 @@
+@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
+
+package kotlinx.coroutines
+
+import kotlinx.coroutines.testing.*
+import kotlin.test.*
+
+/**
+ * Test for [CancellableContinuation.resume] with `onCancellation` parameter.
+ */
+@Suppress("DEPRECATION")
+class CancellableResumeOldTest : TestBase() {
+ @Test
+ fun testResumeImmediateNormally() = runTest {
+ expect(1)
+ val ok = suspendCancellableCoroutine<String> { cont ->
+ expect(2)
+ cont.invokeOnCancellation { expectUnreached() }
+ cont.resume("OK") { expectUnreached() }
+ expect(3)
+ }
+ assertEquals("OK", ok)
+ finish(4)
+ }
+
+ @Test
+ fun testResumeImmediateAfterCancel() = runTest(
+ expected = { it is TestException }
+ ) {
+ expect(1)
+ suspendCancellableCoroutine<String> { cont ->
+ expect(2)
+ cont.invokeOnCancellation { expect(3) }
+ cont.cancel(TestException("FAIL"))
+ expect(4)
+ cont.resume("OK") { cause ->
+ expect(5)
+ assertIs<TestException>(cause)
+ }
+ finish(6)
+ }
+ expectUnreached()
+ }
+
+ @Test
+ fun testResumeImmediateAfterCancelWithHandlerFailure() = runTest(
+ expected = { it is TestException },
+ unhandled = listOf(
+ { it is CompletionHandlerException && it.cause is TestException2 },
+ { it is CompletionHandlerException && it.cause is TestException3 }
+ )
+ ) {
+ expect(1)
+ suspendCancellableCoroutine<String> { cont ->
+ expect(2)
+ cont.invokeOnCancellation {
+ expect(3)
+ throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
+ }
+ cont.cancel(TestException("FAIL"))
+ expect(4)
+ cont.resume("OK") { cause ->
+ expect(5)
+ assertIs<TestException>(cause)
+ throw TestException3("FAIL") // onCancellation block fails with exception
+ }
+ finish(6)
+ }
+ expectUnreached()
+ }
+
+ @Test
+ fun testResumeImmediateAfterIndirectCancel() = runTest(
+ expected = { it is CancellationException }
+ ) {
+ expect(1)
+ val ctx = coroutineContext
+ suspendCancellableCoroutine<String> { cont ->
+ expect(2)
+ cont.invokeOnCancellation { expect(3) }
+ ctx.cancel()
+ expect(4)
+ cont.resume("OK") {
+ expect(5)
+ }
+ finish(6)
+ }
+ expectUnreached()
+ }
+
+ @Test
+ fun testResumeImmediateAfterIndirectCancelWithHandlerFailure() = runTest(
+ expected = { it is CancellationException },
+ unhandled = listOf(
+ { it is CompletionHandlerException && it.cause is TestException2 },
+ { it is CompletionHandlerException && it.cause is TestException3 }
+ )
+ ) {
+ expect(1)
+ val ctx = coroutineContext
+ suspendCancellableCoroutine<String> { cont ->
+ expect(2)
+ cont.invokeOnCancellation {
+ expect(3)
+ throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
+ }
+ ctx.cancel()
+ expect(4)
+ cont.resume("OK") {
+ expect(5)
+ throw TestException3("FAIL") // onCancellation block fails with exception
+ }
+ finish(6)
+ }
+ expectUnreached()
+ }
+
+ @Test
+ fun testResumeLaterNormally() = runTest {
+ expect(1)
+ lateinit var cc: CancellableContinuation<String>
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ val ok = suspendCancellableCoroutine<String> { cont ->
+ expect(3)
+ cont.invokeOnCancellation { expectUnreached() }
+ cc = cont
+ }
+ assertEquals("OK", ok)
+ finish(6)
+ }
+ expect(4)
+ cc.resume("OK") { expectUnreached() }
+ expect(5)
+ }
+
+ @Test
+ fun testResumeLaterAfterCancel() = runTest {
+ expect(1)
+ lateinit var cc: CancellableContinuation<String>
+ val job = launch(start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ try {
+ suspendCancellableCoroutine<String> { cont ->
+ expect(3)
+ cont.invokeOnCancellation { expect(5) }
+ cc = cont
+ }
+ expectUnreached()
+ } catch (e: CancellationException) {
+ finish(9)
+ }
+ }
+ expect(4)
+ job.cancel(TestCancellationException())
+ expect(6)
+ cc.resume("OK") { cause ->
+ expect(7)
+ assertIs<TestCancellationException>(cause)
+ }
+ expect(8)
+ }
+
+ @Test
+ fun testResumeLaterAfterCancelWithHandlerFailure() = runTest(
+ unhandled = listOf(
+ { it is CompletionHandlerException && it.cause is TestException2 },
+ { it is CompletionHandlerException && it.cause is TestException3 }
+ )
+ ) {
+ expect(1)
+ lateinit var cc: CancellableContinuation<String>
+ val job = launch(start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ try {
+ suspendCancellableCoroutine<String> { cont ->
+ expect(3)
+ cont.invokeOnCancellation {
+ expect(5)
+ throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
+ }
+ cc = cont
+ }
+ expectUnreached()
+ } catch (e: CancellationException) {
+ finish(9)
+ }
+ }
+ expect(4)
+ job.cancel(TestCancellationException())
+ expect(6)
+ cc.resume("OK") { cause ->
+ expect(7)
+ assertIs<TestCancellationException>(cause)
+ throw TestException3("FAIL") // onCancellation block fails with exception
+ }
+ expect(8)
+ }
+
+ @Test
+ fun testResumeCancelWhileDispatched() = runTest {
+ expect(1)
+ lateinit var cc: CancellableContinuation<String>
+ val job = launch(start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ try {
+ suspendCancellableCoroutine<String> { cont ->
+ expect(3)
+ // resumed first, dispatched, then cancelled, but still got invokeOnCancellation call
+ cont.invokeOnCancellation { cause ->
+ // Note: invokeOnCancellation is called before cc.resume(value) { ... } handler
+ expect(7)
+ assertIs<TestCancellationException>(cause)
+ }
+ cc = cont
+ }
+ expectUnreached()
+ } catch (e: CancellationException) {
+ expect(9)
+ }
+ }
+ expect(4)
+ cc.resume("OK") { cause ->
+ // Note: this handler is called after invokeOnCancellation handler
+ expect(8)
+ assertIs<TestCancellationException>(cause)
+ }
+ expect(5)
+ job.cancel(TestCancellationException()) // cancel while execution is dispatched
+ expect(6)
+ yield() // to coroutine -- throws cancellation exception
+ finish(10)
+ }
+
+ @Test
+ fun testResumeCancelWhileDispatchedWithHandlerFailure() = runTest(
+ unhandled = listOf(
+ { it is CompletionHandlerException && it.cause is TestException2 },
+ { it is CompletionHandlerException && it.cause is TestException3 }
+ )
+ ) {
+ expect(1)
+ lateinit var cc: CancellableContinuation<String>
+ val job = launch(start = CoroutineStart.UNDISPATCHED) {
+ expect(2)
+ try {
+ suspendCancellableCoroutine<String> { cont ->
+ expect(3)
+ // resumed first, dispatched, then cancelled, but still got invokeOnCancellation call
+ cont.invokeOnCancellation { cause ->
+ // Note: invokeOnCancellation is called before cc.resume(value) { ... } handler
+ expect(7)
+ assertIs<TestCancellationException>(cause)
+ throw TestException2("FAIL") // invokeOnCancellation handler fails with exception
+ }
+ cc = cont
+ }
+ expectUnreached()
+ } catch (e: CancellationException) {
+ expect(9)
+ }
+ }
+ expect(4)
+ cc.resume("OK") { cause ->
+ // Note: this handler is called after invokeOnCancellation handler
+ expect(8)
+ assertIs<TestCancellationException>(cause)
+ throw TestException3("FAIL") // onCancellation block fails with exception
+ }
+ expect(5)
+ job.cancel(TestCancellationException()) // cancel while execution is dispatched
+ expect(6)
+ yield() // to coroutine -- throws cancellation exception
+ finish(10)
+ }
+
+ @Test
+ fun testResumeUnconfined() = runTest {
+ val outerScope = this
+ withContext(Dispatchers.Unconfined) {
+ val result = suspendCancellableCoroutine<String> {
+ outerScope.launch {
+ it.resume("OK") {
+ expectUnreached()
+ }
+ }
+ }
+ assertEquals("OK", result)
+ }
+ }
+}
diff --git a/kotlinx-coroutines-core/common/test/CancellableResumeTest.kt b/kotlinx-coroutines-core/common/test/CancellableResumeTest.kt
index 517b0ce..c048a8d 100644
--- a/kotlinx-coroutines-core/common/test/CancellableResumeTest.kt
+++ b/kotlinx-coroutines-core/common/test/CancellableResumeTest.kt
@@ -15,7 +15,7 @@
val ok = suspendCancellableCoroutine<String> { cont ->
expect(2)
cont.invokeOnCancellation { expectUnreached() }
- cont.resume("OK") { expectUnreached() }
+ cont.resume("OK") { _, _, _ -> expectUnreached() }
expect(3)
}
assertEquals("OK", ok)
@@ -32,8 +32,11 @@
cont.invokeOnCancellation { expect(3) }
cont.cancel(TestException("FAIL"))
expect(4)
- cont.resume("OK") { cause ->
+ val value = "OK"
+ cont.resume(value) { cause, valueToClose, context ->
expect(5)
+ assertSame(value, valueToClose)
+ assertSame(context, cont.context)
assertIs<TestException>(cause)
}
finish(6)
@@ -58,8 +61,11 @@
}
cont.cancel(TestException("FAIL"))
expect(4)
- cont.resume("OK") { cause ->
+ val value = "OK"
+ cont.resume(value) { cause, valueToClose, context ->
expect(5)
+ assertSame(value, valueToClose)
+ assertSame(context, cont.context)
assertIs<TestException>(cause)
throw TestException3("FAIL") // onCancellation block fails with exception
}
@@ -79,8 +85,12 @@
cont.invokeOnCancellation { expect(3) }
ctx.cancel()
expect(4)
- cont.resume("OK") {
+ val value = "OK"
+ cont.resume(value) { cause, valueToClose, context ->
expect(5)
+ assertSame(value, valueToClose)
+ assertSame(context, cont.context)
+ assertIs<CancellationException>(cause)
}
finish(6)
}
@@ -105,8 +115,12 @@
}
ctx.cancel()
expect(4)
- cont.resume("OK") {
+ val value = "OK"
+ cont.resume(value) { cause, valueToClose, context ->
expect(5)
+ assertSame(value, valueToClose)
+ assertSame(context, cont.context)
+ assertIs<CancellationException>(cause)
throw TestException3("FAIL") // onCancellation block fails with exception
}
finish(6)
@@ -129,7 +143,7 @@
finish(6)
}
expect(4)
- cc.resume("OK") { expectUnreached() }
+ cc.resume("OK") { _, _, _ -> expectUnreached() }
expect(5)
}
@@ -146,15 +160,18 @@
cc = cont
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
finish(9)
}
}
expect(4)
job.cancel(TestCancellationException())
expect(6)
- cc.resume("OK") { cause ->
+ val value = "OK"
+ cc.resume(value) { cause, valueToClose, context ->
expect(7)
+ assertSame(value, valueToClose)
+ assertSame(context, cc.context)
assertIs<TestCancellationException>(cause)
}
expect(8)
@@ -181,15 +198,18 @@
cc = cont
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
finish(9)
}
}
expect(4)
job.cancel(TestCancellationException())
expect(6)
- cc.resume("OK") { cause ->
+ val value = "OK"
+ cc.resume(value) { cause, valueToClose, context ->
expect(7)
+ assertSame(value, valueToClose)
+ assertSame(context, cc.context)
assertIs<TestCancellationException>(cause)
throw TestException3("FAIL") // onCancellation block fails with exception
}
@@ -214,14 +234,17 @@
cc = cont
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
expect(9)
}
}
expect(4)
- cc.resume("OK") { cause ->
+ val value = "OK"
+ cc.resume("OK") { cause, valueToClose, context ->
// Note: this handler is called after invokeOnCancellation handler
expect(8)
+ assertSame(value, valueToClose)
+ assertSame(context, cc.context)
assertIs<TestCancellationException>(cause)
}
expect(5)
@@ -255,14 +278,17 @@
cc = cont
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
expect(9)
}
}
expect(4)
- cc.resume("OK") { cause ->
+ val value = "OK"
+ cc.resume(value) { cause, valueToClose, context ->
// Note: this handler is called after invokeOnCancellation handler
expect(8)
+ assertSame(value, valueToClose)
+ assertSame(context, cc.context)
assertIs<TestCancellationException>(cause)
throw TestException3("FAIL") // onCancellation block fails with exception
}
@@ -279,7 +305,7 @@
withContext(Dispatchers.Unconfined) {
val result = suspendCancellableCoroutine<String> {
outerScope.launch {
- it.resume("OK") {
+ it.resume("OK") { _, _, _ ->
expectUnreached()
}
}
diff --git a/kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt b/kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt
index e981248..e34cba4 100644
--- a/kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt
+++ b/kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt
@@ -9,7 +9,7 @@
@Test
fun testAsync() = runTest {
- CoroutineStart.values().forEach { testAsyncCancelledParent(it) }
+ CoroutineStart.entries.forEach { testAsyncCancelledParent(it) }
}
private suspend fun testAsyncCancelledParent(start: CoroutineStart) {
@@ -25,14 +25,14 @@
}
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
// Expected
}
}
@Test
fun testLaunch() = runTest {
- CoroutineStart.values().forEach { testLaunchCancelledParent(it) }
+ CoroutineStart.entries.forEach { testLaunchCancelledParent(it) }
}
private suspend fun testLaunchCancelledParent(start: CoroutineStart) {
@@ -48,7 +48,7 @@
}
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
// Expected
}
}
@@ -67,9 +67,10 @@
@Test
fun testBroadcast() = runTest {
- CoroutineStart.values().forEach { testBroadcastCancelledParent(it) }
+ CoroutineStart.entries.forEach { testBroadcastCancelledParent(it) }
}
+ @Suppress("DEPRECATION_ERROR")
private suspend fun testBroadcastCancelledParent(start: CoroutineStart) {
try {
withContext(Job()) {
@@ -83,7 +84,7 @@
}
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
// Expected
}
}
@@ -105,7 +106,7 @@
block()
}
expectUnreached()
- } catch (e: CancellationException) {
+ } catch (_: CancellationException) {
// Expected
}
}
diff --git a/kotlinx-coroutines-core/common/test/CoroutineScopeTest.kt b/kotlinx-coroutines-core/common/test/CoroutineScopeTest.kt
index 87ab511..94a9d0c 100644
--- a/kotlinx-coroutines-core/common/test/CoroutineScopeTest.kt
+++ b/kotlinx-coroutines-core/common/test/CoroutineScopeTest.kt
@@ -248,6 +248,31 @@
}
@Test
+ fun testLaunchContainsDefaultDispatcher() = runTest {
+ val scopeWithoutDispatcher = CoroutineScope(coroutineContext.minusKey(ContinuationInterceptor))
+ scopeWithoutDispatcher.launch(Dispatchers.Default) {
+ assertSame(Dispatchers.Default, coroutineContext[ContinuationInterceptor])
+ }.join()
+ scopeWithoutDispatcher.launch {
+ assertSame(Dispatchers.Default, coroutineContext[ContinuationInterceptor])
+ }.join()
+ }
+
+ @Test
+ fun testNewCoroutineContextDispatcher() {
+ fun newContextDispatcher(c1: CoroutineContext, c2: CoroutineContext) =
+ ContextScope(c1).newCoroutineContext(c2)[ContinuationInterceptor]
+
+ assertSame(Dispatchers.Default, newContextDispatcher(EmptyCoroutineContext, EmptyCoroutineContext))
+ assertSame(Dispatchers.Default, newContextDispatcher(EmptyCoroutineContext, Dispatchers.Default))
+ assertSame(Dispatchers.Default, newContextDispatcher(Dispatchers.Default, EmptyCoroutineContext))
+ assertSame(Dispatchers.Default, newContextDispatcher(Dispatchers.Default, Dispatchers.Default))
+ assertSame(Dispatchers.Default, newContextDispatcher(Dispatchers.Unconfined, Dispatchers.Default))
+ assertSame(Dispatchers.Unconfined, newContextDispatcher(Dispatchers.Default, Dispatchers.Unconfined))
+ assertSame(Dispatchers.Unconfined, newContextDispatcher(Dispatchers.Unconfined, Dispatchers.Unconfined))
+ }
+
+ @Test
fun testScopePlusContext() {
assertSame(EmptyCoroutineContext, scopePlusContext(EmptyCoroutineContext, EmptyCoroutineContext))
assertSame(Dispatchers.Default, scopePlusContext(EmptyCoroutineContext, Dispatchers.Default))
diff --git a/kotlinx-coroutines-core/common/test/EmptyContext.kt b/kotlinx-coroutines-core/common/test/EmptyContext.kt
index 61e595c..abec3d3 100644
--- a/kotlinx-coroutines-core/common/test/EmptyContext.kt
+++ b/kotlinx-coroutines-core/common/test/EmptyContext.kt
@@ -1,8 +1,30 @@
package kotlinx.coroutines
-import kotlinx.coroutines.intrinsics.*
+import kotlinx.coroutines.internal.probeCoroutineCreated
+import kotlinx.coroutines.internal.probeCoroutineResumed
import kotlin.coroutines.*
+import kotlin.coroutines.intrinsics.*
suspend fun <T> withEmptyContext(block: suspend () -> T): T = suspendCoroutine { cont ->
block.startCoroutineUnintercepted(Continuation(EmptyCoroutineContext) { cont.resumeWith(it) })
}
+
+/**
+ * Use this function to restart a coroutine directly from inside of [suspendCoroutine],
+ * when the code is already in the context of this coroutine.
+ * It does not use [ContinuationInterceptor] and does not update the context of the current thread.
+ */
+fun <T> (suspend () -> T).startCoroutineUnintercepted(completion: Continuation<T>) {
+ val actualCompletion = probeCoroutineCreated(completion)
+ val value = try {
+ probeCoroutineResumed(actualCompletion)
+ startCoroutineUninterceptedOrReturn(actualCompletion)
+ } catch (e: Throwable) {
+ actualCompletion.resumeWithException(e)
+ return
+ }
+ if (value !== COROUTINE_SUSPENDED) {
+ @Suppress("UNCHECKED_CAST")
+ actualCompletion.resume(value as T)
+ }
+}
diff --git a/kotlinx-coroutines-core/common/test/JobTest.kt b/kotlinx-coroutines-core/common/test/JobTest.kt
index b86ac73..55119ab 100644
--- a/kotlinx-coroutines-core/common/test/JobTest.kt
+++ b/kotlinx-coroutines-core/common/test/JobTest.kt
@@ -175,6 +175,20 @@
}
@Test
+ fun testInvokeOnCancellingFiringOnNormalExit() = runTest {
+ val job = launch {
+ expect(2)
+ }
+ job.invokeOnCompletion(onCancelling = true) {
+ assertNull(it)
+ expect(3)
+ }
+ expect(1)
+ job.join()
+ finish(4)
+ }
+
+ @Test
fun testOverriddenParent() = runTest {
val parent = Job()
val deferred = launch(parent, CoroutineStart.ATOMIC) {
diff --git a/kotlinx-coroutines-core/common/test/ParentCancellationTest.kt b/kotlinx-coroutines-core/common/test/ParentCancellationTest.kt
index fd07a06..43f77a5 100644
--- a/kotlinx-coroutines-core/common/test/ParentCancellationTest.kt
+++ b/kotlinx-coroutines-core/common/test/ParentCancellationTest.kt
@@ -4,7 +4,6 @@
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.channels.*
-import kotlin.coroutines.*
import kotlin.test.*
/**
@@ -57,6 +56,7 @@
}
@Test
+ @Suppress("DEPRECATION_ERROR")
fun testBroadcastChild() = runTest {
testParentCancellation(runsInScopeContext = true) { fail ->
broadcast<Unit> { fail() }.openSubscription()
@@ -165,4 +165,4 @@
}
finish(3)
}
-}
\ No newline at end of file
+}
diff --git a/kotlinx-coroutines-core/common/test/channels/BroadcastChannelFactoryTest.kt b/kotlinx-coroutines-core/common/test/channels/BroadcastChannelFactoryTest.kt
index 652f307..f13aae5 100644
--- a/kotlinx-coroutines-core/common/test/channels/BroadcastChannelFactoryTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/BroadcastChannelFactoryTest.kt
@@ -1,10 +1,9 @@
package kotlinx.coroutines.channels
import kotlinx.coroutines.testing.*
-import kotlinx.coroutines.*
import kotlin.test.*
-
+@Suppress("DEPRECATION_ERROR")
class BroadcastChannelFactoryTest : TestBase() {
@Test
diff --git a/kotlinx-coroutines-core/common/test/channels/BroadcastTest.kt b/kotlinx-coroutines-core/common/test/channels/BroadcastTest.kt
index a308f7a..3e8514b 100644
--- a/kotlinx-coroutines-core/common/test/channels/BroadcastTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/BroadcastTest.kt
@@ -1,4 +1,4 @@
-@file:Suppress("DEPRECATION")
+@file:Suppress("DEPRECATION_ERROR")
package kotlinx.coroutines.channels
diff --git a/kotlinx-coroutines-core/common/test/channels/BufferedBroadcastChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/BufferedBroadcastChannelTest.kt
index 834d974..9680232 100644
--- a/kotlinx-coroutines-core/common/test/channels/BufferedBroadcastChannelTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/BufferedBroadcastChannelTest.kt
@@ -4,6 +4,7 @@
import kotlinx.coroutines.*
import kotlin.test.*
+@Suppress("DEPRECATION_ERROR")
class BufferedBroadcastChannelTest : TestBase() {
@Test
diff --git a/kotlinx-coroutines-core/common/test/channels/BufferedChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/BufferedChannelTest.kt
index 27ac005..39b9f9d 100644
--- a/kotlinx-coroutines-core/common/test/channels/BufferedChannelTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/BufferedChannelTest.kt
@@ -6,6 +6,40 @@
class BufferedChannelTest : TestBase() {
@Test
+ fun testIteratorHasNextIsIdempotent() = runTest {
+ val q = Channel<Int>()
+ check(q.isEmpty)
+ val iter = q.iterator()
+ expect(1)
+ val sender = launch {
+ expect(4)
+ q.send(1) // sent
+ expect(10)
+ q.close()
+ expect(11)
+ }
+ expect(2)
+ val receiver = launch {
+ expect(5)
+ check(iter.hasNext())
+ expect(6)
+ check(iter.hasNext())
+ expect(7)
+ check(iter.hasNext())
+ expect(8)
+ check(iter.next() == 1)
+ expect(9)
+ check(!iter.hasNext())
+ expect(12)
+ }
+ expect(3)
+ sender.join()
+ receiver.join()
+ check(q.isClosedForReceive)
+ finish(13)
+ }
+
+ @Test
fun testSimple() = runTest {
val q = Channel<Int>(1)
check(q.isEmpty)
diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt
index d4d6887..235609c 100644
--- a/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/ChannelsTest.kt
@@ -95,6 +95,15 @@
}
+ @Test
+ fun testToListOnFailedChannel() = runTest {
+ val channel = Channel<Int>()
+ channel.close(TestException())
+ assertFailsWith<TestException> {
+ channel.toList()
+ }
+ }
+
private fun <E> Iterable<E>.asReceiveChannel(context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel<E> =
GlobalScope.produce(context) {
for (element in this@asReceiveChannel)
diff --git a/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt
index 72b5fde..9c534e0 100644
--- a/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt
@@ -4,6 +4,7 @@
import kotlinx.coroutines.*
import kotlin.test.*
+@Suppress("DEPRECATION_ERROR")
class ConflatedBroadcastChannelTest : TestBase() {
@Test
diff --git a/kotlinx-coroutines-core/common/test/channels/ConsumeTest.kt b/kotlinx-coroutines-core/common/test/channels/ConsumeTest.kt
index f5df234..57d9b5b 100644
--- a/kotlinx-coroutines-core/common/test/channels/ConsumeTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/ConsumeTest.kt
@@ -91,6 +91,45 @@
assertTrue(channel.isClosedForReceive)
}
+ /** Checks that [ReceiveChannel.consumeEach] reacts to cancellation, but processes the elements that are
+ * readily available in the buffer. */
+ @Test
+ fun testConsumeEachExitsOnCancellation() = runTest {
+ val undeliveredElements = mutableListOf<Int>()
+ val channel = Channel<Int>(2, onUndeliveredElement = {
+ undeliveredElements.add(it)
+ })
+ launch {
+ // These two elements will be sent and put into the buffer:
+ channel.send(0)
+ channel.send(1)
+ // This element will not fit into the buffer, so `send` suspends:
+ channel.send(2)
+ // At this point, the consumer's `launch` is cancelled.
+ yield() // Allow the cancellation handler of the consumer to run.
+ // Try to send a new element, which will fail at this point:
+ channel.send(3)
+ fail("unreached")
+ }
+ launch {
+ channel.consumeEach {
+ cancel()
+ assertTrue(it in 0..2)
+ }
+ }.join()
+ assertTrue(channel.isClosedForReceive)
+ assertEquals(listOf(3), undeliveredElements)
+ }
+
+ @Test
+ fun testConsumeEachThrowingOnChannelClosing() = runTest {
+ val channel = Channel<Int>()
+ channel.close(TestException())
+ assertFailsWith<TestException> {
+ channel.consumeEach { fail("unreached") }
+ }
+ }
+
/** Check that [BroadcastChannel.consume] does not suffer from KT-58685 */
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
@Test
diff --git a/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt b/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt
index 1b68bac..654982e 100644
--- a/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt
+++ b/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt
@@ -114,6 +114,36 @@
}
@Test
+ fun testAwaitCloseOnlyAllowedOnce() = runTest {
+ expect(1)
+ val c = produce<Int> {
+ try {
+ awaitClose()
+ } catch (e: CancellationException) {
+ assertFailsWith<IllegalStateException> {
+ awaitClose()
+ }
+ finish(2)
+ throw e
+ }
+ }
+ yield() // let the `produce` procedure run
+ c.cancel()
+ }
+
+ @Test
+ fun testInvokeOnCloseWithAwaitClose() = runTest {
+ expect(1)
+ produce<Int> {
+ invokeOnClose { }
+ assertFailsWith<IllegalStateException> {
+ awaitClose()
+ }
+ finish(2)
+ }
+ }
+
+ @Test
fun testAwaitConsumerCancellation() = runTest {
val parent = Job()
val channel = produce<Int>(parent) {
@@ -178,6 +208,64 @@
finish(3)
}
+ @Test
+ fun testUncaughtExceptionsInProduce() = runTest(
+ unhandled = listOf({ it is TestException })
+ ) {
+ val c = produce<Int> {
+ launch(SupervisorJob()) {
+ throw TestException()
+ }.join()
+ send(3)
+ }
+ assertEquals(3, c.receive())
+ }
+
+ @Test
+ fun testCancellingProduceCoroutineButNotChannel() = runTest {
+ val c = produce<Int>(Job(), capacity = Channel.UNLIMITED) {
+ launch { throw TestException() }
+ try {
+ yield()
+ } finally {
+ repeat(10) { trySend(it) }
+ }
+ }
+ repeat(10) { assertEquals(it, c.receive()) }
+ }
+
+ @Test
+ fun testReceivingValuesAfterFailingTheCoroutine() = runTest {
+ val produceJob = Job()
+ val c = produce<Int>(produceJob, capacity = Channel.UNLIMITED) {
+ repeat(5) { send(it) }
+ throw TestException()
+ }
+ produceJob.join()
+ assertTrue(produceJob.isCancelled)
+ repeat(5) { assertEquals(it, c.receive()) }
+ assertFailsWith<TestException> { c.receive() }
+ }
+
+ @Test
+ fun testSilentKillerInProduce() = runTest {
+ val parentScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+ val channel = parentScope.produce<Int>(capacity = Channel.UNLIMITED) {
+ repeat(5) {
+ send(it)
+ }
+ parentScope.cancel()
+ // suspending after this point would fail, but sending succeeds
+ send(-1)
+ }
+ launch {
+ for (c in channel) {
+ println(c) // 0, 1, 2, 3, 4, -1
+ } // throws an exception after reaching -1
+ fail("unreached")
+ }
+ }
+
private suspend fun cancelOnCompletion(coroutineContext: CoroutineContext) = CoroutineScope(coroutineContext).apply {
val source = Channel<Int>()
expect(1)
diff --git a/kotlinx-coroutines-core/common/test/channels/TestBroadcastChannelKind.kt b/kotlinx-coroutines-core/common/test/channels/TestBroadcastChannelKind.kt
index 0d23238..693f1f1 100644
--- a/kotlinx-coroutines-core/common/test/channels/TestBroadcastChannelKind.kt
+++ b/kotlinx-coroutines-core/common/test/channels/TestBroadcastChannelKind.kt
@@ -1,5 +1,6 @@
package kotlinx.coroutines.channels
+@Suppress("DEPRECATION_ERROR")
enum class TestBroadcastChannelKind {
ARRAY_1 {
override fun <T> create(): BroadcastChannel<T> = BroadcastChannel(1)
@@ -18,4 +19,4 @@
abstract fun <T> create(): BroadcastChannel<T>
open val isConflated: Boolean get() = false
-}
\ No newline at end of file
+}
diff --git a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt
index 97562d4..605c746 100644
--- a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt
+++ b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt
@@ -21,7 +21,7 @@
fun <T> create(onUndeliveredElement: ((T) -> Unit)? = null): Channel<T> = when {
viaBroadcast && onUndeliveredElement != null -> error("Broadcast channels to do not support onUndeliveredElement")
- viaBroadcast -> ChannelViaBroadcast(BroadcastChannel(capacity))
+ viaBroadcast -> @Suppress("DEPRECATION_ERROR") ChannelViaBroadcast(BroadcastChannel(capacity))
else -> Channel(capacity, onUndeliveredElement = onUndeliveredElement)
}
@@ -30,6 +30,7 @@
}
internal class ChannelViaBroadcast<E>(
+ @Suppress("DEPRECATION_ERROR")
private val broadcast: BroadcastChannel<E>
): Channel<E>, SendChannel<E> by broadcast {
val sub = broadcast.openSubscription()
diff --git a/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt b/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt
index bd1fd6d..771768e 100644
--- a/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt
+++ b/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt
@@ -22,7 +22,21 @@
val delayNanos = ThreadLocalEventLoop.currentOrNull()?.processNextEvent()
?: error("Event loop is missing, virtual time source works only as part of event loop")
if (delayNanos <= 0) continue
- if (delayNanos > 0 && delayNanos != Long.MAX_VALUE) error("Unexpected external delay: $delayNanos")
+ if (delayNanos > 0 && delayNanos != Long.MAX_VALUE) {
+ if (usesSharedEventLoop) {
+ val targetTime = currentTime + delayNanos
+ while (currentTime < targetTime) {
+ val nextTask = heap.minByOrNull { it.deadline } ?: break
+ if (nextTask.deadline > targetTime) break
+ heap.remove(nextTask)
+ currentTime = nextTask.deadline
+ nextTask.run()
+ }
+ currentTime = maxOf(currentTime, targetTime)
+ } else {
+ error("Unexpected external delay: $delayNanos")
+ }
+ }
val nextTask = heap.minByOrNull { it.deadline } ?: return@launch
heap.remove(nextTask)
currentTime = nextTask.deadline
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/CancellableTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/CancellableTest.kt
index fff474e..9086dab 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/CancellableTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/CancellableTest.kt
@@ -1,8 +1,7 @@
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlin.test.*
class CancellableTest : TestBase() {
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/ChunkedTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/ChunkedTest.kt
new file mode 100644
index 0000000..e901c8c
--- /dev/null
+++ b/kotlinx-coroutines-core/common/test/flow/operators/ChunkedTest.kt
@@ -0,0 +1,88 @@
+package kotlinx.coroutines.flow
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.*
+import kotlinx.coroutines.testing.*
+import kotlin.test.*
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class ChunkedTest : TestBase() {
+
+ @Test
+ fun testChunked() = runTest {
+ doTest(flowOf(1, 2, 3, 4, 5), 2, listOf(listOf(1, 2), listOf(3, 4), listOf(5)))
+ doTest(flowOf(1, 2, 3, 4, 5), 3, listOf(listOf(1, 2, 3), listOf(4, 5)))
+ doTest(flowOf(1, 2, 3, 4), 2, listOf(listOf(1, 2), listOf(3, 4)))
+ doTest(flowOf(1), 3, listOf(listOf(1)))
+ }
+
+ private suspend fun <T> doTest(flow: Flow<T>, chunkSize: Int, expected: List<List<T>>) {
+ assertEquals(expected, flow.chunked(chunkSize).toList())
+ assertEquals(flow.toList().chunked(chunkSize), flow.chunked(chunkSize).toList())
+ }
+
+ @Test
+ fun testEmpty() = runTest {
+ doTest(emptyFlow<Int>(), 1, emptyList())
+ doTest(emptyFlow<Int>(), 2, emptyList())
+ }
+
+ @Test
+ fun testChunkedCancelled() = runTest {
+ val result = flow {
+ expect(1)
+ emit(1)
+ emit(2)
+ expect(2)
+ }.chunked(1).buffer().take(1).toList()
+ assertEquals(listOf(listOf(1)), result)
+ finish(3)
+ }
+
+ @Test
+ fun testChunkedCancelledWithSuspension() = runTest {
+ val result = flow {
+ expect(1)
+ emit(1)
+ yield()
+ expectUnreached()
+ emit(2)
+ }.chunked(1).buffer().take(1).toList()
+ assertEquals(listOf(listOf(1)), result)
+ finish(2)
+ }
+
+ @Test
+ fun testChunkedDoesNotIgnoreCancellation() = runTest {
+ expect(1)
+ val result = flow {
+ coroutineScope {
+ launch {
+ hang { expect(2) }
+ }
+ yield()
+ emit(1)
+ emit(2)
+ }
+ }.chunked(1).take(1).toList()
+ assertEquals(listOf(listOf(1)), result)
+ finish(3)
+ }
+
+ @Test
+ fun testIae() {
+ assertFailsWith<IllegalArgumentException> { emptyFlow<Int>().chunked(-1) }
+ assertFailsWith<IllegalArgumentException> { emptyFlow<Int>().chunked(0) }
+ assertFailsWith<IllegalArgumentException> { emptyFlow<Int>().chunked(Int.MIN_VALUE) }
+ assertFailsWith<IllegalArgumentException> { emptyFlow<Int>().chunked(Int.MIN_VALUE + 1) }
+ }
+
+ @Test
+ fun testSample() = runTest {
+ val result = flowOf("a", "b", "c", "d", "e")
+ .chunked(2)
+ .map { it.joinToString(separator = "") }
+ .toList()
+ assertEquals(listOf("ab", "cd", "e"), result)
+ }
+}
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/CombineParametersTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/CombineParametersTest.kt
index f496dd7..35ddba7 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/CombineParametersTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/CombineParametersTest.kt
@@ -1,8 +1,7 @@
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlin.test.*
class CombineParametersTest : TestBase() {
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/CombineTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/CombineTest.kt
index 6db9b84..a69e39f 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/CombineTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/CombineTest.kt
@@ -1,9 +1,8 @@
@file:Suppress("UNCHECKED_CAST")
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlin.test.*
import kotlinx.coroutines.flow.combine as combineOriginal
import kotlinx.coroutines.flow.combineTransform as combineTransformOriginal
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/ConflateTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/ConflateTest.kt
index b680b22..7b3878c 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/ConflateTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/ConflateTest.kt
@@ -1,8 +1,7 @@
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlin.test.*
class ConflateTest : TestBase() {
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/LintTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/LintTest.kt
index 6d0391f..7fc2282 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/LintTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/LintTest.kt
@@ -1,8 +1,7 @@
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlin.test.*
class LintTest: TestBase() {
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/OnEmptyTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/OnEmptyTest.kt
index cca7708..c493818 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/OnEmptyTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/OnEmptyTest.kt
@@ -1,8 +1,7 @@
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlin.test.*
class OnEmptyTest : TestBase() {
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/SampleTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/SampleTest.kt
index 781587d..70c9d24 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/SampleTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/SampleTest.kt
@@ -1,9 +1,8 @@
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
-import kotlinx.coroutines.flow.*
import kotlin.test.*
import kotlin.time.*
import kotlin.time.Duration.Companion.milliseconds
diff --git a/kotlinx-coroutines-core/common/test/flow/operators/TimeoutTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/TimeoutTest.kt
index 0162a21..6d3b8a8 100644
--- a/kotlinx-coroutines-core/common/test/flow/operators/TimeoutTest.kt
+++ b/kotlinx-coroutines-core/common/test/flow/operators/TimeoutTest.kt
@@ -1,8 +1,7 @@
-package kotlinx.coroutines.flow.operators
+package kotlinx.coroutines.flow
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.internal.*
import kotlin.coroutines.*
import kotlin.test.*
diff --git a/kotlinx-coroutines-core/common/test/sync/MutexTest.kt b/kotlinx-coroutines-core/common/test/sync/MutexTest.kt
index 7c6b8ab..c2d555f 100644
--- a/kotlinx-coroutines-core/common/test/sync/MutexTest.kt
+++ b/kotlinx-coroutines-core/common/test/sync/MutexTest.kt
@@ -192,4 +192,9 @@
}
}
}
+
+ @Test
+ fun testMutexIsNotSemaphore() {
+ assertIsNot<Semaphore>(Mutex())
+ }
}
diff --git a/kotlinx-coroutines-core/concurrent/src/Dispatchers.kt b/kotlinx-coroutines-core/concurrent/src/Dispatchers.kt
index aadca2f..d18efdc 100644
--- a/kotlinx-coroutines-core/concurrent/src/Dispatchers.kt
+++ b/kotlinx-coroutines-core/concurrent/src/Dispatchers.kt
@@ -26,6 +26,15 @@
* the system may have up to `64 + 100 + 60` threads dedicated to blocking tasks during peak loads,
* but during its steady state there is only a small number of threads shared
* among `Dispatchers.IO`, `myMysqlDbDispatcher` and `myMongoDbDispatcher`
+ *
+ * It is recommended to replace manually created thread-backed executors with `Dispatchers.IO.limitedParallelism` instead:
+ * ```
+ * // Requires manual closing, allocates resources for all threads
+ * val databasePoolDispatcher = newFixedThreadPoolContext(128)
+ *
+ * // Provides the same number of threads as a resource but shares and caches them internally
+ * val databasePoolDispatcher = Dispatchers.IO.limitedParallelism(128)
+ * ```
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public expect val Dispatchers.IO: CoroutineDispatcher
diff --git a/kotlinx-coroutines-core/concurrent/src/MultithreadedDispatchers.common.kt b/kotlinx-coroutines-core/concurrent/src/MultithreadedDispatchers.common.kt
index bda8d35..b85c878 100644
--- a/kotlinx-coroutines-core/concurrent/src/MultithreadedDispatchers.common.kt
+++ b/kotlinx-coroutines-core/concurrent/src/MultithreadedDispatchers.common.kt
@@ -19,7 +19,8 @@
* associated native resources (threads or native workers). It should not be allocated in place,
* should be closed at the end of its lifecycle, and has non-trivial memory and CPU footprint.
* If you do not need a separate thread pool, but only have to limit effective parallelism of the dispatcher,
- * it is recommended to use [CoroutineDispatcher.limitedParallelism] instead.
+ * it is recommended to use [`Dispatchers.IO.limitedParallelism(1)`][CoroutineDispatcher.limitedParallelism]
+ * or [`Dispatchers.Default.limitedParallelism(1)`][CoroutineDispatcher.limitedParallelism] instead.
*
* If you need a completely separate thread pool with scheduling policy that is based on the standard
* JDK executors, use the following expression:
@@ -48,7 +49,8 @@
* associated native resources (threads or native workers). It should not be allocated in place,
* should be closed at the end of its lifecycle, and has non-trivial memory and CPU footprint.
* If you do not need a separate thread pool, but only have to limit effective parallelism of the dispatcher,
- * it is recommended to use [CoroutineDispatcher.limitedParallelism] instead.
+ * it is recommended to use [`Dispatchers.IO.limitedParallelism(nThreads)`][CoroutineDispatcher.limitedParallelism]
+ * or [`Dispatchers.Default.limitedParallelism(nThreads)`][CoroutineDispatcher.limitedParallelism] instead.
*
* If you need a completely separate thread pool with scheduling policy that is based on the standard
* JDK executors, use the following expression:
@@ -59,4 +61,5 @@
* @param name the base name of the created threads.
*/
@ExperimentalCoroutinesApi
+@DelicateCoroutinesApi
public expect fun newFixedThreadPoolContext(nThreads: Int, name: String): CloseableCoroutineDispatcher
diff --git a/kotlinx-coroutines-core/concurrent/src/internal/LockFreeLinkedList.kt b/kotlinx-coroutines-core/concurrent/src/internal/LockFreeLinkedList.kt
index 970c01f..cd09f35 100644
--- a/kotlinx-coroutines-core/concurrent/src/internal/LockFreeLinkedList.kt
+++ b/kotlinx-coroutines-core/concurrent/src/internal/LockFreeLinkedList.kt
@@ -1,4 +1,4 @@
-@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE")
+@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE", "ACTUAL_WITHOUT_EXPECT")
package kotlinx.coroutines.internal
@@ -8,18 +8,6 @@
private typealias Node = LockFreeLinkedListNode
-@PublishedApi
-internal const val UNDECIDED: Int = 0
-
-@PublishedApi
-internal const val SUCCESS: Int = 1
-
-@PublishedApi
-internal const val FAILURE: Int = 2
-
-@PublishedApi
-internal val CONDITION_FALSE: Any = Symbol("CONDITION_FALSE")
-
/**
* Doubly-linked concurrent list node with remove support.
* Based on paper
@@ -49,37 +37,10 @@
private fun removed(): Removed =
_removedRef.value ?: Removed(this).also { _removedRef.lazySet(it) }
- @PublishedApi
- internal abstract class CondAddOp(
- @JvmField val newNode: Node
- ) : AtomicOp<Node>() {
- @JvmField var oldNext: Node? = null
-
- override fun complete(affected: Node, failure: Any?) {
- val success = failure == null
- val update = if (success) newNode else oldNext
- if (update != null && affected._next.compareAndSet( this, update)) {
- // only the thread the makes this update actually finishes add operation
- if (success) newNode.finishAdd(oldNext!!)
- }
- }
- }
-
- @PublishedApi
- internal inline fun makeCondAddOp(node: Node, crossinline condition: () -> Boolean): CondAddOp =
- object : CondAddOp(node) {
- override fun prepare(affected: Node): Any? = if (condition()) null else CONDITION_FALSE
- }
-
public actual open val isRemoved: Boolean get() = next is Removed
// LINEARIZABLE. Returns Node | Removed
- public val next: Any get() {
- _next.loop { next ->
- if (next !is OpDescriptor) return next
- next.perform(this)
- }
- }
+ public val next: Any get() = _next.value
// LINEARIZABLE. Returns next non-removed Node
public actual val nextNode: Node get() =
@@ -91,7 +52,7 @@
// prev.next correction, which does not provide linearizable backwards iteration, but can be used to
// resume forward iteration when current node was removed.
public actual val prevNode: Node
- get() = correctPrev(null) ?: findPrevNonRemoved(_prev.value)
+ get() = correctPrev() ?: findPrevNonRemoved(_prev.value)
private tailrec fun findPrevNonRemoved(current: Node): Node {
if (!current.isRemoved) return current
@@ -117,29 +78,27 @@
// ------ addLastXXX ------
/**
- * Adds last item to this list.
+ * Adds last item to this list. Returns `false` if the list is closed.
*/
- public actual fun addLast(node: Node) {
+ public actual fun addLast(node: Node, permissionsBitmask: Int): Boolean {
while (true) { // lock-free loop on prev.next
- if (prevNode.addNext(node, this)) return
- }
- }
-
- /**
- * Adds last item to this list atomically if the [condition] is true.
- */
- public actual inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean {
- val condAdd = makeCondAddOp(node, condition)
- while (true) { // lock-free loop on prev.next
- val prev = prevNode // sentinel node is never removed, so prev is always defined
- when (prev.tryCondAddNext(node, this, condAdd)) {
- SUCCESS -> return true
- FAILURE -> return false
+ val currentPrev = prevNode
+ return when {
+ currentPrev is ListClosed ->
+ currentPrev.forbiddenElementsBitmask and permissionsBitmask == 0 &&
+ currentPrev.addLast(node, permissionsBitmask)
+ currentPrev.addNext(node, this) -> true
+ else -> continue
}
}
}
- // ------ addXXX util ------
+ /**
+ * Forbids adding new items to this list.
+ */
+ public actual fun close(forbiddenElementsBit: Int) {
+ addLast(ListClosed(forbiddenElementsBit), forbiddenElementsBit)
+ }
/**
* Given:
@@ -174,17 +133,6 @@
return true
}
- // returns UNDECIDED, SUCCESS or FAILURE
- @PublishedApi
- internal fun tryCondAddNext(node: Node, next: Node, condAdd: CondAddOp): Int {
- node._prev.lazySet(this)
- node._next.lazySet(next)
- condAdd.oldNext = next
- if (!_next.compareAndSet(next, condAdd)) return UNDECIDED
- // added operation successfully (linearized) -- complete it & fixup the list
- return if (condAdd.perform(this) == null) SUCCESS else FAILURE
- }
-
// ------ removeXXX ------
/**
@@ -207,7 +155,7 @@
val removed = (next as Node).removed()
if (_next.compareAndSet(next, removed)) {
// was removed successfully (linearized remove) -- fixup the list
- next.correctPrev(null)
+ next.correctPrev()
return null
}
}
@@ -247,14 +195,12 @@
if (next._prev.compareAndSet(nextPrev, this)) {
// This newly added node could have been removed, and the above CAS would have added it physically again.
// Let us double-check for this situation and correct if needed
- if (isRemoved) next.correctPrev(null)
+ if (isRemoved) next.correctPrev()
return
}
}
}
- protected open fun nextIfRemoved(): Node? = (next as? Removed)?.ref
-
/**
* Returns the corrected value of the previous node while also correcting the `prev` pointer
* (so that `this.prev.next === this`) and helps complete node removals to the left ot this node.
@@ -265,7 +211,7 @@
* remover of this node will ultimately call [correctPrev] on the next node and that will fix all
* the links from this node, too.
*/
- private tailrec fun correctPrev(op: OpDescriptor?): Node? {
+ private tailrec fun correctPrev(): Node? {
val oldPrev = _prev.value
var prev: Node = oldPrev
var last: Node? = null // will be set so that last.next === prev
@@ -278,22 +224,17 @@
// otherwise need to update prev
if (!this._prev.compareAndSet(oldPrev, prev)) {
// Note: retry from scratch on failure to update prev
- return correctPrev(op)
+ return correctPrev()
}
return prev // return the correct prev
}
// slow path when we need to help remove operations
this.isRemoved -> return null // nothing to do, this node was removed, bail out asap to save time
- prevNext === op -> return prev // part of the same op -- don't recurse, didn't correct prev
- prevNext is OpDescriptor -> { // help & retry
- prevNext.perform(prev)
- return correctPrev(op) // retry from scratch
- }
prevNext is Removed -> {
if (last !== null) {
// newly added (prev) node is already removed, correct last.next around it
if (!last._next.compareAndSet(prev, prevNext.ref)) {
- return correctPrev(op) // retry from scratch on failure to update next
+ return correctPrev() // retry from scratch on failure to update next
}
prev = last
last = null
@@ -327,15 +268,13 @@
* @suppress **This is unstable API and it is subject to change.**
*/
public actual open class LockFreeLinkedListHead : LockFreeLinkedListNode() {
- public actual val isEmpty: Boolean get() = next === this
-
/**
* Iterates over all elements in this list of a specified type.
*/
- public actual inline fun <reified T : Node> forEach(block: (T) -> Unit) {
+ public actual inline fun forEach(block: (Node) -> Unit) {
var cur: Node = next as Node
while (cur != this) {
- if (cur is T) block(cur)
+ block(cur)
cur = cur.nextNode
}
}
@@ -345,6 +284,6 @@
// optimization: because head is never removed, we don't have to read _next.value to check these:
override val isRemoved: Boolean get() = false
-
- override fun nextIfRemoved(): Node? = null
}
+
+private class ListClosed(@JvmField val forbiddenElementsBitmask: Int): LockFreeLinkedListNode()
diff --git a/kotlinx-coroutines-core/concurrent/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt b/kotlinx-coroutines-core/concurrent/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt
index a9e8756..99b3262 100644
--- a/kotlinx-coroutines-core/concurrent/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt
+++ b/kotlinx-coroutines-core/concurrent/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt
@@ -5,6 +5,7 @@
import kotlinx.coroutines.*
import kotlin.test.*
+@Suppress("DEPRECATION_ERROR")
class ConflatedBroadcastChannelNotifyStressTest : TestBase() {
private val nSenders = 2
private val nReceivers = 3
diff --git a/kotlinx-coroutines-core/jdk8/src/future/Future.kt b/kotlinx-coroutines-core/jdk8/src/future/Future.kt
index ac61b1e..37620bb 100644
--- a/kotlinx-coroutines-core/jdk8/src/future/Future.kt
+++ b/kotlinx-coroutines-core/jdk8/src/future/Future.kt
@@ -138,7 +138,7 @@
handleCoroutineException(EmptyCoroutineContext, e)
}
}
- result.cancelFutureOnCompletion(future)
+ result.invokeOnCompletion(handler = CancelFutureOnCompletion(future))
return result
}
@@ -190,3 +190,18 @@
}
}
}
+
+private class CancelFutureOnCompletion(
+ private val future: Future<*>
+) : JobNode() {
+ override val onCancelling get() = false
+
+ override fun invoke(cause: Throwable?) {
+ // Don't interrupt when cancelling future on completion, because no one is going to reset this
+ // interruption flag and it will cause spurious failures elsewhere.
+ // We do not cancel the future if it's already completed in some way,
+ // because `cancel` on a completed future won't change the state but is not guaranteed to behave well
+ // on reentrancy. See https://github.com/Kotlin/kotlinx.coroutines/issues/4156
+ if (cause != null && !future.isDone) future.cancel(false)
+ }
+}
diff --git a/kotlinx-coroutines-core/js/src/CoroutineContext.kt b/kotlinx-coroutines-core/js/src/CoroutineContext.kt
index 3156dca..4384943 100644
--- a/kotlinx-coroutines-core/js/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/js/src/CoroutineContext.kt
@@ -1,8 +1,6 @@
package kotlinx.coroutines
import kotlinx.browser.*
-import kotlinx.coroutines.internal.*
-import kotlin.coroutines.*
private external val navigator: dynamic
private const val UNDEFINED = "undefined"
@@ -28,30 +26,3 @@
jsTypeOf(navigator.userAgent) != UNDEFINED &&
jsTypeOf(navigator.userAgent.match) != UNDEFINED &&
navigator.userAgent.match("\\bjsdom\\b")
-
-@PublishedApi // Used from kotlinx-coroutines-test via suppress, not part of ABI
-internal actual val DefaultDelay: Delay
- get() = Dispatchers.Default as Delay
-
-public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
- val combined = coroutineContext + context
- return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
- combined + Dispatchers.Default else combined
-}
-
-public actual fun CoroutineContext.newCoroutineContext(addedContext: CoroutineContext): CoroutineContext {
- return this + addedContext
-}
-
-// No debugging facilities on JS
-internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T = block()
-internal actual inline fun <T> withContinuationContext(continuation: Continuation<*>, countOrElement: Any?, block: () -> T): T = block()
-internal actual fun Continuation<*>.toDebugString(): String = toString()
-internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on JS
-
-internal actual class UndispatchedCoroutine<in T> actual constructor(
- context: CoroutineContext,
- uCont: Continuation<T>
-) : ScopeCoroutine<T>(context, uCont) {
- override fun afterResume(state: Any?) = uCont.resumeWith(recoverResult(state, uCont))
-}
diff --git a/kotlinx-coroutines-core/js/src/JSDispatcher.kt b/kotlinx-coroutines-core/js/src/JSDispatcher.kt
index f547c15..f0cd50a 100644
--- a/kotlinx-coroutines-core/js/src/JSDispatcher.kt
+++ b/kotlinx-coroutines-core/js/src/JSDispatcher.kt
@@ -3,7 +3,7 @@
import org.w3c.dom.*
import kotlin.js.Promise
-public actual typealias W3CWindow = Window
+internal actual typealias W3CWindow = Window
internal actual fun w3cSetTimeout(window: W3CWindow, handler: () -> Unit, timeout: Int): Int =
setTimeout(window, handler, timeout)
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/EventLoop.kt b/kotlinx-coroutines-core/jsAndWasmJsShared/src/EventLoop.kt
similarity index 100%
rename from kotlinx-coroutines-core/jsAndWasmShared/src/EventLoop.kt
rename to kotlinx-coroutines-core/jsAndWasmJsShared/src/EventLoop.kt
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/JSDispatcher.kt b/kotlinx-coroutines-core/jsAndWasmJsShared/src/internal/JSDispatcher.kt
similarity index 96%
rename from kotlinx-coroutines-core/jsAndWasmShared/src/internal/JSDispatcher.kt
rename to kotlinx-coroutines-core/jsAndWasmJsShared/src/internal/JSDispatcher.kt
index b002ea7..c98a067 100644
--- a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/JSDispatcher.kt
+++ b/kotlinx-coroutines-core/jsAndWasmJsShared/src/internal/JSDispatcher.kt
@@ -3,7 +3,7 @@
import kotlinx.coroutines.internal.*
import kotlin.coroutines.*
-public expect abstract class W3CWindow
+internal expect abstract class W3CWindow
internal expect fun w3cSetTimeout(window: W3CWindow, handler: () -> Unit, timeout: Int): Int
internal expect fun w3cSetTimeout(handler: () -> Unit, timeout: Int): Int
internal expect fun w3cClearTimeout(handle: Int)
@@ -30,9 +30,9 @@
abstract fun scheduleQueueProcessing()
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
parallelism.checkParallelism()
- return this
+ return namedOrThis(name)
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/test/MessageQueueTest.kt b/kotlinx-coroutines-core/jsAndWasmJsShared/test/MessageQueueTest.kt
similarity index 100%
rename from kotlinx-coroutines-core/jsAndWasmShared/test/MessageQueueTest.kt
rename to kotlinx-coroutines-core/jsAndWasmJsShared/test/MessageQueueTest.kt
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/test/SetTimeoutDispatcherTest.kt b/kotlinx-coroutines-core/jsAndWasmJsShared/test/SetTimeoutDispatcherTest.kt
similarity index 100%
rename from kotlinx-coroutines-core/jsAndWasmShared/test/SetTimeoutDispatcherTest.kt
rename to kotlinx-coroutines-core/jsAndWasmJsShared/test/SetTimeoutDispatcherTest.kt
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/CloseableCoroutineDispatcher.kt b/kotlinx-coroutines-core/jsAndWasmShared/src/CloseableCoroutineDispatcher.kt
index 81e0c5d..3ea73ad 100644
--- a/kotlinx-coroutines-core/jsAndWasmShared/src/CloseableCoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/jsAndWasmShared/src/CloseableCoroutineDispatcher.kt
@@ -1,5 +1,5 @@
package kotlinx.coroutines
-public actual abstract class CloseableCoroutineDispatcher actual constructor() : CoroutineDispatcher() {
- public actual abstract fun close()
+public actual abstract class CloseableCoroutineDispatcher actual constructor() : CoroutineDispatcher(), AutoCloseable {
+ public actual abstract override fun close()
}
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/CoroutineContext.kt b/kotlinx-coroutines-core/jsAndWasmShared/src/CoroutineContext.kt
new file mode 100644
index 0000000..82862ac
--- /dev/null
+++ b/kotlinx-coroutines-core/jsAndWasmShared/src/CoroutineContext.kt
@@ -0,0 +1,31 @@
+package kotlinx.coroutines
+
+import kotlinx.coroutines.internal.ScopeCoroutine
+import kotlin.coroutines.*
+
+@PublishedApi // Used from kotlinx-coroutines-test via suppress, not part of ABI
+internal actual val DefaultDelay: Delay
+ get() = Dispatchers.Default as Delay
+
+public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
+ val combined = coroutineContext + context
+ return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
+ combined + Dispatchers.Default else combined
+}
+
+public actual fun CoroutineContext.newCoroutineContext(addedContext: CoroutineContext): CoroutineContext {
+ return this + addedContext
+}
+
+// No debugging facilities on Wasm and JS
+internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T = block()
+internal actual inline fun <T> withContinuationContext(continuation: Continuation<*>, countOrElement: Any?, block: () -> T): T = block()
+internal actual fun Continuation<*>.toDebugString(): String = toString()
+internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on Wasm and JS
+
+internal actual class UndispatchedCoroutine<in T> actual constructor(
+ context: CoroutineContext,
+ uCont: Continuation<T>
+) : ScopeCoroutine<T>(context, uCont) {
+ override fun afterResume(state: Any?) = uCont.resumeWith(recoverResult(state, uCont))
+}
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/SchedulerTask.kt b/kotlinx-coroutines-core/jsAndWasmShared/src/SchedulerTask.kt
index 111a9fc..24b2311 100644
--- a/kotlinx-coroutines-core/jsAndWasmShared/src/SchedulerTask.kt
+++ b/kotlinx-coroutines-core/jsAndWasmShared/src/SchedulerTask.kt
@@ -1,13 +1,3 @@
package kotlinx.coroutines
internal actual abstract class SchedulerTask : Runnable
-
-internal actual interface SchedulerTaskContext { }
-
-private object TaskContext: SchedulerTaskContext { }
-
-internal actual val SchedulerTask.taskContext: SchedulerTaskContext get() = TaskContext
-
-@Suppress("NOTHING_TO_INLINE")
-internal actual inline fun SchedulerTaskContext.afterTask() {}
-
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/Concurrent.kt b/kotlinx-coroutines-core/jsAndWasmShared/src/internal/Concurrent.kt
index 652d60c..74e54bc 100644
--- a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/Concurrent.kt
+++ b/kotlinx-coroutines-core/jsAndWasmShared/src/internal/Concurrent.kt
@@ -11,21 +11,21 @@
internal actual fun <E> identitySet(expectedSize: Int): MutableSet<E> = HashSet(expectedSize)
-internal actual class WorkaroundAtomicReference<T> actual constructor(private var value: T) {
+internal actual class WorkaroundAtomicReference<V> actual constructor(private var value: V) {
- public actual fun get(): T = value
+ public actual fun get(): V = value
- public actual fun set(value: T) {
+ public actual fun set(value: V) {
this.value = value
}
- public actual fun getAndSet(value: T): T {
+ public actual fun getAndSet(value: V): V {
val prev = this.value
this.value = value
return prev
}
- public actual fun compareAndSet(expected: T, value: T): Boolean {
+ public actual fun compareAndSet(expected: V, value: V): Boolean {
if (this.value === expected) {
this.value = value
return true
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/LinkedList.kt b/kotlinx-coroutines-core/jsAndWasmShared/src/internal/LinkedList.kt
index 8d68f8b..6810d61 100644
--- a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/LinkedList.kt
+++ b/kotlinx-coroutines-core/jsAndWasmShared/src/internal/LinkedList.kt
@@ -2,32 +2,32 @@
package kotlinx.coroutines.internal
-import kotlinx.coroutines.*
-
-private typealias Node = LinkedListNode
+private typealias Node = LockFreeLinkedListNode
/** @suppress **This is unstable API and it is subject to change.** */
-public actual typealias LockFreeLinkedListNode = LinkedListNode
-
-/** @suppress **This is unstable API and it is subject to change.** */
-public actual typealias LockFreeLinkedListHead = LinkedListHead
-
-/** @suppress **This is unstable API and it is subject to change.** */
-public open class LinkedListNode : DisposableHandle {
+public actual open class LockFreeLinkedListNode {
@PublishedApi internal var _next = this
@PublishedApi internal var _prev = this
@PublishedApi internal var _removed: Boolean = false
- public inline val nextNode get() = _next
- public inline val prevNode get() = _prev
- public inline val isRemoved get() = _removed
+ public actual inline val nextNode get() = _next
+ inline actual val prevNode get() = _prev
+ inline actual val isRemoved get() = _removed
- public fun addLast(node: Node) {
- val prev = this._prev
- node._next = this
- node._prev = prev
- prev._next = node
- this._prev = node
+ public actual fun addLast(node: Node, permissionsBitmask: Int): Boolean = when (val prev = this._prev) {
+ is ListClosed ->
+ prev.forbiddenElementsBitmask and permissionsBitmask == 0 && prev.addLast(node, permissionsBitmask)
+ else -> {
+ node._next = this
+ node._prev = prev
+ prev._next = node
+ this._prev = node
+ true
+ }
+ }
+
+ public actual fun close(forbiddenElementsBit: Int) {
+ addLast(ListClosed(forbiddenElementsBit), forbiddenElementsBit)
}
/*
@@ -36,16 +36,7 @@
* I.g. `LockFreeLinkedListHead` throws, while `SendElementWithUndeliveredHandler`
* invokes handler on remove
*/
- public open fun remove(): Boolean {
- return removeImpl()
- }
-
- override fun dispose() {
- remove()
- }
-
- @PublishedApi
- internal fun removeImpl(): Boolean {
+ public actual open fun remove(): Boolean {
if (_removed) return false
val prev = this._prev
val next = this._next
@@ -55,60 +46,28 @@
return true
}
- public fun addOneIfEmpty(node: Node): Boolean {
+ public actual fun addOneIfEmpty(node: Node): Boolean {
if (_next !== this) return false
- addLast(node)
+ addLast(node, Int.MIN_VALUE)
return true
}
-
- public inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean {
- if (!condition()) return false
- addLast(node)
- return true
- }
-
- public inline fun addLastIfPrev(node: Node, predicate: (Node) -> Boolean): Boolean {
- if (!predicate(_prev)) return false
- addLast(node)
- return true
- }
-
- public inline fun addLastIfPrevAndIf(
- node: Node,
- predicate: (Node) -> Boolean, // prev node predicate
- crossinline condition: () -> Boolean // atomically checked condition
- ): Boolean {
- if (!predicate(_prev)) return false
- if (!condition()) return false
- addLast(node)
- return true
- }
-
- public fun helpRemove() {} // No concurrency on JS -> no removal
-
- public fun removeFirstOrNull(): Node? {
- val next = _next
- if (next === this) return null
- check(next.removeImpl()) { "Should remove" }
- return next
- }
}
/** @suppress **This is unstable API and it is subject to change.** */
-public open class LinkedListHead : LinkedListNode() {
- public val isEmpty get() = _next === this
-
+public actual open class LockFreeLinkedListHead : Node() {
/**
* Iterates over all elements in this list of a specified type.
*/
- public inline fun <reified T : Node> forEach(block: (T) -> Unit) {
+ public actual inline fun forEach(block: (Node) -> Unit) {
var cur: Node = _next
while (cur != this) {
- if (cur is T) block(cur)
+ block(cur)
cur = cur._next
}
}
// just a defensive programming -- makes sure that list head sentinel is never removed
- public final override fun remove(): Nothing = throw UnsupportedOperationException()
+ public actual final override fun remove(): Nothing = throw UnsupportedOperationException()
}
+
+private class ListClosed(val forbiddenElementsBitmask: Int): LockFreeLinkedListNode()
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/ProbesSupport.kt b/kotlinx-coroutines-core/jsAndWasmShared/src/internal/ProbesSupport.kt
index 00581f1..7afce8f 100644
--- a/kotlinx-coroutines-core/jsAndWasmShared/src/internal/ProbesSupport.kt
+++ b/kotlinx-coroutines-core/jsAndWasmShared/src/internal/ProbesSupport.kt
@@ -4,3 +4,6 @@
@Suppress("NOTHING_TO_INLINE")
internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> = completion
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun <T> probeCoroutineResumed(completion: Continuation<T>) { }
diff --git a/kotlinx-coroutines-core/jsAndWasmShared/test/internal/LinkedListTest.kt b/kotlinx-coroutines-core/jsAndWasmShared/test/internal/LinkedListTest.kt
index 04b7006..305484f 100644
--- a/kotlinx-coroutines-core/jsAndWasmShared/test/internal/LinkedListTest.kt
+++ b/kotlinx-coroutines-core/jsAndWasmShared/test/internal/LinkedListTest.kt
@@ -6,19 +6,19 @@
import kotlin.test.assertTrue
class LinkedListTest {
- data class IntNode(val i: Int) : LinkedListNode()
+ data class IntNode(val i: Int) : LockFreeLinkedListNode()
@Test
fun testSimpleAddLastRemove() {
- val list = LinkedListHead()
+ val list = LockFreeLinkedListHead()
assertContents(list)
- val n1 = IntNode(1).apply { list.addLast(this) }
+ val n1 = IntNode(1).apply { list.addLast(this, Int.MAX_VALUE) }
assertContents(list, 1)
- val n2 = IntNode(2).apply { list.addLast(this) }
+ val n2 = IntNode(2).apply { list.addLast(this, Int.MAX_VALUE) }
assertContents(list, 1, 2)
- val n3 = IntNode(3).apply { list.addLast(this) }
+ val n3 = IntNode(3).apply { list.addLast(this, Int.MAX_VALUE) }
assertContents(list, 1, 2, 3)
- val n4 = IntNode(4).apply { list.addLast(this) }
+ val n4 = IntNode(4).apply { list.addLast(this, Int.MAX_VALUE) }
assertContents(list, 1, 2, 3, 4)
assertTrue(n1.remove())
assertContents(list, 2, 3, 4)
@@ -31,13 +31,12 @@
assertContents(list)
}
- private fun assertContents(list: LinkedListHead, vararg expected: Int) {
+ private fun assertContents(list: LockFreeLinkedListHead, vararg expected: Int) {
val n = expected.size
val actual = IntArray(n)
var index = 0
- list.forEach<IntNode> { actual[index++] = it.i }
+ list.forEach { if (it is IntNode) actual[index++] = it.i }
assertEquals(n, index)
for (i in 0 until n) assertEquals(expected[i], actual[i], "item i")
- assertEquals(expected.isEmpty(), list.isEmpty)
}
}
diff --git a/kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin b/kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin
index ce82c79..f8b6bcd 100644
--- a/kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin
+++ b/kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin
Binary files differ
diff --git a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
index b467cd1..3ce7e0d 100644
--- a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
+++ b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
@@ -184,6 +184,11 @@
(this as Object).notifyAll()
}
+ // User only for testing and nothing else
internal val isThreadPresent
get() = _thread != null
+
+ override fun toString(): String {
+ return "DefaultExecutor"
+ }
}
diff --git a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt
index 6b3fa5e..a5e8da4 100644
--- a/kotlinx-coroutines-core/jvm/src/Dispatchers.kt
+++ b/kotlinx-coroutines-core/jvm/src/Dispatchers.kt
@@ -62,7 +62,7 @@
* during operations over IO dispatcher.
*/
@JvmStatic
- public val IO: CoroutineDispatcher = DefaultIoScheduler
+ public val IO: CoroutineDispatcher get() = DefaultIoScheduler
/**
* Shuts down built-in dispatchers, such as [Default] and [IO],
diff --git a/kotlinx-coroutines-core/jvm/src/EventLoop.kt b/kotlinx-coroutines-core/jvm/src/EventLoop.kt
index d7388ce..15d4ab5 100644
--- a/kotlinx-coroutines-core/jvm/src/EventLoop.kt
+++ b/kotlinx-coroutines-core/jvm/src/EventLoop.kt
@@ -59,11 +59,11 @@
*
* ### Invariants
*
- * - When invoked from [Dispatchers.Default] **thread** (even if the actual context is different dispatcher,
- * [CoroutineDispatcher.limitedParallelism] or any in-place wrapper), it runs an arbitrary task that ended
- * up being scheduled to [Dispatchers.Default] or its counterpart. Tasks scheduled to [Dispatchers.IO]
- * **are not** executed[1].
- * - When invoked from [Dispatchers.IO] thread, the same rules apply, but for blocking tasks only.
+ * - When invoked from [Dispatchers.Default] **thread** (even if the actual context is different dispatcher,
+ * [CoroutineDispatcher.limitedParallelism] or any in-place wrapper), it runs an arbitrary task that ended
+ * up being scheduled to [Dispatchers.Default] or its counterpart. Tasks scheduled to [Dispatchers.IO]
+ * **are not** executed[1].
+ * - When invoked from [Dispatchers.IO] thread, the same rules apply, but for blocking tasks only.
*
* [1] -- this is purely technical limitation: the scheduler does not have "notify me when CPU token is available" API,
* and we cannot leave this method without leaving thread in its original state.
diff --git a/kotlinx-coroutines-core/jvm/src/Executors.kt b/kotlinx-coroutines-core/jvm/src/Executors.kt
index 53ef77d..8ba3f18 100644
--- a/kotlinx-coroutines-core/jvm/src/Executors.kt
+++ b/kotlinx-coroutines-core/jvm/src/Executors.kt
@@ -1,10 +1,10 @@
package kotlinx.coroutines
-import kotlinx.coroutines.flow.*
import kotlinx.coroutines.internal.*
-import java.io.*
+import java.io.Closeable
import java.util.concurrent.*
import kotlin.coroutines.*
+import kotlin.AutoCloseable
/**
* [CoroutineDispatcher] that has underlying [Executor] for dispatching tasks.
@@ -13,7 +13,7 @@
* This class is generally used as a bridge between coroutine-based API and
* asynchronous API that requires an instance of the [Executor].
*/
-public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closeable {
+public abstract class ExecutorCoroutineDispatcher : CoroutineDispatcher(), Closeable, AutoCloseable {
/** @suppress */
@ExperimentalStdlibApi
public companion object Key : AbstractCoroutineContextKey<CoroutineDispatcher, ExecutorCoroutineDispatcher>(
@@ -117,13 +117,12 @@
internal class ExecutorCoroutineDispatcherImpl(override val executor: Executor) : ExecutorCoroutineDispatcher(), Delay {
- /*
- * Attempts to reflectively (to be Java 6 compatible) invoke
- * ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy in order to cleanup
- * internal scheduler queue on cancellation.
- */
init {
- removeFutureOnCancel(executor)
+ /* Attempt to invoke ScheduledThreadPoolExecutor.setRemoveOnCancelPolicy in order to clean up
+ * the internal scheduler queue on cancellation. */
+ if (executor is ScheduledThreadPoolExecutor) {
+ executor.removeOnCancelPolicy = true
+ }
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
@@ -144,7 +143,7 @@
)
// If everything went fine and the scheduling attempt was not rejected -- use it
if (future != null) {
- continuation.cancelFutureOnCancellation(future)
+ continuation.invokeOnCancellation(CancelFutureOnCancel(future))
return
}
// Otherwise fallback to default executor
@@ -200,3 +199,12 @@
}
override fun toString(): String = "DisposableFutureHandle[$future]"
}
+
+private class CancelFutureOnCancel(private val future: Future<*>) : CancelHandler {
+ override fun invoke(cause: Throwable?) {
+ // Don't interrupt when cancelling future on completion, because no one is going to reset this
+ // interruption flag and it will cause spurious failures elsewhere
+ future.cancel(false)
+ }
+ override fun toString() = "CancelFutureOnCancel[$future]"
+}
diff --git a/kotlinx-coroutines-core/jvm/src/Future.kt b/kotlinx-coroutines-core/jvm/src/Future.kt
index 2e217cc..be9466d 100644
--- a/kotlinx-coroutines-core/jvm/src/Future.kt
+++ b/kotlinx-coroutines-core/jvm/src/Future.kt
@@ -9,36 +9,19 @@
* Cancels a specified [future] when this job is cancelled.
* This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
* ```
- * invokeOnCompletion { if (it != null) future.cancel(false) }
- * ```
- *
- * @suppress **This an internal API and should not be used from general code.**
- */
-@InternalCoroutinesApi
-public fun Job.cancelFutureOnCompletion(future: Future<*>): DisposableHandle =
- invokeOnCompletion(handler = CancelFutureOnCompletion(future))
-
-/**
- * Cancels a specified [future] when this job is cancelled.
- * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
- * ```
* invokeOnCancellation { if (it != null) future.cancel(false) }
* ```
*/
+// Warning since 1.9.0
+@Deprecated(
+ "This function does not do what its name implies: it will not cancel the future if just cancel() was called.",
+ level = DeprecationLevel.WARNING,
+ replaceWith = ReplaceWith("this.invokeOnCancellation { future.cancel(false) }")
+)
public fun CancellableContinuation<*>.cancelFutureOnCancellation(future: Future<*>): Unit =
- invokeOnCancellation(handler = CancelFutureOnCancel(future))
+ invokeOnCancellation(handler = PublicCancelFutureOnCancel(future))
-private class CancelFutureOnCompletion(
- private val future: Future<*>
-) : JobNode() {
- override fun invoke(cause: Throwable?) {
- // Don't interrupt when cancelling future on completion, because no one is going to reset this
- // interruption flag and it will cause spurious failures elsewhere
- if (cause != null) future.cancel(false)
- }
-}
-
-private class CancelFutureOnCancel(private val future: Future<*>) : CancelHandler {
+private class PublicCancelFutureOnCancel(private val future: Future<*>) : CancelHandler {
override fun invoke(cause: Throwable?) {
// Don't interrupt when cancelling future on completion, because no one is going to reset this
// interruption flag and it will cause spurious failures elsewhere
diff --git a/kotlinx-coroutines-core/jvm/src/Interruptible.kt b/kotlinx-coroutines-core/jvm/src/Interruptible.kt
index b25a289..6b52f49 100644
--- a/kotlinx-coroutines-core/jvm/src/Interruptible.kt
+++ b/kotlinx-coroutines-core/jvm/src/Interruptible.kt
@@ -42,8 +42,8 @@
private fun <T> runInterruptibleInExpectedContext(coroutineContext: CoroutineContext, block: () -> T): T {
try {
- val threadState = ThreadState(coroutineContext.job)
- threadState.setup()
+ val threadState = ThreadState()
+ threadState.setup(coroutineContext.job)
try {
return block()
} finally {
@@ -59,7 +59,7 @@
private const val INTERRUPTING = 2
private const val INTERRUPTED = 3
-private class ThreadState(private val job: Job) : InternalCompletionHandler {
+private class ThreadState : JobNode() {
/*
=== States ===
@@ -95,8 +95,10 @@
// Registered cancellation handler
private var cancelHandle: DisposableHandle? = null
- fun setup() {
- cancelHandle = job.invokeOnCompletion(onCancelling = true, invokeImmediately = true, handler = this)
+ override val onCancelling get() = true
+
+ fun setup(job: Job) {
+ cancelHandle = job.invokeOnCompletion(handler = this)
// Either we successfully stored it or it was immediately cancelled
_state.loop { state ->
when (state) {
diff --git a/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt b/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt
index 5f81dd3..ca1ab87 100644
--- a/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt
+++ b/kotlinx-coroutines-core/jvm/src/SchedulerTask.kt
@@ -3,12 +3,3 @@
import kotlinx.coroutines.scheduling.*
internal actual typealias SchedulerTask = Task
-
-internal actual typealias SchedulerTaskContext = TaskContext
-
-@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
-internal actual val SchedulerTask.taskContext: SchedulerTaskContext get() = taskContext
-
-@Suppress("NOTHING_TO_INLINE", "EXTENSION_SHADOWED_BY_MEMBER")
-internal actual inline fun SchedulerTaskContext.afterTask() =
- afterTask()
diff --git a/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt b/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt
index 15b2780..c1898fb 100644
--- a/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt
+++ b/kotlinx-coroutines-core/jvm/src/ThreadContextElement.kt
@@ -200,10 +200,10 @@
* ...
* println(myThreadLocal.get()) // Prints "null"
* launch(Dispatchers.Default + myThreadLocal.asContextElement(value = "foo")) {
- * println(myThreadLocal.get()) // Prints "foo"
- * withContext(Dispatchers.Main) {
- * println(myThreadLocal.get()) // Prints "foo", but it's on UI thread
- * }
+ * println(myThreadLocal.get()) // Prints "foo"
+ * withContext(Dispatchers.Main) {
+ * println(myThreadLocal.get()) // Prints "foo", but it's on UI thread
+ * }
* }
* println(myThreadLocal.get()) // Prints "null"
* ```
@@ -213,8 +213,8 @@
* ```
* myThreadLocal.set("main")
* withContext(Dispatchers.Main) {
- * println(myThreadLocal.get()) // Prints "main"
- * myThreadLocal.set("UI")
+ * println(myThreadLocal.get()) // Prints "main"
+ * myThreadLocal.set("UI")
* }
* println(myThreadLocal.get()) // Prints "main", not "UI"
* ```
@@ -231,13 +231,13 @@
* val tl = ThreadLocal.withInitial { "initial" }
*
* runBlocking {
- * println(tl.get()) // Will print "initial"
- * // Change context
- * withContext(tl.asContextElement("modified")) {
- * println(tl.get()) // Will print "modified"
- * }
- * // Context is changed again
- * println(tl.get()) // <- WARN: can print either "modified" or "initial"
+ * println(tl.get()) // Will print "initial"
+ * // Change context
+ * withContext(tl.asContextElement("modified")) {
+ * println(tl.get()) // Will print "modified"
+ * }
+ * // Context is changed again
+ * println(tl.get()) // <- WARN: can print either "modified" or "initial"
* }
* ```
* to fix this behaviour use `runBlocking(tl.asContextElement())`
@@ -252,10 +252,10 @@
* Example of usage:
* ```
* suspend fun processRequest() {
- * if (traceCurrentRequestThreadLocal.isPresent()) { // Probabilistic tracing
- * // Do some heavy-weight tracing
- * }
- * // Process request regularly
+ * if (traceCurrentRequestThreadLocal.isPresent()) { // Probabilistic tracing
+ * // Do some heavy-weight tracing
+ * }
+ * // Process request regularly
* }
* ```
*/
@@ -269,13 +269,13 @@
* E.g. one may use the following method to enforce proper use of the thread locals with coroutines:
* ```
* public suspend inline fun <T> ThreadLocal<T>.getSafely(): T {
- * ensurePresent()
- * return get()
+ * ensurePresent()
+ * return get()
* }
*
* // Usage
* withContext(...) {
- * val value = threadLocal.getSafely() // Fail-fast in case of improper context
+ * val value = threadLocal.getSafely() // Fail-fast in case of improper context
* }
* ```
*/
diff --git a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugCoroutineInfoImpl.kt b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugCoroutineInfoImpl.kt
index 66bb904..47d6936 100644
--- a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugCoroutineInfoImpl.kt
+++ b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugCoroutineInfoImpl.kt
@@ -30,8 +30,10 @@
*/
private val _context = WeakReference(context)
public val context: CoroutineContext? // can be null when the coroutine was already garbage-collected
+ // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
get() = _context.get()
+ // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
public val creationStackTrace: List<StackTraceElement> get() = creationStackTrace()
/**
diff --git a/kotlinx-coroutines-core/jvm/src/debug/internal/StackTraceFrame.kt b/kotlinx-coroutines-core/jvm/src/debug/internal/StackTraceFrame.kt
index d780545..5ab67dc 100644
--- a/kotlinx-coroutines-core/jvm/src/debug/internal/StackTraceFrame.kt
+++ b/kotlinx-coroutines-core/jvm/src/debug/internal/StackTraceFrame.kt
@@ -5,11 +5,9 @@
/**
* A stack-trace represented as [CoroutineStackFrame].
*/
-@PublishedApi
-internal class StackTraceFrame internal constructor(
+internal class StackTraceFrame(
override val callerFrame: CoroutineStackFrame?,
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
- @JvmField public val stackTraceElement: StackTraceElement
+ private val stackTraceElement: StackTraceElement
) : CoroutineStackFrame {
override fun getStackTraceElement(): StackTraceElement = stackTraceElement
}
diff --git a/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt b/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt
index e6f3453..d8c46d2 100644
--- a/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt
+++ b/kotlinx-coroutines-core/jvm/src/flow/internal/FlowExceptions.kt
@@ -1,7 +1,6 @@
package kotlinx.coroutines.flow.internal
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
internal actual class AbortFlowException actual constructor(
@JvmField @Transient actual val owner: Any
diff --git a/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt b/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt
index 4e8eaf2..3fc97b1 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt
@@ -1,8 +1,6 @@
package kotlinx.coroutines.internal
-import java.lang.reflect.*
import java.util.*
-import java.util.concurrent.*
import kotlin.concurrent.withLock as withLockJvm
@Suppress("ACTUAL_WITHOUT_EXPECT")
@@ -10,26 +8,15 @@
internal actual inline fun <T> ReentrantLock.withLock(action: () -> T) = this.withLockJvm(action)
-@Suppress("ACTUAL_WITHOUT_EXPECT") // Visibility
+@Suppress("ACTUAL_WITHOUT_EXPECT", "NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // Visibility
internal actual typealias WorkaroundAtomicReference<T> = java.util.concurrent.atomic.AtomicReference<T>
+// BenignDataRace is OptionalExpectation and doesn't have to be here
+// but then IC breaks. See KT-66317
+@Retention(AnnotationRetention.SOURCE)
+@Target(AnnotationTarget.FIELD)
+internal actual annotation class BenignDataRace()
+
@Suppress("NOTHING_TO_INLINE") // So that R8 can completely remove ConcurrentKt class
internal actual inline fun <E> identitySet(expectedSize: Int): MutableSet<E> =
Collections.newSetFromMap(IdentityHashMap(expectedSize))
-
-private val REMOVE_FUTURE_ON_CANCEL: Method? = try {
- ScheduledThreadPoolExecutor::class.java.getMethod("setRemoveOnCancelPolicy", Boolean::class.java)
-} catch (e: Throwable) {
- null
-}
-
-@Suppress("NAME_SHADOWING")
-internal fun removeFutureOnCancel(executor: Executor): Boolean {
- try {
- val executor = executor as? ScheduledThreadPoolExecutor ?: return false
- (REMOVE_FUTURE_ON_CANCEL ?: return false).invoke(executor, true)
- return true
- } catch (e: Throwable) {
- return false // failed to setRemoveOnCancelPolicy, assume it does not removes future on cancel
- }
-}
diff --git a/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt b/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt
index 6267581..eb2c486 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/FastServiceLoader.kt
@@ -1,11 +1,11 @@
package kotlinx.coroutines.internal
+import kotlinx.coroutines.CoroutineExceptionHandler
import java.io.*
import java.net.*
import java.util.*
import java.util.jar.*
import java.util.zip.*
-import kotlin.collections.ArrayList
/**
* Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
@@ -35,7 +35,7 @@
* If lookups are successful, we return resultinAg instances because we know that
* `MainDispatcherFactory` API is internal and this is the only possible classes of `MainDispatcherFactory` Service on Android.
*
- * Such intricate dance is required to avoid calls to `ServiceLoader.load` for multiple reasons:
+ * Such an intricate dance is required to avoid calls to `ServiceLoader.load` for multiple reasons:
* 1) It eliminates disk lookup on potentially slow devices on the Main thread.
* 2) Various Android toolchain versions by various vendors don't tend to handle ServiceLoader calls properly.
* Sometimes META-INF is removed from the resulting APK, sometimes class names are mangled, etc.
@@ -51,12 +51,24 @@
return load(clz, clz.classLoader)
}
+ /*
+ * If `ANDROID_DETECTED` is true, it is still possible to have `AndroidDispatcherFactory` missing.
+ * The most notable case of it is firebase-sdk that repackages some Android classes but can be used from an arbitrary
+ * K/JVM application.
+ * See also #3914.
+ */
return try {
val result = ArrayList<MainDispatcherFactory>(2)
- createInstanceOf(clz, "kotlinx.coroutines.android.AndroidDispatcherFactory")?.apply { result.add(this) }
+ val mainFactory = createInstanceOf(clz, "kotlinx.coroutines.android.AndroidDispatcherFactory")
+ if (mainFactory == null) {
+ // Fallback to regular service loading
+ return load(clz, clz.classLoader)
+ }
+ result.add(mainFactory)
+ // Also search for test-module factory
createInstanceOf(clz, "kotlinx.coroutines.test.internal.TestMainDispatcherFactory")?.apply { result.add(this) }
result
- } catch (e: Throwable) {
+ } catch (_: Throwable) {
// Fallback to the regular SL in case of any unexpected exception
load(clz, clz.classLoader)
}
@@ -73,7 +85,7 @@
return try {
val clz = Class.forName(serviceClass, true, baseClass.classLoader)
baseClass.cast(clz.getDeclaredConstructor().newInstance())
- } catch (e: ClassNotFoundException) { // Do not fail if TestMainDispatcherFactory is not found
+ } catch (_: ClassNotFoundException) { // Do not fail if TestMainDispatcherFactory is not found
null
}
}
@@ -81,7 +93,7 @@
private fun <S> load(service: Class<S>, loader: ClassLoader): List<S> {
return try {
loadProviders(service, loader)
- } catch (e: Throwable) {
+ } catch (_: Throwable) {
// Fallback to default service loader
ServiceLoader.load(service, loader).toList()
}
diff --git a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt
index 3eaf723..0cace58 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt
@@ -91,7 +91,7 @@
override fun isDispatchNeeded(context: CoroutineContext): Boolean =
missing()
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher =
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher =
missing()
override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle =
diff --git a/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt b/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt
index 53713be..47c8189 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/ProbesSupport.kt
@@ -3,6 +3,10 @@
package kotlinx.coroutines.internal
import kotlin.coroutines.*
-import kotlin.coroutines.jvm.internal.probeCoroutineCreated as probe
-internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> = probe(completion)
+internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> =
+ kotlin.coroutines.jvm.internal.probeCoroutineCreated(completion)
+
+internal actual inline fun <T> probeCoroutineResumed(completion: Continuation<T>) {
+ kotlin.coroutines.jvm.internal.probeCoroutineResumed(completion)
+}
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
index 4d6dce3..3c22116 100644
--- a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
+++ b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
@@ -10,22 +10,25 @@
import kotlin.math.*
/**
- * Coroutine scheduler (pool of shared threads) which primary target is to distribute dispatched coroutines
- * over worker threads, including both CPU-intensive and blocking tasks, in the most efficient manner.
+ * Coroutine scheduler (pool of shared threads) with a primary target to distribute dispatched coroutines
+ * over worker threads, including both CPU-intensive and potentially blocking tasks, in the most efficient manner.
*
- * Current scheduler implementation has two optimization targets:
- * - Efficiency in the face of communication patterns (e.g. actors communicating via channel)
- * - Dynamic resizing to support blocking calls without re-dispatching coroutine to separate "blocking" thread pool.
+ * The current scheduler implementation has two optimization targets:
+ * - Efficiency in the face of communication patterns (e.g. actors communicating via channel).
+ * - Dynamic thread state and resizing to schedule blocking calls without re-dispatching coroutine to a separate "blocking" thread pool.
*
* ### Structural overview
*
- * Scheduler consists of [corePoolSize] worker threads to execute CPU-bound tasks and up to
- * [maxPoolSize] lazily created threads to execute blocking tasks.
- * Every worker has a local queue in addition to a global scheduler queue
- * and the global queue has priority over local queue to avoid starvation of externally-submitted
- * (e.g. from Android UI thread) tasks.
- * Work-stealing is implemented on top of that queues to provide
- * even load distribution and illusion of centralized run queue.
+ * The scheduler consists of [corePoolSize] worker threads to execute CPU-bound tasks and up to
+ * [maxPoolSize] lazily created threads to execute blocking tasks.
+ * The scheduler has two global queues -- one for CPU tasks and one for blocking tasks.
+ * These queues are used for tasks that a submited externally (from threads not belonging to the scheduler)
+ * and as overflow buffers for thread-local queues.
+ *
+ * Every worker has a local queue in addition to global scheduler queues.
+ * The queue to pick the task from is selected randomly to avoid starvation of both local queue and
+ * global queue submitted tasks.
+ * Work-stealing is implemented on top of that queues to provide even load distribution and an illusion of centralized run queue.
*
* ### Scheduling policy
*
@@ -33,7 +36,7 @@
* If the head is not empty, the task from the head is moved to the tail. Though it is an unfair scheduling policy,
* it effectively couples communicating coroutines into one and eliminates scheduling latency
* that arises from placing tasks to the end of the queue.
- * Placing former head to the tail is necessary to provide semi-FIFO order, otherwise, queue degenerates to stack.
+ * Placing former head to the tail is necessary to provide semi-FIFO order, otherwise, queue degenerates to a stack.
* When a coroutine is dispatched from an external thread, it's put into the global queue.
* The original idea with a single-slot LIFO buffer comes from Golang runtime scheduler by D. Vyukov.
* It was proven to be "fair enough", performant and generally well accepted and initially was a significant inspiration
@@ -45,39 +48,41 @@
* before parking when his local queue is empty.
* A non-standard solution is implemented to provide tasks affinity: a task from FIFO buffer may be stolen
* only if it is stale enough based on the value of [WORK_STEALING_TIME_RESOLUTION_NS].
- * For this purpose, monotonic global clock is used, and every task has associated with its submission time.
+ * For this purpose, monotonic global clock is used, and every task has a submission time associated with task.
* This approach shows outstanding results when coroutines are cooperative,
- * but as downside scheduler now depends on a high-resolution global clock,
- * which may limit scalability on NUMA machines. Tasks from LIFO buffer can be stolen on a regular basis.
+ * but as a downside, the scheduler now depends on a high-resolution global clock,
+ * which may limit scalability on NUMA machines.
*
* ### Thread management
- * One of the hardest parts of the scheduler is decentralized management of the threads with the progress guarantees
+ *
+ * One of the hardest parts of the scheduler is decentralized management of the threads with progress guarantees
* similar to the regular centralized executors.
* The state of the threads consists of [controlState] and [parkedWorkersStack] fields.
- * The former field incorporates the amount of created threads, CPU-tokens and blocking tasks
- * that require a thread compensation,
- * while the latter represents intrusive versioned Treiber stack of idle workers.
- * When a worker cannot find any work, they first add themselves to the stack,
+ * The former field incorporates the number of created threads, CPU-tokens and blocking tasks
+ * that require thread compensation,
+ * while the latter represents an intrusive versioned Treiber stack of idle workers.
+ * When a worker cannot find any work, it first adds itself to the stack,
* then re-scans the queue to avoid missing signals and then attempts to park
- * with additional rendezvous against unnecessary parking.
+ * with an additional rendezvous against unnecessary parking.
* If a worker finds a task that it cannot yet steal due to time constraints, it stores this fact in its state
* (to be uncounted when additional work is signalled) and parks for such duration.
*
- * When a new task arrives in the scheduler (whether it is local or global queue),
+ * When a new task arrives to the scheduler (whether it is a local or a global queue),
* either an idle worker is being signalled, or a new worker is attempted to be created.
* (Only [corePoolSize] workers can be created for regular CPU tasks)
*
* ### Support for blocking tasks
- * The scheduler also supports the notion of [blocking][TASK_PROBABLY_BLOCKING] tasks.
- * When executing or enqueuing blocking tasks, the scheduler notifies or creates one more worker in
- * addition to core pool size, so at any given moment, it has [corePoolSize] threads (potentially not yet created)
- * to serve CPU-bound tasks. To properly guarantee liveness, the scheduler maintains
- * "CPU permits" -- [corePoolSize] special tokens that permit an arbitrary worker to execute and steal CPU-bound tasks.
- * When worker encounters blocking tasks, it basically hands off its permit to another thread (not directly though) to
- * keep invariant "scheduler always has at least min(pending CPU tasks, core pool size)
+ *
+ * The scheduler also supports the notion of [blocking][Task.isBlocking] tasks.
+ * When executing or enqueuing blocking tasks, the scheduler notifies or creates an additional worker in
+ * addition to the core pool size, so at any given moment, it has [corePoolSize] threads (potentially not yet created)
+ * available to serve CPU-bound tasks. To properly guarantee liveness, the scheduler maintains
+ * "CPU permits" -- #[corePoolSize] special tokens that allow an arbitrary worker to execute and steal CPU-bound tasks.
+ * When a worker encounters a blocking tasks, it releases its permit to the scheduler to
+ * keep an invariant "scheduler always has at least min(pending CPU tasks, core pool size)
* and at most core pool size threads to execute CPU tasks".
* To avoid overprovision, workers without CPU permit are allowed to scan [globalBlockingQueue]
- * and steal **only** blocking tasks from other workers.
+ * and steal **only** blocking tasks from other workers which imposes a non-trivial complexity to the queue management.
*
* The scheduler does not limit the count of pending blocking tasks, potentially creating up to [maxPoolSize] threads.
* End users do not have access to the scheduler directly and can dispatch blocking tasks only with
@@ -420,7 +425,7 @@
block.taskContext = taskContext
return block
}
- return TaskImpl(block, nanoTime, taskContext)
+ return block.asTask(nanoTime, taskContext)
}
// NB: should only be called from 'dispatch' method due to blocking tasks increment
@@ -509,7 +514,7 @@
*/
if (state === WorkerState.TERMINATED) return task
// Do not add CPU tasks in local queue if we are not able to execute it
- if (task.mode == TASK_NON_BLOCKING && state === WorkerState.BLOCKING) {
+ if (!task.isBlocking && state === WorkerState.BLOCKING) {
return task
}
mayHaveLocalTasks = true
@@ -805,29 +810,26 @@
private fun inStack(): Boolean = nextParkedWorker !== NOT_IN_STACK
private fun executeTask(task: Task) {
- val taskMode = task.mode
- idleReset(taskMode)
- beforeTask(taskMode)
- runSafely(task)
- afterTask(taskMode)
- }
-
- private fun beforeTask(taskMode: Int) {
- if (taskMode == TASK_NON_BLOCKING) return
- // Always notify about new work when releasing CPU-permit to execute some blocking task
- if (tryReleaseCpu(WorkerState.BLOCKING)) {
- signalCpuWork()
+ terminationDeadline = 0L // reset deadline for termination
+ if (state == WorkerState.PARKING) {
+ assert { task.isBlocking }
+ state = WorkerState.BLOCKING
}
- }
-
- private fun afterTask(taskMode: Int) {
- if (taskMode == TASK_NON_BLOCKING) return
- decrementBlockingTasks()
- val currentState = state
- // Shutdown sequence of blocking dispatcher
- if (currentState !== WorkerState.TERMINATED) {
- assert { currentState == WorkerState.BLOCKING } // "Expected BLOCKING state, but has $currentState"
- state = WorkerState.DORMANT
+ if (task.isBlocking) {
+ // Always notify about new work when releasing CPU-permit to execute some blocking task
+ if (tryReleaseCpu(WorkerState.BLOCKING)) {
+ signalCpuWork()
+ }
+ runSafely(task)
+ decrementBlockingTasks()
+ val currentState = state
+ // Shutdown sequence of blocking dispatcher
+ if (currentState !== WorkerState.TERMINATED) {
+ assert { currentState == WorkerState.BLOCKING } // "Expected BLOCKING state, but has $currentState"
+ state = WorkerState.DORMANT
+ }
+ } else {
+ runSafely(task)
}
}
@@ -918,15 +920,6 @@
state = WorkerState.TERMINATED
}
- // It is invoked by this worker when it finds a task
- private fun idleReset(mode: Int) {
- terminationDeadline = 0L // reset deadline for termination
- if (state == WorkerState.PARKING) {
- assert { mode == TASK_PROBABLY_BLOCKING }
- state = WorkerState.BLOCKING
- }
- }
-
fun findTask(mayHaveLocalTasks: Boolean): Task? {
if (tryAcquireCpuPermit()) return findAnyTask(mayHaveLocalTasks)
/*
@@ -1008,12 +1001,12 @@
enum class WorkerState {
/**
- * Has CPU token and either executes [TASK_NON_BLOCKING] task or tries to find one.
+ * Has CPU token and either executes a [Task.isBlocking]` == false` task or tries to find one.
*/
CPU_ACQUIRED,
/**
- * Executing task with [TASK_PROBABLY_BLOCKING].
+ * Executing task with [Task.isBlocking].
*/
BLOCKING,
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/Deprecated.kt b/kotlinx-coroutines-core/jvm/src/scheduling/Deprecated.kt
deleted file mode 100644
index 2fd3173..0000000
--- a/kotlinx-coroutines-core/jvm/src/scheduling/Deprecated.kt
+++ /dev/null
@@ -1,208 +0,0 @@
-@file:Suppress("unused")
-
-package kotlinx.coroutines.scheduling
-import kotlinx.atomicfu.*
-import kotlinx.coroutines.*
-import java.util.concurrent.*
-import kotlin.coroutines.*
-
-/**
- * This API was "public @InternalApi" and leaked into Ktor enabled-by-default sources.
- * Since then, we refactored scheduler sources and its API and decided to get rid of it in
- * its current shape.
- *
- * To preserve backwards compatibility with Ktor 1.x, previous version of the code is
- * extracted here as is and isolated from the rest of code base, so R8 can get rid of it.
- *
- * It should be removed after Ktor 3.0.0 (EOL of Ktor 1.x) around 2022.
- */
-@PublishedApi
-internal open class ExperimentalCoroutineDispatcher(
- private val corePoolSize: Int,
- private val maxPoolSize: Int,
- private val idleWorkerKeepAliveNs: Long,
- private val schedulerName: String = "CoroutineScheduler"
-) : ExecutorCoroutineDispatcher() {
- public constructor(
- corePoolSize: Int = CORE_POOL_SIZE,
- maxPoolSize: Int = MAX_POOL_SIZE,
- schedulerName: String = DEFAULT_SCHEDULER_NAME
- ) : this(corePoolSize, maxPoolSize, IDLE_WORKER_KEEP_ALIVE_NS, schedulerName)
-
- @Deprecated(message = "Binary compatibility for Ktor 1.0-beta", level = DeprecationLevel.HIDDEN)
- public constructor(
- corePoolSize: Int = CORE_POOL_SIZE,
- maxPoolSize: Int = MAX_POOL_SIZE
- ) : this(corePoolSize, maxPoolSize, IDLE_WORKER_KEEP_ALIVE_NS)
-
- override val executor: Executor
- get() = coroutineScheduler
-
- // This is variable for test purposes, so that we can reinitialize from clean state
- private var coroutineScheduler = createScheduler()
-
- override fun dispatch(context: CoroutineContext, block: Runnable): Unit =
- try {
- coroutineScheduler.dispatch(block)
- } catch (e: RejectedExecutionException) {
- // CoroutineScheduler only rejects execution when it is being closed and this behavior is reserved
- // for testing purposes, so we don't have to worry about cancelling the affected Job here.
- DefaultExecutor.dispatch(context, block)
- }
-
- override fun dispatchYield(context: CoroutineContext, block: Runnable): Unit =
- try {
- coroutineScheduler.dispatch(block, tailDispatch = true)
- } catch (e: RejectedExecutionException) {
- // CoroutineScheduler only rejects execution when it is being closed and this behavior is reserved
- // for testing purposes, so we don't have to worry about cancelling the affected Job here.
- DefaultExecutor.dispatchYield(context, block)
- }
-
- override fun close(): Unit = coroutineScheduler.close()
-
- override fun toString(): String {
- return "${super.toString()}[scheduler = $coroutineScheduler]"
- }
-
- /**
- * Creates a coroutine execution context with limited parallelism to execute tasks which may potentially block.
- * Resulting [CoroutineDispatcher] doesn't own any resources (its threads) and provides a view of the original [ExperimentalCoroutineDispatcher],
- * giving it additional hints to adjust its behaviour.
- *
- * @param parallelism parallelism level, indicating how many threads can execute tasks in the resulting dispatcher parallel.
- */
- fun blocking(parallelism: Int = 16): CoroutineDispatcher {
- require(parallelism > 0) { "Expected positive parallelism level, but have $parallelism" }
- return LimitingDispatcher(this, parallelism, null, TASK_PROBABLY_BLOCKING)
- }
-
- /**
- * Creates a coroutine execution context with limited parallelism to execute CPU-intensive tasks.
- * Resulting [CoroutineDispatcher] doesn't own any resources (its threads) and provides a view of the original [ExperimentalCoroutineDispatcher],
- * giving it additional hints to adjust its behaviour.
- *
- * @param parallelism parallelism level, indicating how many threads can execute tasks in the resulting dispatcher parallel.
- */
- fun limited(parallelism: Int): CoroutineDispatcher {
- require(parallelism > 0) { "Expected positive parallelism level, but have $parallelism" }
- require(parallelism <= corePoolSize) { "Expected parallelism level lesser than core pool size ($corePoolSize), but have $parallelism" }
- return LimitingDispatcher(this, parallelism, null, TASK_NON_BLOCKING)
- }
-
- internal fun dispatchWithContext(block: Runnable, context: TaskContext, tailDispatch: Boolean) {
- try {
- coroutineScheduler.dispatch(block, context, tailDispatch)
- } catch (e: RejectedExecutionException) {
- // CoroutineScheduler only rejects execution when it is being closed and this behavior is reserved
- // for testing purposes, so we don't have to worry about cancelling the affected Job here.
- // TaskContext shouldn't be lost here to properly invoke before/after task
- DefaultExecutor.enqueue(coroutineScheduler.createTask(block, context))
- }
- }
-
- private fun createScheduler() = CoroutineScheduler(corePoolSize, maxPoolSize, idleWorkerKeepAliveNs, schedulerName)
-}
-
-private class LimitingDispatcher(
- private val dispatcher: ExperimentalCoroutineDispatcher,
- private val parallelism: Int,
- private val name: String?,
- override val taskMode: Int
-) : ExecutorCoroutineDispatcher(), TaskContext, Executor {
-
- private val queue = ConcurrentLinkedQueue<Runnable>()
- private val inFlightTasks = atomic(0)
-
- override val executor: Executor
- get() = this
-
- override fun execute(command: Runnable) = dispatch(command, false)
-
- override fun close(): Unit = error("Close cannot be invoked on LimitingBlockingDispatcher")
-
- override fun dispatch(context: CoroutineContext, block: Runnable) = dispatch(block, false)
-
- private fun dispatch(block: Runnable, tailDispatch: Boolean) {
- var taskToSchedule = block
- while (true) {
- // Commit in-flight tasks slot
- val inFlight = inFlightTasks.incrementAndGet()
-
- // Fast path, if parallelism limit is not reached, dispatch task and return
- if (inFlight <= parallelism) {
- dispatcher.dispatchWithContext(taskToSchedule, this, tailDispatch)
- return
- }
-
- // Parallelism limit is reached, add task to the queue
- queue.add(taskToSchedule)
-
- /*
- * We're not actually scheduled anything, so rollback committed in-flight task slot:
- * If the amount of in-flight tasks is still above the limit, do nothing
- * If the amount of in-flight tasks is lesser than parallelism, then
- * it's a race with a thread which finished the task from the current context, we should resubmit the first task from the queue
- * to avoid starvation.
- *
- * Race example #1 (TN is N-th thread, R is current in-flight tasks number), execution is sequential:
- *
- * T1: submit task, start execution, R == 1
- * T2: commit slot for next task, R == 2
- * T1: finish T1, R == 1
- * T2: submit next task to local queue, decrement R, R == 0
- * Without retries, task from T2 will be stuck in the local queue
- */
- if (inFlightTasks.decrementAndGet() >= parallelism) {
- return
- }
-
- taskToSchedule = queue.poll() ?: return
- }
- }
-
- override fun dispatchYield(context: CoroutineContext, block: Runnable) {
- dispatch(block, tailDispatch = true)
- }
-
- override fun toString(): String {
- return name ?: "${super.toString()}[dispatcher = $dispatcher]"
- }
-
- /**
- * Tries to dispatch tasks which were blocked due to reaching parallelism limit if there is any.
- *
- * Implementation note: blocking tasks are scheduled in a fair manner (to local queue tail) to avoid
- * non-blocking continuations starvation.
- * E.g. for
- * ```
- * foo()
- * blocking()
- * bar()
- * ```
- * it's more profitable to execute bar at the end of `blocking` rather than pending blocking task
- */
- override fun afterTask() {
- var next = queue.poll()
- // If we have pending tasks in current blocking context, dispatch first
- if (next != null) {
- dispatcher.dispatchWithContext(next, this, true)
- return
- }
- inFlightTasks.decrementAndGet()
-
- /*
- * Re-poll again and try to submit task if it's required otherwise tasks may be stuck in the local queue.
- * Race example #2 (TN is N-th thread, R is current in-flight tasks number), execution is sequential:
- * T1: submit task, start execution, R == 1
- * T2: commit slot for next task, R == 2
- * T1: finish T1, poll queue (it's still empty), R == 2
- * T2: submit next task to the local queue, decrement R, R == 1
- * T1: decrement R, finish. R == 0
- *
- * The task from T2 is stuck is the local queue
- */
- next = queue.poll() ?: return
- dispatch(next, true)
- }
-}
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt b/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt
index f3d66cd..350e3e9 100644
--- a/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt
+++ b/kotlinx-coroutines-core/jvm/src/scheduling/Dispatcher.kt
@@ -11,11 +11,12 @@
IDLE_WORKER_KEEP_ALIVE_NS, DEFAULT_SCHEDULER_NAME
) {
- @ExperimentalCoroutinesApi
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
parallelism.checkParallelism()
- if (parallelism >= CORE_POOL_SIZE) return this
- return super.limitedParallelism(parallelism)
+ if (parallelism >= CORE_POOL_SIZE) {
+ return namedOrThis(name)
+ }
+ return super.limitedParallelism(parallelism, name)
}
// Shuts down the dispatcher, used only by Dispatchers.shutdown()
@@ -43,11 +44,17 @@
DefaultScheduler.dispatchWithContext(block, BlockingContext, false)
}
- @ExperimentalCoroutinesApi
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
parallelism.checkParallelism()
- if (parallelism >= MAX_POOL_SIZE) return this
- return super.limitedParallelism(parallelism)
+ if (parallelism >= MAX_POOL_SIZE) {
+ return namedOrThis(name)
+ }
+ return super.limitedParallelism(parallelism, name)
+ }
+
+ // This name only leaks to user code as part of .limitedParallelism machinery
+ override fun toString(): String {
+ return "Dispatchers.IO"
}
}
@@ -66,10 +73,9 @@
override fun execute(command: java.lang.Runnable) = dispatch(EmptyCoroutineContext, command)
- @ExperimentalCoroutinesApi
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
// See documentation to Dispatchers.IO for the rationale
- return UnlimitedIoScheduler.limitedParallelism(parallelism)
+ return UnlimitedIoScheduler.limitedParallelism(parallelism, name)
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt b/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt
index eefccd5..bdf6335 100644
--- a/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt
+++ b/kotlinx-coroutines-core/jvm/src/scheduling/Tasks.kt
@@ -49,61 +49,48 @@
internal var schedulerTimeSource: SchedulerTimeSource = NanoTimeSource
/**
- * Marker indicating that task is CPU-bound and will not block
+ * Concurrency context of a task.
+ *
+ * Currently, it only signifies whether the task is blocking or non-blocking.
*/
-internal const val TASK_NON_BLOCKING = 0
+internal typealias TaskContext = Boolean
/**
- * Marker indicating that task may potentially block, thus giving scheduler a hint that additional thread may be required
+ * This would be [TaskContext.toString] if [TaskContext] was a proper class.
*/
-internal const val TASK_PROBABLY_BLOCKING = 1
+private fun taskContextString(taskContext: TaskContext): String = if (taskContext) "Blocking" else "Non-blocking"
-internal interface TaskContext {
- val taskMode: Int // TASK_XXX
- fun afterTask()
-}
+internal const val NonBlockingContext: TaskContext = false
-private class TaskContextImpl(override val taskMode: Int): TaskContext {
- override fun afterTask() {
- // Nothing for non-blocking context
- }
-}
+internal const val BlockingContext: TaskContext = true
-@JvmField
-internal val NonBlockingContext: TaskContext = TaskContextImpl(TASK_NON_BLOCKING)
-
-@JvmField
-internal val BlockingContext: TaskContext = TaskContextImpl(TASK_PROBABLY_BLOCKING)
-
-@PublishedApi
-internal abstract class Task internal constructor(
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
+/**
+ * A scheduler task.
+ */
+internal abstract class Task(
@JvmField var submissionTime: Long,
- // Used by the IDEA debugger via reflection and must be kept binary-compatible, see KTIJ-24102
- @JvmField internal var taskContext: TaskContext
+ @JvmField var taskContext: TaskContext
) : Runnable {
internal constructor() : this(0, NonBlockingContext)
- internal inline val mode: Int get() = taskContext.taskMode // TASK_XXX
}
-internal inline val Task.isBlocking get() = taskContext.taskMode == TASK_PROBABLY_BLOCKING
+internal inline val Task.isBlocking get() = taskContext
+
+internal fun Runnable.asTask(submissionTime: Long, taskContext: TaskContext): Task =
+ TaskImpl(this, submissionTime, taskContext)
// Non-reusable Task implementation to wrap Runnable instances that do not otherwise implement task
-internal class TaskImpl(
+private class TaskImpl(
@JvmField val block: Runnable,
submissionTime: Long,
taskContext: TaskContext
) : Task(submissionTime, taskContext) {
override fun run() {
- try {
- block.run()
- } finally {
- taskContext.afterTask()
- }
+ block.run()
}
override fun toString(): String =
- "Task[${block.classSimpleName}@${block.hexAddress}, $submissionTime, $taskContext]"
+ "Task[${block.classSimpleName}@${block.hexAddress}, $submissionTime, ${taskContextString(taskContext)}]"
}
// Open for tests
diff --git a/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt b/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt
index 9bd7e8c..32573ca 100644
--- a/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/DispatchersToStringTest.kt
@@ -1,5 +1,9 @@
+@file:OptIn(ExperimentalStdlibApi::class)
+
package kotlinx.coroutines
+import kotlinx.coroutines.scheduling.CORE_POOL_SIZE
+import kotlinx.coroutines.scheduling.MAX_POOL_SIZE
import kotlin.test.*
class DispatchersToStringTest {
@@ -11,4 +15,42 @@
assertEquals("Dispatchers.Main[missing]", Dispatchers.Main.toString())
assertEquals("Dispatchers.Main[missing]", Dispatchers.Main.immediate.toString())
}
+
+ @Test
+ fun testLimitedParallelism() {
+ for (parallelism in 1..100) {
+ assertEquals(
+ "Dispatchers.IO" + if (parallelism < MAX_POOL_SIZE) ".limitedParallelism($parallelism)" else "",
+ Dispatchers.IO.limitedParallelism(parallelism).toString()
+ )
+ assertEquals(
+ "Dispatchers.Default" + if (parallelism < CORE_POOL_SIZE) ".limitedParallelism($parallelism)" else "",
+ Dispatchers.Default.limitedParallelism(parallelism).toString()
+ )
+ }
+ // Not overridden at all, limited parallelism returns `this`
+ assertEquals("DefaultExecutor", (DefaultDelay as CoroutineDispatcher).limitedParallelism(42).toString())
+
+ assertEquals("filesDispatcher", Dispatchers.IO.limitedParallelism(1, "filesDispatcher").toString())
+ assertEquals("json", Dispatchers.Default.limitedParallelism(2, "json").toString())
+ assertEquals("\uD80C\uDE11", (DefaultDelay as CoroutineDispatcher).limitedParallelism(42, "\uD80C\uDE11").toString())
+ assertEquals("DefaultExecutor", (DefaultDelay as CoroutineDispatcher).limitedParallelism(42).toString())
+
+ val limitedNamed = Dispatchers.IO.limitedParallelism(10, "limited")
+ assertEquals("limited.limitedParallelism(2)", limitedNamed.limitedParallelism(2).toString())
+ assertEquals("2", limitedNamed.limitedParallelism(2, "2").toString())
+ // We asked for too many threads with no name, this was returned
+ assertEquals("limited", limitedNamed.limitedParallelism(12).toString())
+ assertEquals("12", limitedNamed.limitedParallelism(12, "12").toString())
+
+ runBlocking {
+ val d = coroutineContext[CoroutineDispatcher]!!
+ assertContains(d.toString(), "BlockingEventLoop")
+ val limited = d.limitedParallelism(2)
+ assertContains(limited.toString(), "BlockingEventLoop")
+ assertFalse(limited.toString().contains("limitedParallelism"))
+ val named = d.limitedParallelism(2, "Named")
+ assertEquals("Named", named.toString())
+ }
+ }
}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test/EventLoopsTest.kt b/kotlinx-coroutines-core/jvm/test/EventLoopsTest.kt
index 9ab52fd..551d197 100644
--- a/kotlinx-coroutines-core/jvm/test/EventLoopsTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/EventLoopsTest.kt
@@ -126,6 +126,21 @@
finish(4)
}
+ /**
+ * Tests that, when delayed tasks are due on an event loop, they will execute earlier than the newly-scheduled
+ * non-delayed tasks.
+ */
+ @Test
+ fun testPendingDelayedBeingDueEarlier() = runTest {
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ delay(1)
+ expect(1)
+ }
+ Thread.sleep(100)
+ yield()
+ finish(2)
+ }
+
class EventSync {
private val waitingThread = atomic<Thread?>(null)
private val fired = atomic(false)
diff --git a/kotlinx-coroutines-core/jvm/test/ExecutorAsCoroutineDispatcherDelayTest.kt b/kotlinx-coroutines-core/jvm/test/ExecutorAsCoroutineDispatcherDelayTest.kt
index 072ae28..819b05e 100644
--- a/kotlinx-coroutines-core/jvm/test/ExecutorAsCoroutineDispatcherDelayTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/ExecutorAsCoroutineDispatcherDelayTest.kt
@@ -38,4 +38,21 @@
executor.shutdown()
assertEquals(1, callsToSchedule)
}
+
+ @Test
+ fun testCancelling() = runTest {
+ val executor = STPE()
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ suspendCancellableCoroutine<Unit> { cont ->
+ expect(1)
+ (executor.asCoroutineDispatcher() as Delay).scheduleResumeAfterDelay(1_000_000, cont)
+ cont.cancel()
+ expect(2)
+ }
+ }
+ expect(3)
+ assertTrue(executor.getQueue().isEmpty())
+ executor.shutdown()
+ finish(4)
+ }
}
diff --git a/kotlinx-coroutines-core/jvm/test/JobChildStressTest.kt b/kotlinx-coroutines-core/jvm/test/JobChildStressTest.kt
index 3ac1967..a30e639 100644
--- a/kotlinx-coroutines-core/jvm/test/JobChildStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/JobChildStressTest.kt
@@ -1,26 +1,36 @@
package kotlinx.coroutines
import kotlinx.coroutines.testing.*
-import org.junit.*
-import org.junit.Test
import java.util.concurrent.*
+import java.util.concurrent.atomic.*
import kotlin.test.*
+/**
+ * Testing the procedure of attaching a child to the parent job.
+ */
class JobChildStressTest : TestBase() {
private val N_ITERATIONS = 10_000 * stressTestMultiplier
private val pool = newFixedThreadPoolContext(3, "JobChildStressTest")
- @After
+ @AfterTest
fun tearDown() {
pool.close()
}
/**
- * Perform concurrent launch of a child job & cancellation of the explicit parent job
+ * Tests attaching a child while the parent is trying to finalize its state.
+ *
+ * Checks the following interleavings:
+ * - A child attaches before the parent is cancelled.
+ * - A child attaches after the parent is cancelled, but before the parent notifies anyone about it.
+ * - A child attaches after the parent notifies the children about being cancelled,
+ * but before it starts waiting for its children.
+ * - A child attempts to attach after the parent stops waiting for its children,
+ * which immediately cancels the child.
*/
@Test
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
- fun testChild() = runTest {
+ fun testChildAttachmentRacingWithCancellation() = runTest {
val barrier = CyclicBarrier(3)
repeat(N_ITERATIONS) {
var wasLaunched = false
@@ -29,7 +39,7 @@
unhandledException = ex
}
val scope = CoroutineScope(pool + handler)
- val parent = CompletableDeferred<Unit>()
+ val parent = createCompletableDeferredForTesting(it)
// concurrent child launcher
val launcher = scope.launch {
barrier.await()
@@ -54,4 +64,44 @@
}
}
}
+
+ /**
+ * Tests attaching a child while the parent is waiting for the last child job to complete.
+ *
+ * Checks the following interleavings:
+ * - A child attaches while the parent is already completing, but is waiting for its children.
+ * - A child attempts to attach after the parent stops waiting for its children,
+ * which immediately cancels the child.
+ */
+ @Test
+ fun testChildAttachmentRacingWithLastChildCompletion() {
+ // All exceptions should get aggregated here
+ repeat(N_ITERATIONS) {
+ runBlocking {
+ val rogueJob = AtomicReference<Job?>()
+ /** not using [createCompletableDeferredForTesting] because we don't need extra children. */
+ val deferred = CompletableDeferred<Unit>()
+ // optionally, add a completion handler to the parent job, so that the child tries to enter a list with
+ // multiple elements, not just one.
+ if (it.mod(2) == 0) {
+ deferred.invokeOnCompletion { }
+ }
+ launch(pool + deferred) {
+ deferred.complete(Unit) // Transition deferred into "completing" state waiting for current child
+ // **Asynchronously** submit task that launches a child so it races with completion
+ pool.executor.execute {
+ rogueJob.set(launch(pool + deferred) {
+ throw TestException("isCancelled: ${coroutineContext.job.isCancelled}")
+ })
+ }
+ }
+
+ deferred.join()
+ val rogue = rogueJob.get()
+ if (rogue?.isActive == true) {
+ throw TestException("Rogue job $rogue with parent " + rogue.parent + " and children list: " + rogue.parent?.children?.toList())
+ }
+ }
+ }
+ }
}
diff --git a/kotlinx-coroutines-core/jvm/test/JobHandlersUpgradeStressTest.kt b/kotlinx-coroutines-core/jvm/test/JobHandlersUpgradeStressTest.kt
index dc2314b..3f085b6 100644
--- a/kotlinx-coroutines-core/jvm/test/JobHandlersUpgradeStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/JobHandlersUpgradeStressTest.kt
@@ -30,6 +30,9 @@
val state = atomic(0)
}
+ /**
+ * Tests handlers not being invoked more than once.
+ */
@Test
fun testStress() {
println("--- JobHandlersUpgradeStressTest")
@@ -91,4 +94,4 @@
println(" Fired handler ${fired.value} times")
}
-}
\ No newline at end of file
+}
diff --git a/kotlinx-coroutines-core/jvm/test/JobOnCompletionStressTest.kt b/kotlinx-coroutines-core/jvm/test/JobOnCompletionStressTest.kt
new file mode 100644
index 0000000..3df62b6
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/JobOnCompletionStressTest.kt
@@ -0,0 +1,192 @@
+package kotlinx.coroutines
+
+import kotlinx.coroutines.channels.*
+import kotlinx.coroutines.testing.*
+import java.util.concurrent.CyclicBarrier
+import java.util.concurrent.atomic.*
+import kotlin.test.*
+import kotlin.time.Duration.Companion.seconds
+
+class JobOnCompletionStressTest: TestBase() {
+ private val N_ITERATIONS = 10_000 * stressTestMultiplier
+ private val pool = newFixedThreadPoolContext(2, "JobOnCompletionStressTest")
+
+ private val completionHandlerSeesCompletedParent = AtomicBoolean(false)
+ private val completionHandlerSeesCancelledParent = AtomicBoolean(false)
+ private val encounteredException = AtomicReference<Throwable?>(null)
+
+ @AfterTest
+ fun tearDown() {
+ pool.close()
+ }
+
+ @Test
+ fun testOnCompletionRacingWithCompletion() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = false,
+ invokeImmediately = true,
+ parentCompletion = { complete(Unit) }
+ ) {
+ assertNull(encounteredException.get())
+ assertTrue(completionHandlerSeesCompletedParent.get())
+ assertFalse(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ @Test
+ fun testOnCompletionRacingWithCancellation() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = false,
+ invokeImmediately = true,
+ parentCompletion = { completeExceptionally(TestException()) }
+ ) {
+ assertIs<TestException>(encounteredException.get())
+ assertTrue(completionHandlerSeesCompletedParent.get())
+ assertTrue(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ @Test
+ fun testOnCancellingRacingWithCompletion() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = true,
+ invokeImmediately = true,
+ parentCompletion = { complete(Unit) }
+ ) {
+ assertNull(encounteredException.get())
+ assertTrue(completionHandlerSeesCompletedParent.get())
+ assertFalse(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ @Test
+ fun testOnCancellingRacingWithCancellation() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = true,
+ invokeImmediately = true,
+ parentCompletion = { completeExceptionally(TestException()) }
+ ) {
+ assertIs<TestException>(encounteredException.get())
+ assertTrue(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ @Test
+ fun testNonImmediateOnCompletionRacingWithCompletion() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = false,
+ invokeImmediately = false,
+ parentCompletion = { complete(Unit) }
+ ) {
+ assertNull(encounteredException.get())
+ assertTrue(completionHandlerSeesCompletedParent.get())
+ assertFalse(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ @Test
+ fun testNonImmediateOnCompletionRacingWithCancellation() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = false,
+ invokeImmediately = false,
+ parentCompletion = { completeExceptionally(TestException()) }
+ ) {
+ assertIs<TestException>(encounteredException.get())
+ assertTrue(completionHandlerSeesCompletedParent.get())
+ assertTrue(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ @Test
+ fun testNonImmediateOnCancellingRacingWithCompletion() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = true,
+ invokeImmediately = false,
+ parentCompletion = { complete(Unit) }
+ ) {
+ assertNull(encounteredException.get())
+ assertTrue(completionHandlerSeesCompletedParent.get())
+ assertFalse(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ @Test
+ fun testNonImmediateOnCancellingRacingWithCancellation() = runTest {
+ testHandlerRacingWithCancellation(
+ onCancelling = true,
+ invokeImmediately = false,
+ parentCompletion = { completeExceptionally(TestException()) }
+ ) {
+ assertIs<TestException>(encounteredException.get())
+ assertTrue(completionHandlerSeesCancelledParent.get())
+ }
+ }
+
+ private suspend fun testHandlerRacingWithCancellation(
+ onCancelling: Boolean,
+ invokeImmediately: Boolean,
+ parentCompletion: CompletableDeferred<Unit>.() -> Unit,
+ validate: () -> Unit,
+ ) {
+ repeat(N_ITERATIONS) {
+ val entered = Channel<Unit>(1)
+ completionHandlerSeesCompletedParent.set(false)
+ completionHandlerSeesCancelledParent.set(false)
+ encounteredException.set(null)
+ val parent = createCompletableDeferredForTesting(it)
+ val barrier = CyclicBarrier(2)
+ val handlerInstallJob = coroutineScope {
+ launch(pool) {
+ barrier.await()
+ parent.parentCompletion()
+ }
+ async(pool) {
+ barrier.await()
+ parent.invokeOnCompletion(
+ onCancelling = onCancelling,
+ invokeImmediately = invokeImmediately,
+ ) { exception ->
+ encounteredException.set(exception)
+ completionHandlerSeesCompletedParent.set(parent.isCompleted)
+ completionHandlerSeesCancelledParent.set(parent.isCancelled)
+ entered.trySend(Unit)
+ }
+ }
+ }
+ if (invokeImmediately || handlerInstallJob.getCompleted() !== NonDisposableHandle) {
+ withTimeout(1.seconds) {
+ entered.receive()
+ }
+ try {
+ validate()
+ } catch (e: Throwable) {
+ println("Iteration $it failed")
+ println("invokeOnCompletion returned ${handlerInstallJob.getCompleted()}")
+ throw e
+ }
+ } else {
+ assertTrue(entered.isEmpty)
+ }
+ }
+ }
+}
+
+/**
+ * Creates a [CompletableDeferred], optionally adding completion handlers and/or other children to the job depending
+ * on [iteration].
+ * The purpose is to test not just attaching completion handlers to empty or one-element lists (see the [JobSupport]
+ * implementation for details on what this means), but also to lists with multiple elements.
+ */
+fun createCompletableDeferredForTesting(iteration: Int): CompletableDeferred<Unit> {
+ val parent = CompletableDeferred<Unit>()
+ /* We optionally add completion handlers and/or other children to the parent job
+ to test the scenarios where a child is placed into an empty list, a single-element list,
+ or a list with multiple elements. */
+ if (iteration.mod(2) == 0) {
+ parent.invokeOnCompletion { }
+ }
+ if (iteration.mod(3) == 0) {
+ GlobalScope.launch(parent) { }
+ }
+ return parent
+}
diff --git a/kotlinx-coroutines-core/jvm/test/MemoryFootprintTest.kt b/kotlinx-coroutines-core/jvm/test/MemoryFootprintTest.kt
index 8f78a92..7a3b69f 100644
--- a/kotlinx-coroutines-core/jvm/test/MemoryFootprintTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/MemoryFootprintTest.kt
@@ -2,7 +2,7 @@
import kotlinx.coroutines.testing.*
import org.junit.Test
-import org.openjdk.jol.info.ClassLayout
+import org.openjdk.jol.info.*
import kotlin.test.*
@@ -12,6 +12,22 @@
fun testJobLayout() = assertLayout(Job().javaClass, 24)
@Test
+ fun testJobSize() {
+ assertTotalSize(jobWithChildren(1), 112)
+ assertTotalSize(jobWithChildren(2), 192) // + 80
+ assertTotalSize(jobWithChildren(3), 248) // + 56
+ assertTotalSize(jobWithChildren(4), 304) // + 56
+ }
+
+ private fun jobWithChildren(numberOfChildren: Int): Job {
+ val result = Job()
+ repeat(numberOfChildren) {
+ Job(result)
+ }
+ return result
+ }
+
+ @Test
fun testCancellableContinuationFootprint() = assertLayout(CancellableContinuationImpl::class.java, 48)
private fun assertLayout(clz: Class<*>, expectedSize: Int) {
@@ -19,4 +35,9 @@
// println(ClassLayout.parseClass(clz).toPrintable())
assertEquals(expectedSize.toLong(), size)
}
+
+ private fun assertTotalSize(instance: Job, expectedSize: Int) {
+ val size = GraphLayout.parseInstance(instance).totalSize()
+ assertEquals(expectedSize.toLong(), size)
+ }
}
diff --git a/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt b/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt
index dc096fe..3528702 100644
--- a/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/MutexCancellationStressTest.kt
@@ -55,7 +55,7 @@
delay(500)
// If we've caught the completion after delay, then there is a chance no progress were made whatsoever, bail out
if (completed.get()) return@launch
- val c = counterLocal.map { it.value }
+ val c = counterLocal.map { it.get() }
for (i in 0 until mutexJobNumber) {
assert(c[i] > lastCounterLocalSnapshot[i]) { "No progress in MutexJob-$i, last observed state: ${c[i]}" }
}
@@ -76,7 +76,7 @@
cancellationJob.join()
mutexJobs.forEach { it.join() }
checkProgressJob.join()
- assertEquals(counter, counterLocal.sumOf { it.value })
+ assertEquals(counter, counterLocal.sumOf { it.get() })
dispatcher.close()
}
}
diff --git a/kotlinx-coroutines-core/jvm/test/RejectedExecutionTest.kt b/kotlinx-coroutines-core/jvm/test/RejectedExecutionTest.kt
index 770a854..cf72b31 100644
--- a/kotlinx-coroutines-core/jvm/test/RejectedExecutionTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/RejectedExecutionTest.kt
@@ -87,7 +87,6 @@
@Test
fun testRejectOnDelay() = runTest {
- if (!removeFutureOnCancel(executor)) return@runTest // Skip this test on old JDKs
expect(1)
executor.acceptTasks = 1 // accept one task
assertFailsWith<CancellationException> {
@@ -109,7 +108,6 @@
@Test
fun testRejectWithTimeout() = runTest {
- if (!removeFutureOnCancel(executor)) return@runTest // Skip this test on old JDKs
expect(1)
executor.acceptTasks = 1 // accept one task
assertFailsWith<CancellationException> {
@@ -165,4 +163,4 @@
if (thread !is CoroutineScheduler.Worker) error("Not a thread from Dispatchers.IO: $thread")
assertEquals(CoroutineScheduler.WorkerState.BLOCKING, thread.state)
}
-}
\ No newline at end of file
+}
diff --git a/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt
index d9f455a..5e88521 100644
--- a/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt
@@ -210,6 +210,9 @@
for (value in channel) {
delay(1)
}
- FieldWalker.assertReachableCount(1, coroutineContext[Job]) { it is ChildContinuation }
+ FieldWalker.assertReachableCount(1, coroutineContext[Job]) {
+ // could be `it is ChildContinuation` if `ChildContinuation` wasn't private
+ it::class.simpleName == "ChildContinuation"
+ }
}
}
diff --git a/kotlinx-coroutines-core/jvm/test/TestSecurityManager.kt b/kotlinx-coroutines-core/jvm/test/TestSecurityManager.kt
deleted file mode 100644
index 781d5d0..0000000
--- a/kotlinx-coroutines-core/jvm/test/TestSecurityManager.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package kotlinx.coroutines
-
-import java.security.Permission
-
-@Suppress("unused")
-class TestSecurityManager : SecurityManager() {
- override fun checkPropertyAccess(key: String?) {
- if (key?.startsWith("kotlinx.") == true)
- throw SecurityException("'$key' property is not allowed")
- }
-
- override fun checkPermission(perm: Permission?) {
- /* allow everything else */
- }
-}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test/ThreadContextElementTest.kt b/kotlinx-coroutines-core/jvm/test/ThreadContextElementTest.kt
index 3b106c4..54e8867 100644
--- a/kotlinx-coroutines-core/jvm/test/ThreadContextElementTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/ThreadContextElementTest.kt
@@ -1,10 +1,13 @@
package kotlinx.coroutines
+import kotlinx.coroutines.flow.*
import kotlinx.coroutines.testing.*
import org.junit.Test
+import java.util.concurrent.CopyOnWriteArrayList
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
import kotlin.coroutines.*
import kotlin.test.*
-import kotlinx.coroutines.flow.*
class ThreadContextElementTest : TestBase() {
@@ -155,39 +158,81 @@
}
}
- class JobCaptor(val capturees: ArrayList<Job> = ArrayList()) : ThreadContextElement<Unit> {
+ class JobCaptor(val capturees: MutableList<String> = CopyOnWriteArrayList()) : ThreadContextElement<Unit> {
companion object Key : CoroutineContext.Key<MyElement>
override val key: CoroutineContext.Key<*> get() = Key
override fun updateThreadContext(context: CoroutineContext) {
- capturees.add(context.job)
+ capturees.add("Update: ${context.job}")
}
override fun restoreThreadContext(context: CoroutineContext, oldState: Unit) {
+ capturees.add("Restore: ${context.job}")
}
}
+ /**
+ * For stability of the test, it is important to make sure that
+ * the parent job actually suspends when calling
+ * `withContext(dispatcher2 + CoroutineName("dispatched"))`.
+ *
+ * Here this requirement is fulfilled by forcing execution on a single thread.
+ * However, dispatching is performed with two non-equal dispatchers to force dispatching.
+ *
+ * Suspend of the parent coroutine [kotlinx.coroutines.DispatchedCoroutine.trySuspend] is out of the control of the test,
+ * while being executed concurrently with resume of the child coroutine [kotlinx.coroutines.DispatchedCoroutine.tryResume].
+ */
@Test
fun testWithContextJobAccess() = runTest {
+ val executor = Executors.newSingleThreadExecutor()
+ // Emulate non-equal dispatchers
+ val executor1 = object : ExecutorService by executor {}
+ val executor2 = object : ExecutorService by executor {}
+ val dispatcher1 = executor1.asCoroutineDispatcher()
+ val dispatcher2 = executor2.asCoroutineDispatcher()
val captor = JobCaptor()
- val manuallyCaptured = ArrayList<Job>()
- runBlocking(captor) {
- manuallyCaptured += coroutineContext.job
- withContext(CoroutineName("undispatched")) {
- manuallyCaptured += coroutineContext.job
- withContext(Dispatchers.IO) {
- manuallyCaptured += coroutineContext.job
- }
- // Context restored, captured again
- manuallyCaptured += coroutineContext.job
- }
- // Context restored, captured again
- manuallyCaptured += coroutineContext.job
- }
+ val manuallyCaptured = mutableListOf<String>()
- assertEquals(manuallyCaptured, captor.capturees)
+ fun registerUpdate(job: Job?) = manuallyCaptured.add("Update: $job")
+ fun registerRestore(job: Job?) = manuallyCaptured.add("Restore: $job")
+
+ var rootJob: Job? = null
+ runBlocking(captor + dispatcher1) {
+ rootJob = coroutineContext.job
+ registerUpdate(rootJob)
+ var undispatchedJob: Job? = null
+ withContext(CoroutineName("undispatched")) {
+ undispatchedJob = coroutineContext.job
+ registerUpdate(undispatchedJob)
+ // These 2 restores and the corresponding next 2 updates happen only if the following `withContext`
+ // call actually suspends.
+ registerRestore(undispatchedJob)
+ registerRestore(rootJob)
+ // Without forcing of single backing thread the code inside `withContext`
+ // may already complete at the moment when the parent coroutine decides
+ // whether it needs to suspend or not.
+ var dispatchedJob: Job? = null
+ withContext(dispatcher2 + CoroutineName("dispatched")) {
+ dispatchedJob = coroutineContext.job
+ registerUpdate(dispatchedJob)
+ }
+ registerRestore(dispatchedJob)
+ // Context restored, captured again
+ registerUpdate(undispatchedJob)
+ }
+ registerRestore(undispatchedJob)
+ // Context restored, captured again
+ registerUpdate(rootJob)
+ }
+ registerRestore(rootJob)
+
+ // Restores may be called concurrently to the update calls in other threads, so their order is not checked.
+ val expected = manuallyCaptured.filter { it.startsWith("Update: ") }.joinToString(separator = "\n")
+ val actual = captor.capturees.filter { it.startsWith("Update: ") }.joinToString(separator = "\n")
+ assertEquals(expected, actual)
+ executor.shutdownNow()
}
@Test
diff --git a/kotlinx-coroutines-core/jvm/test/WithTimeoutChildDipspatchStressTest.kt b/kotlinx-coroutines-core/jvm/test/WithTimeoutChildDispatchStressTest.kt
similarity index 100%
rename from kotlinx-coroutines-core/jvm/test/WithTimeoutChildDipspatchStressTest.kt
rename to kotlinx-coroutines-core/jvm/test/WithTimeoutChildDispatchStressTest.kt
diff --git a/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelLeakTest.kt b/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelLeakTest.kt
index 7102636..7cd1487 100644
--- a/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelLeakTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelLeakTest.kt
@@ -4,6 +4,7 @@
import org.junit.Test
import kotlin.test.*
+@Suppress("DEPRECATION_ERROR")
class BroadcastChannelLeakTest : TestBase() {
@Test
fun testBufferedBroadcastChannelSubscriptionLeak() {
@@ -18,7 +19,7 @@
enum class TestKind { BROADCAST_CLOSE, SUB_CANCEL, BOTH }
private fun checkLeak(factory: () -> BroadcastChannel<String>) = runTest {
- for (kind in TestKind.values()) {
+ for (kind in TestKind.entries) {
val broadcast = factory()
val sub = broadcast.openSubscription()
broadcast.send("OK")
diff --git a/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt
index 5b21180..671f5d8 100644
--- a/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt
@@ -118,7 +118,7 @@
try {
val stop = doReceived(receiverIndex, channel.receive())
if (stop) break
- } catch (ex: ClosedReceiveChannelException) {
+ } catch (_: ClosedReceiveChannelException) {
break
}
}
@@ -144,7 +144,7 @@
val event = select<Long> { channel.onReceive { it } }
val stop = doReceived(receiverIndex, event)
if (stop) break
- } catch (ex: ClosedReceiveChannelException) {
+ } catch (_: ClosedReceiveChannelException) {
break
}
}
diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelMemoryLeakStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelMemoryLeakStressTest.kt
index 67170c2..4c239a3 100644
--- a/kotlinx-coroutines-core/jvm/test/channels/ChannelMemoryLeakStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelMemoryLeakStressTest.kt
@@ -1,8 +1,7 @@
-package channels
+package kotlinx.coroutines.channels
import kotlinx.coroutines.testing.*
import kotlinx.coroutines.*
-import kotlinx.coroutines.channels.*
import org.junit.Test
class ChannelMemoryLeakStressTest : TestBase() {
diff --git a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListLongStressTest.kt b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListLongStressTest.kt
index 577e73d..95be1cb 100644
--- a/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListLongStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListLongStressTest.kt
@@ -1,6 +1,5 @@
package kotlinx.coroutines.internal
-import kotlinx.coroutines.testing.*
import kotlinx.coroutines.testing.TestBase
import org.junit.Test
import java.util.*
@@ -31,7 +30,7 @@
for (j in 0 until nAddThreads)
threads += thread(start = false, name = "adder-$j") {
for (i in j until nAdded step nAddThreads) {
- list.addLast(IntNode(i))
+ list.addLast(IntNode(i), Int.MAX_VALUE)
}
println("${Thread.currentThread().name} completed")
workingAdders.decrementAndGet()
@@ -41,8 +40,8 @@
val rnd = Random()
do {
val lastTurn = workingAdders.get() == 0
- list.forEach<IntNode> { node ->
- if (shallRemove(node.i) && (lastTurn || rnd.nextDouble() < removeProbability))
+ list.forEach { node ->
+ if (node is IntNode && shallRemove(node.i) && (lastTurn || rnd.nextDouble() < removeProbability))
node.remove()
}
} while (!lastTurn)
@@ -62,8 +61,8 @@
if (!shallRemove(i))
yield(i)
}
- list.forEach<IntNode> { node ->
- require(node.i == expected.next())
+ list.forEach { node ->
+ require(node !is IntNode || node.i == expected.next())
}
require(!expected.hasNext())
}
diff --git a/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureTest.kt b/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureTest.kt
index 0b9f83b..34534c8 100644
--- a/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/jdk8/future/FutureTest.kt
@@ -595,4 +595,20 @@
GlobalScope.future<Unit>(start = CoroutineStart.LAZY) { }
}
}
+
+ @Test
+ fun testStackOverflowOnExceptionalCompletion() = runTest {
+ val future = CompletableFuture<Unit>()
+ val didRun = AtomicBoolean(false)
+ future.whenComplete { _, _ -> didRun.set(true) }
+ val deferreds = List(100000) { future.asDeferred() }
+ future.completeExceptionally(TestException())
+ deferreds.forEach {
+ assertTrue(it.isCompleted)
+ val exception = it.getCompletionExceptionOrNull()
+ assertIs<TestException>(exception)
+ assertTrue(exception.suppressedExceptions.isEmpty())
+ }
+ assertTrue(didRun.get())
+ }
}
diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt
index 26a58c3..96a8d13 100644
--- a/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt
@@ -54,6 +54,7 @@
sequentialSpecification = SequentialConflatedChannel::class.java,
obstructionFree = false
)
+@Suppress("DEPRECATION_ERROR")
class ConflatedBroadcastChannelLincheckTest : ChannelLincheckTestBaseAll(
c = ChannelViaBroadcast(ConflatedBroadcastChannel()),
sequentialSpecification = SequentialConflatedChannel::class.java,
diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt
index 12b44d5..e99e8a1 100644
--- a/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt
@@ -9,7 +9,7 @@
import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.*
abstract class SemaphoreLincheckTestBase(permits: Int) : AbstractLincheckTest() {
- private val semaphore = SemaphoreImpl(permits = permits, acquiredPermits = 0)
+ private val semaphore = SemaphoreAndMutexImpl(permits = permits, acquiredPermits = 0)
@Operation
fun tryAcquire() = semaphore.tryAcquire()
diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTerminationStressTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTerminationStressTest.kt
index 2b00eb7..f9e93ab 100644
--- a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTerminationStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherTerminationStressTest.kt
@@ -1,7 +1,7 @@
-package kotlinx.coroutines
+package kotlinx.coroutines.scheduling
import kotlinx.coroutines.testing.*
-import kotlinx.coroutines.scheduling.*
+import kotlinx.coroutines.*
import org.junit.*
import java.util.*
import java.util.concurrent.*
diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt
index 8029db6..29d8ed9 100644
--- a/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/scheduling/BlockingCoroutineDispatcherThreadLimitStressTest.kt
@@ -14,7 +14,7 @@
corePoolSize = CORES_COUNT
}
- private val observedConcurrency = ConcurrentHashMap<Int, Boolean>()
+ private val observedParallelism = ConcurrentHashMap<Int, Boolean>().keySet(true)
private val concurrentWorkers = AtomicInteger(0)
@Test
@@ -27,23 +27,18 @@
async(limitingDispatcher) {
try {
val currentlyExecuting = concurrentWorkers.incrementAndGet()
- observedConcurrency[currentlyExecuting] = true
- assertTrue(currentlyExecuting <= CORES_COUNT)
+ observedParallelism.add(currentlyExecuting)
} finally {
concurrentWorkers.decrementAndGet()
}
}
}
- tasks.forEach { it.await() }
- for (i in CORES_COUNT + 1..CORES_COUNT * 2) {
- require(i !in observedConcurrency.keys) { "Unexpected state: $observedConcurrency" }
- }
- checkPoolThreadsCreated(0..CORES_COUNT + 1)
+ tasks.awaitAll()
+ assertEquals(1, observedParallelism.single(), "Expected parallelism should be 1, had $observedParallelism")
}
}
@Test
- @Ignore
fun testLimitParallelism() = runBlocking {
val limitingDispatcher = blockingDispatcher(CORES_COUNT)
val iterations = 50_000 * stressTestMultiplier
@@ -51,17 +46,13 @@
async(limitingDispatcher) {
try {
val currentlyExecuting = concurrentWorkers.incrementAndGet()
- observedConcurrency[currentlyExecuting] = true
- assertTrue(currentlyExecuting <= CORES_COUNT)
+ observedParallelism.add(currentlyExecuting)
} finally {
concurrentWorkers.decrementAndGet()
}
}
}
- tasks.forEach { it.await() }
- for (i in CORES_COUNT + 1..CORES_COUNT * 2) {
- require(i !in observedConcurrency.keys) { "Unexpected state: $observedConcurrency" }
- }
- checkPoolThreadsCreated(CORES_COUNT..CORES_COUNT * 3)
+ tasks.awaitAll()
+ assertTrue(observedParallelism.max() <= CORES_COUNT, "Unexpected state: $observedParallelism")
}
}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt
index edb3799..042ea2f 100644
--- a/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/scheduling/CoroutineSchedulerTest.kt
@@ -9,16 +9,16 @@
import kotlin.test.*
class CoroutineSchedulerTest : TestBase() {
- private val taskModes = listOf(TASK_NON_BLOCKING, TASK_PROBABLY_BLOCKING)
+ private val contexts = listOf(NonBlockingContext, BlockingContext)
@Test
fun testModesExternalSubmission() { // Smoke
CoroutineScheduler(1, 1).use {
- for (mode in taskModes) {
+ for (context in contexts) {
val latch = CountDownLatch(1)
it.dispatch(Runnable {
latch.countDown()
- }, TaskContextImpl(mode))
+ }, context)
latch.await()
}
@@ -28,12 +28,12 @@
@Test
fun testModesInternalSubmission() { // Smoke
CoroutineScheduler(2, 2).use {
- val latch = CountDownLatch(taskModes.size)
+ val latch = CountDownLatch(contexts.size)
it.dispatch(Runnable {
- for (mode in taskModes) {
+ for (context in contexts) {
it.dispatch(Runnable {
latch.countDown()
- }, TaskContextImpl(mode))
+ }, context)
}
})
@@ -164,8 +164,4 @@
check(ratio >= 0.9)
}
}
-
- private class TaskContextImpl(override val taskMode: Int) : TaskContext {
- override fun afterTask() {}
- }
}
diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt b/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt
index f6e0f70..33e3283 100644
--- a/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt
+++ b/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt
@@ -95,6 +95,12 @@
}
}
+/**
+ * Implementation note:
+ * Our [Dispatcher.IO] is a [limitedParallelism][CoroutineDispatcher.limitedParallelism] dispatcher
+ * on top of unbounded scheduler. We want to test this scenario, but on top of non-singleton
+ * scheduler so we can control the number of threads, thus this method.
+ */
internal fun SchedulerCoroutineDispatcher.blocking(parallelism: Int = 16): CoroutineDispatcher {
return object : CoroutineDispatcher() {
diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/WorkQueueTest.kt b/kotlinx-coroutines-core/jvm/test/scheduling/WorkQueueTest.kt
index e9fbc6b..08ed5ca 100644
--- a/kotlinx-coroutines-core/jvm/test/scheduling/WorkQueueTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/scheduling/WorkQueueTest.kt
@@ -78,8 +78,8 @@
}
}
-internal fun task(n: Long) = TaskImpl(Runnable {}, n, NonBlockingContext)
-internal fun blockingTask(n: Long) = TaskImpl(Runnable {}, n, BlockingContext)
+internal fun task(n: Long) = Runnable {}.asTask(n, NonBlockingContext)
+internal fun blockingTask(n: Long) = Runnable {}.asTask(n, BlockingContext)
internal fun WorkQueue.drain(ref: ObjectRef<Task?>): List<Long> {
var task: Task? = poll()
diff --git a/kotlinx-coroutines-core/native/src/Builders.kt b/kotlinx-coroutines-core/native/src/Builders.kt
index 0464747..9c77b2c 100644
--- a/kotlinx-coroutines-core/native/src/Builders.kt
+++ b/kotlinx-coroutines-core/native/src/Builders.kt
@@ -7,7 +7,7 @@
import kotlin.native.concurrent.*
/**
- * Runs new coroutine and **blocks** current thread _interruptibly_ until its completion.
+ * Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
*
* It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in
* `main` functions and in tests.
@@ -25,21 +25,21 @@
* Here, instead of releasing the thread on which `loadConfiguration` runs if `fetchConfigurationData` suspends, it will
* block, potentially leading to thread starvation issues.
*
- * The default [CoroutineDispatcher] for this builder in an implementation of [EventLoop] that processes continuations
+ * The default [CoroutineDispatcher] for this builder is an internal implementation of event loop that processes continuations
* in this blocked thread until the completion of this coroutine.
* See [CoroutineDispatcher] for the other implementations that are provided by `kotlinx.coroutines`.
*
* When [CoroutineDispatcher] is explicitly specified in the [context], then the new coroutine runs in the context of
- * the specified dispatcher while the current thread is blocked. If the specified dispatcher implements [EventLoop]
- * interface and this `runBlocking` invocation is performed from inside of the this event loop's thread, then
- * this event loop is processed using its [processNextEvent][EventLoop.processNextEvent] method until coroutine completes.
+ * the specified dispatcher while the current thread is blocked. If the specified dispatcher is an event loop of another `runBlocking`,
+ * then this invocation uses the outer event loop.
*
* If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and
* this `runBlocking` invocation throws [InterruptedException].
*
- * See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
+ * See [newCoroutineContext][CoroutineScope.newCoroutineContext] for a description of debugging facilities that are available
+ * for a newly created coroutine.
*
- * @param context context of the coroutine. The default value is an implementation of [EventLoop].
+ * @param context the context of the coroutine. The default value is an event loop on the current thread.
* @param block the coroutine code.
*/
public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
diff --git a/kotlinx-coroutines-core/native/src/CloseableCoroutineDispatcher.kt b/kotlinx-coroutines-core/native/src/CloseableCoroutineDispatcher.kt
index 81e0c5d..3ea73ad 100644
--- a/kotlinx-coroutines-core/native/src/CloseableCoroutineDispatcher.kt
+++ b/kotlinx-coroutines-core/native/src/CloseableCoroutineDispatcher.kt
@@ -1,5 +1,5 @@
package kotlinx.coroutines
-public actual abstract class CloseableCoroutineDispatcher actual constructor() : CoroutineDispatcher() {
- public actual abstract fun close()
+public actual abstract class CloseableCoroutineDispatcher actual constructor() : CoroutineDispatcher(), AutoCloseable {
+ public actual abstract override fun close()
}
diff --git a/kotlinx-coroutines-core/native/src/CoroutineContext.kt b/kotlinx-coroutines-core/native/src/CoroutineContext.kt
index 70064e2..3f4c8d9 100644
--- a/kotlinx-coroutines-core/native/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/native/src/CoroutineContext.kt
@@ -31,8 +31,8 @@
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
val combined = coroutineContext + context
- return if (combined !== DefaultDelay && combined[ContinuationInterceptor] == null)
- combined + (DefaultDelay as CoroutineContext.Element) else combined
+ return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
+ combined + Dispatchers.Default else combined
}
public actual fun CoroutineContext.newCoroutineContext(addedContext: CoroutineContext): CoroutineContext {
diff --git a/kotlinx-coroutines-core/native/src/Dispatchers.kt b/kotlinx-coroutines-core/native/src/Dispatchers.kt
index 9965186..e66c05f 100644
--- a/kotlinx-coroutines-core/native/src/Dispatchers.kt
+++ b/kotlinx-coroutines-core/native/src/Dispatchers.kt
@@ -27,10 +27,9 @@
private val unlimitedPool = newFixedThreadPoolContext(2048, "Dispatchers.IO")
private val io = unlimitedPool.limitedParallelism(64) // Default JVM size
- @ExperimentalCoroutinesApi
- override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
// See documentation to Dispatchers.IO for the rationale
- return unlimitedPool.limitedParallelism(parallelism)
+ return unlimitedPool.limitedParallelism(parallelism, name)
}
override fun dispatch(context: CoroutineContext, block: Runnable) {
diff --git a/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt b/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt
index 283fce0..7968ffd 100644
--- a/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt
+++ b/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt
@@ -11,6 +11,7 @@
import kotlin.time.*
import kotlin.time.Duration.Companion.milliseconds
+@DelicateCoroutinesApi
public actual fun newFixedThreadPoolContext(nThreads: Int, name: String): CloseableCoroutineDispatcher {
require(nThreads >= 1) { "Expected at least one thread, but got: $nThreads" }
return MultiWorkerDispatcher(name, nThreads)
@@ -76,7 +77,7 @@
private class MultiWorkerDispatcher(
private val name: String,
- workersCount: Int
+ private val workersCount: Int
) : CloseableCoroutineDispatcher() {
private val tasksQueue = Channel<Runnable>(Channel.UNLIMITED)
private val availableWorkers = Channel<CancellableContinuation<Runnable>>(Channel.UNLIMITED)
@@ -139,6 +140,14 @@
}
}
+ override fun limitedParallelism(parallelism: Int, name: String?): CoroutineDispatcher {
+ parallelism.checkParallelism()
+ if (parallelism >= workersCount) {
+ return namedOrThis(name)
+ }
+ return super.limitedParallelism(parallelism, name)
+ }
+
override fun close() {
tasksAndWorkersCounter.getAndUpdate { if (it.isClosed()) it else it or 1L }
val workers = workerPool.close() // no new workers will be created
diff --git a/kotlinx-coroutines-core/native/src/SchedulerTask.kt b/kotlinx-coroutines-core/native/src/SchedulerTask.kt
index 229302b..24b2311 100644
--- a/kotlinx-coroutines-core/native/src/SchedulerTask.kt
+++ b/kotlinx-coroutines-core/native/src/SchedulerTask.kt
@@ -1,12 +1,3 @@
package kotlinx.coroutines
internal actual abstract class SchedulerTask : Runnable
-
-internal actual interface SchedulerTaskContext { }
-
-private object TaskContext: SchedulerTaskContext { }
-
-internal actual val SchedulerTask.taskContext: SchedulerTaskContext get() = TaskContext
-
-@Suppress("NOTHING_TO_INLINE")
-internal actual inline fun SchedulerTaskContext.afterTask() {}
diff --git a/kotlinx-coroutines-core/native/src/internal/Concurrent.kt b/kotlinx-coroutines-core/native/src/internal/Concurrent.kt
index 8423c61..fcfe624 100644
--- a/kotlinx-coroutines-core/native/src/internal/Concurrent.kt
+++ b/kotlinx-coroutines-core/native/src/internal/Concurrent.kt
@@ -14,17 +14,17 @@
@Suppress("ACTUAL_WITHOUT_EXPECT") // This suppress can be removed in 2.0: KT-59355
internal actual typealias BenignDataRace = kotlin.concurrent.Volatile
-internal actual class WorkaroundAtomicReference<T> actual constructor(value: T) {
+internal actual class WorkaroundAtomicReference<V> actual constructor(value: V) {
- private val nativeAtomic = kotlin.concurrent.AtomicReference<T>(value)
+ private val nativeAtomic = kotlin.concurrent.AtomicReference<V>(value)
- public actual fun get(): T = nativeAtomic.value
+ public actual fun get(): V= nativeAtomic.value
- public actual fun set(value: T) {
+ public actual fun set(value: V) {
nativeAtomic.value = value
}
- public actual fun getAndSet(value: T): T = nativeAtomic.getAndSet(value)
+ public actual fun getAndSet(value: V): V = nativeAtomic.getAndSet(value)
- public actual fun compareAndSet(expected: T, value: T): Boolean = nativeAtomic.compareAndSet(expected, value)
+ public actual fun compareAndSet(expected: V, value: V): Boolean = nativeAtomic.compareAndSet(expected, value)
}
diff --git a/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt b/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt
index 00581f1..7afce8f 100644
--- a/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt
+++ b/kotlinx-coroutines-core/native/src/internal/ProbesSupport.kt
@@ -4,3 +4,6 @@
@Suppress("NOTHING_TO_INLINE")
internal actual inline fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> = completion
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun <T> probeCoroutineResumed(completion: Continuation<T>) { }
diff --git a/kotlinx-coroutines-core/npm/README.md b/kotlinx-coroutines-core/npm/README.md
deleted file mode 100644
index 868fb65..0000000
--- a/kotlinx-coroutines-core/npm/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# kotlinx.coroutines
-
-Library support for Kotlin coroutines in
-[Kotlin/JS](https://kotlinlang.org/docs/reference/js-overview.html).
-
-```kotlin
-suspend fun main() = coroutineScope {
- launch {
- delay(1000)
- println("Kotlin Coroutines World!")
- }
- println("Hello")
-}
-```
-
-## Documentation
-
-* [Guide to kotlinx.coroutines by example on JVM](https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html) (**read it first**)
-* [Full kotlinx.coroutines API reference](https://kotlinlang.org/api/kotlinx.coroutines/)
diff --git a/kotlinx-coroutines-core/npm/package.json b/kotlinx-coroutines-core/npm/package.json
deleted file mode 100644
index 5dda394..0000000
--- a/kotlinx-coroutines-core/npm/package.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "name": "kotlinx-coroutines-core",
- "version" : "$version",
- "description" : "Library support for Kotlin coroutines",
- "main" : "kotlinx-coroutines-core.js",
- "author": "JetBrains",
- "license": "Apache-2.0",
- "homepage": "https://github.com/Kotlin/kotlinx.coroutines",
- "bugs": {
- "url": "https://github.com/Kotlin/kotlinx.coroutines/issues"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/Kotlin/kotlinx.coroutines.git"
- },
- "keywords": [
- "Kotlin",
- "async",
- "coroutines",
- "JavaScript",
- "JetBrains"
- ],
- "peerDependencies": {
- $kotlinDependency
- }
-}
diff --git a/kotlinx-coroutines-core/wasmJs/src/CoroutineContext.kt b/kotlinx-coroutines-core/wasmJs/src/CoroutineContext.kt
index f4db72a..8e41274 100644
--- a/kotlinx-coroutines-core/wasmJs/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/wasmJs/src/CoroutineContext.kt
@@ -1,9 +1,5 @@
package kotlinx.coroutines
-import kotlinx.coroutines.internal.*
-import org.w3c.dom.*
-import kotlin.coroutines.*
-
internal external interface JsProcess : JsAny {
fun nextTick(handler: () -> Unit)
}
@@ -11,37 +7,10 @@
internal fun tryGetProcess(): JsProcess? =
js("(typeof(process) !== 'undefined' && typeof(process.nextTick) === 'function') ? process : null")
-internal fun tryGetWindow(): Window? =
+internal fun tryGetWindow(): W3CWindow? =
js("(typeof(window) !== 'undefined' && window != null && typeof(window.addEventListener) === 'function') ? window : null")
internal actual fun createDefaultDispatcher(): CoroutineDispatcher =
tryGetProcess()?.let(::NodeDispatcher)
?: tryGetWindow()?.let(::WindowDispatcher)
?: SetTimeoutDispatcher
-
-@PublishedApi // Used from kotlinx-coroutines-test via suppress, not part of ABI
-internal actual val DefaultDelay: Delay
- get() = Dispatchers.Default as Delay
-
-public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
- val combined = coroutineContext + context
- return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
- combined + Dispatchers.Default else combined
-}
-
-public actual fun CoroutineContext.newCoroutineContext(addedContext: CoroutineContext): CoroutineContext {
- return this + addedContext
-}
-
-// No debugging facilities on Wasm
-internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T = block()
-internal actual inline fun <T> withContinuationContext(continuation: Continuation<*>, countOrElement: Any?, block: () -> T): T = block()
-internal actual fun Continuation<*>.toDebugString(): String = toString()
-internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on Wasm
-
-internal actual class UndispatchedCoroutine<in T> actual constructor(
- context: CoroutineContext,
- uCont: Continuation<T>
-) : ScopeCoroutine<T>(context, uCont) {
- override fun afterResume(state: Any?) = uCont.resumeWith(recoverResult(state, uCont))
-}
diff --git a/kotlinx-coroutines-core/wasmJs/src/JSDispatcher.kt b/kotlinx-coroutines-core/wasmJs/src/JSDispatcher.kt
index 9481e2f..d5a9519 100644
--- a/kotlinx-coroutines-core/wasmJs/src/JSDispatcher.kt
+++ b/kotlinx-coroutines-core/wasmJs/src/JSDispatcher.kt
@@ -1,9 +1,10 @@
package kotlinx.coroutines
-import org.w3c.dom.Window
import kotlin.js.*
-public actual typealias W3CWindow = Window
+internal actual abstract external class W3CWindow {
+ fun clearTimeout(handle: Int)
+}
internal actual fun w3cSetTimeout(window: W3CWindow, handler: () -> Unit, timeout: Int): Int =
setTimeout(window, handler, timeout)
@@ -40,7 +41,7 @@
}
@Suppress("UNUSED_PARAMETER")
-private fun subscribeToWindowMessages(window: Window, process: () -> Unit): Unit = js("""{
+private fun subscribeToWindowMessages(window: W3CWindow, process: () -> Unit): Unit = js("""{
const handler = (event) => {
if (event.source == window && event.data == 'dispatchCoroutine') {
event.stopPropagation();
@@ -51,7 +52,7 @@
}""")
@Suppress("UNUSED_PARAMETER")
-private fun createRescheduleMessagePoster(window: Window): () -> Unit =
+private fun createRescheduleMessagePoster(window: W3CWindow): () -> Unit =
js("() => window.postMessage('dispatchCoroutine', '*')")
@Suppress("UNUSED_PARAMETER")
@@ -84,5 +85,6 @@
js("{ if (typeof clearTimeout !== 'undefined') clearTimeout(handle); }")
@Suppress("UNUSED_PARAMETER")
-private fun setTimeout(window: Window, handler: () -> Unit, timeout: Int): Int =
+private fun setTimeout(window: W3CWindow, handler: () -> Unit, timeout: Int): Int =
js("window.setTimeout(handler, timeout)")
+
diff --git a/kotlinx-coroutines-core/wasmWasi/src/Debug.kt b/kotlinx-coroutines-core/wasmWasi/src/Debug.kt
new file mode 100644
index 0000000..740265a
--- /dev/null
+++ b/kotlinx-coroutines-core/wasmWasi/src/Debug.kt
@@ -0,0 +1,10 @@
+package kotlinx.coroutines
+
+internal actual val DEBUG: Boolean = false
+
+internal actual val Any.hexAddress: String
+ get() = this.hashCode().toString()
+
+internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown"
+
+internal actual inline fun assert(value: () -> Boolean) {}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/wasmWasi/src/EventLoop.kt b/kotlinx-coroutines-core/wasmWasi/src/EventLoop.kt
new file mode 100644
index 0000000..aac143d
--- /dev/null
+++ b/kotlinx-coroutines-core/wasmWasi/src/EventLoop.kt
@@ -0,0 +1,120 @@
+@file:OptIn(UnsafeWasmMemoryApi::class)
+package kotlinx.coroutines
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.wasm.*
+import kotlin.wasm.unsafe.*
+
+@WasmImport("wasi_snapshot_preview1", "poll_oneoff")
+private external fun wasiPollOneOff(ptrToSubscription: Int, eventPtr: Int, nsubscriptions: Int, resultPtr: Int): Int
+
+@WasmImport("wasi_snapshot_preview1", "clock_time_get")
+private external fun wasiRawClockTimeGet(clockId: Int, precision: Long, resultPtr: Int): Int
+
+private const val CLOCKID_MONOTONIC = 1
+
+internal actual fun createEventLoop(): EventLoop = DefaultExecutor
+
+internal actual fun nanoTime(): Long = withScopedMemoryAllocator { allocator: MemoryAllocator ->
+ val ptrTo8Bytes = allocator.allocate(8)
+ val returnCode = wasiRawClockTimeGet(
+ clockId = CLOCKID_MONOTONIC,
+ precision = 1,
+ resultPtr = ptrTo8Bytes.address.toInt()
+ )
+ check(returnCode == 0) { "clock_time_get failed with the return code $returnCode" }
+ ptrTo8Bytes.loadLong()
+}
+
+private fun sleep(nanos: Long, ptrTo32Bytes: Pointer, ptrTo8Bytes: Pointer, ptrToSubscription: Pointer) {
+ //__wasi_timestamp_t timeout;
+ (ptrToSubscription + 24).storeLong(nanos)
+ val returnCode = wasiPollOneOff(
+ ptrToSubscription = ptrToSubscription.address.toInt(),
+ eventPtr = ptrTo32Bytes.address.toInt(),
+ nsubscriptions = 1,
+ resultPtr = ptrTo8Bytes.address.toInt()
+ )
+ check(returnCode == 0) { "poll_oneoff failed with the return code $returnCode" }
+}
+
+internal actual object DefaultExecutor : EventLoopImplBase() {
+ override fun shutdown() {
+ // don't do anything: on WASI, the event loop is the default executor, we can't shut it down
+ }
+
+ override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle =
+ scheduleInvokeOnTimeout(timeMillis, block)
+
+ actual override fun enqueue(task: Runnable) {
+ if (kotlin.wasm.internal.onExportedFunctionExit == null) {
+ kotlin.wasm.internal.onExportedFunctionExit = ::runEventLoop
+ }
+ super.enqueue(task)
+ }
+}
+
+internal actual abstract class EventLoopImplPlatform : EventLoop() {
+ protected actual fun unpark() {
+ // do nothing: in WASI, no external callbacks can be invoked while `poll_oneoff` is running,
+ // so it is both impossible and unnecessary to unpark the event loop
+ }
+
+ protected actual fun reschedule(now: Long, delayedTask: EventLoopImplBase.DelayedTask) {
+ // throw; on WASI, the event loop is the default executor, we can't shut it down or reschedule tasks
+ // to anyone else
+ throw UnsupportedOperationException("runBlocking event loop is not supported")
+ }
+}
+
+internal actual inline fun platformAutoreleasePool(crossinline block: () -> Unit) = block()
+
+internal fun runEventLoop() {
+ withScopedMemoryAllocator { allocator ->
+ val ptrToSubscription = initializeSubscriptionPtr(allocator)
+ val ptrTo32Bytes = allocator.allocate(32)
+ val ptrTo8Bytes = allocator.allocate(8)
+ val eventLoop = DefaultExecutor
+ eventLoop.incrementUseCount()
+ try {
+ while (true) {
+ val parkNanos = eventLoop.processNextEvent()
+ if (parkNanos == Long.MAX_VALUE) {
+ // no more events
+ break
+ }
+ if (parkNanos > 0) {
+ // sleep until the next event
+ sleep(
+ parkNanos,
+ ptrTo32Bytes = ptrTo32Bytes,
+ ptrTo8Bytes = ptrTo8Bytes,
+ ptrToSubscription = ptrToSubscription
+ )
+ }
+ }
+ } finally { // paranoia
+ eventLoop.decrementUseCount()
+ }
+ }
+}
+
+private fun initializeSubscriptionPtr(allocator: MemoryAllocator): Pointer {
+ val ptrToSubscription = allocator.allocate(48)
+ //userdata
+ ptrToSubscription.storeLong(0)
+ //uint8_t tag;
+ (ptrToSubscription + 8).storeByte(0) //EVENTTYPE_CLOCK
+ //__wasi_clockid_t id;
+ (ptrToSubscription + 16).storeInt(CLOCKID_MONOTONIC) //CLOCKID_MONOTONIC
+ //__wasi_timestamp_t timeout;
+ //(ptrToSubscription + 24).storeLong(timeout)
+ //__wasi_timestamp_t precision;
+ (ptrToSubscription + 32).storeLong(0)
+ //__wasi_subclockflags_t
+ (ptrToSubscription + 40).storeShort(0) //ABSOLUTE_TIME=1/RELATIVE=0
+
+ return ptrToSubscription
+}
+
+internal actual fun createDefaultDispatcher(): CoroutineDispatcher = DefaultExecutor
diff --git a/kotlinx-coroutines-core/wasmWasi/src/internal/CoroutineExceptionHandlerImpl.kt b/kotlinx-coroutines-core/wasmWasi/src/internal/CoroutineExceptionHandlerImpl.kt
new file mode 100644
index 0000000..ba75a7f
--- /dev/null
+++ b/kotlinx-coroutines-core/wasmWasi/src/internal/CoroutineExceptionHandlerImpl.kt
@@ -0,0 +1,57 @@
+@file:OptIn(UnsafeWasmMemoryApi::class)
+
+package kotlinx.coroutines.internal
+
+import kotlin.wasm.WasmImport
+import kotlin.wasm.unsafe.UnsafeWasmMemoryApi
+import kotlin.wasm.unsafe.withScopedMemoryAllocator
+
+private const val STDERR = 2
+
+/**
+ * Write to a file descriptor. Note: This is similar to `writev` in POSIX.
+ */
+@WasmImport("wasi_snapshot_preview1", "fd_write")
+private external fun wasiRawFdWrite(descriptor: Int, scatterPtr: Int, scatterSize: Int, errorPtr: Int): Int
+
+@OptIn(UnsafeWasmMemoryApi::class)
+private fun printlnErrorStream(message: String): Int = withScopedMemoryAllocator { allocator ->
+ val data = message.encodeToByteArray()
+ val dataSize = data.size
+ val memorySize = dataSize + 1
+
+ val ptr = allocator.allocate(memorySize)
+ var currentPtr = ptr
+ for (el in data) {
+ currentPtr.storeByte(el)
+ currentPtr += 1
+ }
+ (ptr + dataSize).storeByte(0x0A)
+
+ val scatterPtr = allocator.allocate(8)
+ (scatterPtr + 0).storeInt(ptr.address.toInt())
+ (scatterPtr + 4).storeInt(memorySize)
+
+ val rp0 = allocator.allocate(4)
+
+ val ret = wasiRawFdWrite(
+ descriptor = STDERR,
+ scatterPtr = scatterPtr.address.toInt(),
+ scatterSize = 1,
+ errorPtr = rp0.address.toInt()
+ )
+
+ if (ret != 0) rp0.loadInt() else 0
+}
+
+/*
+* Terminate the process normally with an exit code.
+ */
+@WasmImport("wasi_snapshot_preview1", "proc_exit")
+private external fun wasiProcExit(exitCode: Int)
+
+internal actual fun propagateExceptionFinalResort(exception: Throwable) {
+ val errorCode = printlnErrorStream("!!!")
+ val returnCode = if (errorCode != 0) errorCode else 1
+ wasiProcExit(returnCode)
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/wasmWasi/src/internal/CoroutineRunner.kt b/kotlinx-coroutines-core/wasmWasi/src/internal/CoroutineRunner.kt
new file mode 100644
index 0000000..081ce0e
--- /dev/null
+++ b/kotlinx-coroutines-core/wasmWasi/src/internal/CoroutineRunner.kt
@@ -0,0 +1,15 @@
+package kotlinx.coroutines.internal
+
+import kotlinx.coroutines.*
+import kotlin.coroutines.*
+
+/** @suppress **This is internal API and it is subject to change.** */
+@InternalCoroutinesApi
+public fun runTestCoroutine(context: CoroutineContext, block: suspend CoroutineScope.() -> Unit) {
+ val newContext = GlobalScope.newCoroutineContext(context)
+ val coroutine = object: AbstractCoroutine<Unit>(newContext, initParentJob = true, active = true) {}
+ coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
+ runEventLoop()
+ check(coroutine.isCompleted) { "Coroutine $coroutine did not complete, but the system reached quiescence" }
+ coroutine.getCompletionExceptionOrNull()?.let { throw it }
+}
diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md
index 24a2fa8..d3cb526 100644
--- a/kotlinx-coroutines-debug/README.md
+++ b/kotlinx-coroutines-debug/README.md
@@ -61,7 +61,7 @@
### Using as JVM agent
Debug module can also be used as a standalone JVM agent to enable debug probes on the application startup.
-You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.8.1.jar`.
+You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.9.0.jar`.
Additionally, on Linux and Mac OS X you can use `kill -5 $pid` command in order to force your application to print all alive coroutines.
When used as Java agent, `"kotlinx.coroutines.debug.enable.creation.stack.trace"` system property can be used to control
[DebugProbes.enableCreationStackTraces] along with agent startup.
diff --git a/kotlinx-coroutines-debug/test/BlockHoundTest.kt b/kotlinx-coroutines-debug/test/BlockHoundTest.kt
index 8faa3e8..685dbb3 100644
--- a/kotlinx-coroutines-debug/test/BlockHoundTest.kt
+++ b/kotlinx-coroutines-debug/test/BlockHoundTest.kt
@@ -56,6 +56,7 @@
}
}
+ @Suppress("DEPRECATION_ERROR")
@Test
fun testBroadcastChannelNotBeingConsideredBlocking() = runTest {
withContext(Dispatchers.Default) {
diff --git a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt
index aba27e7..f1b9a6d 100644
--- a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt
+++ b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt
@@ -32,7 +32,6 @@
"\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" +
"\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" +
"\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" +
- "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" +
"\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n",
ignoredCoroutine = "BlockingCoroutine"
) {
@@ -60,7 +59,6 @@
"\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" +
"\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" +
"\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" +
- "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" +
"\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n" +
"\tat kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt)\n" +
"\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" +
@@ -91,7 +89,6 @@
"\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" +
"\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" +
"\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" +
- "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" +
"\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n" +
"\tat kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt)\n" +
"\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" +
@@ -106,6 +103,26 @@
}
}
+ /**
+ * Tests that a coroutine started with [CoroutineStart.UNDISPATCHED] is considered running.
+ */
+ @Test
+ fun testUndispatchedCoroutineIsRunning() = runBlocking {
+ val job = launch(Dispatchers.IO, start = CoroutineStart.UNDISPATCHED) { // or launch(Dispatchers.Unconfined)
+ verifyDump(
+ "Coroutine \"coroutine#1\":StandaloneCoroutine{Active}@1e4a7dd4, state: RUNNING\n",
+ ignoredCoroutine = "BlockingCoroutine"
+ )
+ delay(Long.MAX_VALUE)
+ }
+ verifyDump(
+ "Coroutine \"coroutine#1\":StandaloneCoroutine{Active}@1e4a7dd4, state: SUSPENDED\n",
+ ignoredCoroutine = "BlockingCoroutine"
+ ) {
+ job.cancel()
+ }
+ }
+
@Test
fun testCreationStackTrace() = runBlocking {
val deferred = async(Dispatchers.IO) {
@@ -125,7 +142,6 @@
val expected =
"kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n" +
"kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)\n" +
- "kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable\$default(Cancellable.kt)\n" +
"kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)\n" +
"kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt)\n" +
"kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt)\n" +
diff --git a/kotlinx-coroutines-debug/test/StartModeProbesTest.kt b/kotlinx-coroutines-debug/test/StartModeProbesTest.kt
index 035873a..d2e2552 100644
--- a/kotlinx-coroutines-debug/test/StartModeProbesTest.kt
+++ b/kotlinx-coroutines-debug/test/StartModeProbesTest.kt
@@ -140,6 +140,7 @@
fun testLazy() = runTest({ it is CancellationException }) {
launch(start = CoroutineStart.LAZY) { }
actor<Int>(start = CoroutineStart.LAZY) { }
+ @Suppress("DEPRECATION_ERROR")
broadcast<Int>(start = CoroutineStart.LAZY) { }
async(start = CoroutineStart.LAZY) { 1 }
verifyPartialDump(5, "BlockingCoroutine",
diff --git a/kotlinx-coroutines-test/README.md b/kotlinx-coroutines-test/README.md
index fbadf57..e8eaf3d 100644
--- a/kotlinx-coroutines-test/README.md
+++ b/kotlinx-coroutines-test/README.md
@@ -26,7 +26,7 @@
Add `kotlinx-coroutines-test` to your project test dependencies:
```
dependencies {
- testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1'
+ testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0'
}
```
@@ -107,7 +107,7 @@
* **The calls to `delay` are automatically skipped**, preserving the relative execution order of the tasks. This way,
it's possible to make tests finish more-or-less immediately.
-* **The execution times out after 10 seconds**, cancelling the test coroutine to prevent tests from hanging forever
+* **The execution times out after 60 seconds**, cancelling the test coroutine to prevent tests from hanging forever
and eating up the CI resources.
* **Controlling the virtual time**: in case just skipping delays is not sufficient, it's possible to more carefully
guide the execution, advancing the virtual time by a duration, draining the queue of the awaiting tasks, or running
@@ -119,7 +119,7 @@
## Timeout
-Test automatically time out after 10 seconds. For example, this test will fail with a timeout exception:
+Test automatically time out after 60 seconds. For example, this test will fail with a timeout exception:
```kotlin
@Test
@@ -128,7 +128,7 @@
}
```
-In case the test is expected to take longer than 10 seconds, the timeout can be increased by passing the `timeout`
+In case the test is expected to take longer than 60 seconds, the timeout can be increased by passing the `timeout`
parameter:
```kotlin
diff --git a/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api
index 64639f2..77cc854 100644
--- a/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api
+++ b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.api
@@ -1,14 +1,3 @@
-public abstract interface class kotlinx/coroutines/test/DelayController {
- public abstract fun advanceTimeBy (J)J
- public abstract fun advanceUntilIdle ()J
- public abstract fun cleanupTestCoroutines ()V
- public abstract fun getCurrentTime ()J
- public abstract fun pauseDispatcher ()V
- public abstract fun pauseDispatcher (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public abstract fun resumeDispatcher ()V
- public abstract fun runCurrent ()V
-}
-
public final class kotlinx/coroutines/test/TestBuildersKt {
public static final fun runBlockingTest (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)V
public static final fun runBlockingTest (Lkotlinx/coroutines/test/TestCoroutineDispatcher;Lkotlin/jvm/functions/Function2;)V
@@ -31,21 +20,17 @@
public static synthetic fun runTestWithLegacyScope$default (Lkotlin/coroutines/CoroutineContext;JLkotlin/jvm/functions/Function2;ILjava/lang/Object;)V
}
-public final class kotlinx/coroutines/test/TestCoroutineDispatcher : kotlinx/coroutines/test/TestDispatcher, kotlinx/coroutines/Delay, kotlinx/coroutines/test/SchedulerAsDelayController {
+public final class kotlinx/coroutines/test/TestCoroutineDispatcher : kotlinx/coroutines/test/TestDispatcher, kotlinx/coroutines/Delay {
public fun <init> ()V
public fun <init> (Lkotlinx/coroutines/test/TestCoroutineScheduler;)V
public synthetic fun <init> (Lkotlinx/coroutines/test/TestCoroutineScheduler;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun advanceTimeBy (J)J
- public fun advanceUntilIdle ()J
- public fun cleanupTestCoroutines ()V
+ public final fun advanceUntilIdle ()J
+ public final fun cleanupTestCoroutines ()V
public fun dispatch (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V
public fun dispatchYield (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V
- public fun getCurrentTime ()J
+ public final fun getCurrentTime ()J
public fun getScheduler ()Lkotlinx/coroutines/test/TestCoroutineScheduler;
- public fun pauseDispatcher ()V
- public fun pauseDispatcher (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public fun resumeDispatcher ()V
- public fun runCurrent ()V
+ public final fun runCurrent ()V
public fun toString ()Ljava/lang/String;
}
@@ -56,13 +41,6 @@
public static synthetic fun UnconfinedTestDispatcher$default (Lkotlinx/coroutines/test/TestCoroutineScheduler;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/coroutines/test/TestDispatcher;
}
-public final class kotlinx/coroutines/test/TestCoroutineExceptionHandler : kotlin/coroutines/AbstractCoroutineContextElement, kotlinx/coroutines/CoroutineExceptionHandler, kotlinx/coroutines/test/UncaughtExceptionCaptor {
- public fun <init> ()V
- public fun cleanupTestCoroutines ()V
- public fun getUncaughtExceptions ()Ljava/util/List;
- public fun handleException (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Throwable;)V
-}
-
public final class kotlinx/coroutines/test/TestCoroutineScheduler : kotlin/coroutines/AbstractCoroutineContextElement, kotlin/coroutines/CoroutineContext$Element {
public static final field Key Lkotlinx/coroutines/test/TestCoroutineScheduler$Key;
public fun <init> ()V
@@ -85,15 +63,10 @@
public final class kotlinx/coroutines/test/TestCoroutineScopeKt {
public static final fun TestCoroutineScope (Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/test/TestCoroutineScope;
public static synthetic fun TestCoroutineScope$default (Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/test/TestCoroutineScope;
- public static final fun advanceTimeBy (Lkotlinx/coroutines/test/TestCoroutineScope;J)V
public static final fun advanceUntilIdle (Lkotlinx/coroutines/test/TestCoroutineScope;)V
public static final fun createTestCoroutineScope (Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/test/TestCoroutineScope;
public static synthetic fun createTestCoroutineScope$default (Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/test/TestCoroutineScope;
public static final fun getCurrentTime (Lkotlinx/coroutines/test/TestCoroutineScope;)J
- public static final fun getUncaughtExceptions (Lkotlinx/coroutines/test/TestCoroutineScope;)Ljava/util/List;
- public static final fun pauseDispatcher (Lkotlinx/coroutines/test/TestCoroutineScope;)V
- public static final fun pauseDispatcher (Lkotlinx/coroutines/test/TestCoroutineScope;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static final fun resumeDispatcher (Lkotlinx/coroutines/test/TestCoroutineScope;)V
public static final fun runCurrent (Lkotlinx/coroutines/test/TestCoroutineScope;)V
}
@@ -128,8 +101,3 @@
public static final fun setCatchNonTestRelatedExceptions (Z)V
}
-public abstract interface class kotlinx/coroutines/test/UncaughtExceptionCaptor {
- public abstract fun cleanupTestCoroutines ()V
- public abstract fun getUncaughtExceptions ()Ljava/util/List;
-}
-
diff --git a/kotlinx-coroutines-test/api/kotlinx-coroutines-test.klib.api b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.klib.api
new file mode 100644
index 0000000..38dfad9
--- /dev/null
+++ b/kotlinx-coroutines-test/api/kotlinx-coroutines-test.klib.api
@@ -0,0 +1,107 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Alias: native => [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-coroutines-test>
+sealed interface kotlinx.coroutines.test/TestScope : kotlinx.coroutines/CoroutineScope { // kotlinx.coroutines.test/TestScope|null[0]
+ abstract val backgroundScope // kotlinx.coroutines.test/TestScope.backgroundScope|{}backgroundScope[0]
+ abstract fun <get-backgroundScope>(): kotlinx.coroutines/CoroutineScope // kotlinx.coroutines.test/TestScope.backgroundScope.<get-backgroundScope>|<get-backgroundScope>(){}[0]
+ abstract val testScheduler // kotlinx.coroutines.test/TestScope.testScheduler|{}testScheduler[0]
+ abstract fun <get-testScheduler>(): kotlinx.coroutines.test/TestCoroutineScheduler // kotlinx.coroutines.test/TestScope.testScheduler.<get-testScheduler>|<get-testScheduler>(){}[0]
+}
+
+abstract class kotlinx.coroutines.test/TestDispatcher : kotlinx.coroutines/CoroutineDispatcher, kotlinx.coroutines/Delay, kotlinx.coroutines/DelayWithTimeoutDiagnostics { // kotlinx.coroutines.test/TestDispatcher|null[0]
+ abstract val scheduler // kotlinx.coroutines.test/TestDispatcher.scheduler|{}scheduler[0]
+ abstract fun <get-scheduler>(): kotlinx.coroutines.test/TestCoroutineScheduler // kotlinx.coroutines.test/TestDispatcher.scheduler.<get-scheduler>|<get-scheduler>(){}[0]
+
+ open fun invokeOnTimeout(kotlin/Long, kotlinx.coroutines/Runnable, kotlin.coroutines/CoroutineContext): kotlinx.coroutines/DisposableHandle // kotlinx.coroutines.test/TestDispatcher.invokeOnTimeout|invokeOnTimeout(kotlin.Long;kotlinx.coroutines.Runnable;kotlin.coroutines.CoroutineContext){}[0]
+ open fun scheduleResumeAfterDelay(kotlin/Long, kotlinx.coroutines/CancellableContinuation<kotlin/Unit>) // kotlinx.coroutines.test/TestDispatcher.scheduleResumeAfterDelay|scheduleResumeAfterDelay(kotlin.Long;kotlinx.coroutines.CancellableContinuation<kotlin.Unit>){}[0]
+ open fun timeoutMessage(kotlin.time/Duration): kotlin/String // kotlinx.coroutines.test/TestDispatcher.timeoutMessage|timeoutMessage(kotlin.time.Duration){}[0]
+}
+
+final class kotlinx.coroutines.test/TestCoroutineScheduler : kotlin.coroutines/AbstractCoroutineContextElement, kotlin.coroutines/CoroutineContext.Element { // kotlinx.coroutines.test/TestCoroutineScheduler|null[0]
+ constructor <init>() // kotlinx.coroutines.test/TestCoroutineScheduler.<init>|<init>(){}[0]
+
+ final val timeSource // kotlinx.coroutines.test/TestCoroutineScheduler.timeSource|{}timeSource[0]
+ final fun <get-timeSource>(): kotlin.time/TimeSource.WithComparableMarks // kotlinx.coroutines.test/TestCoroutineScheduler.timeSource.<get-timeSource>|<get-timeSource>(){}[0]
+
+ final var currentTime // kotlinx.coroutines.test/TestCoroutineScheduler.currentTime|{}currentTime[0]
+ final fun <get-currentTime>(): kotlin/Long // kotlinx.coroutines.test/TestCoroutineScheduler.currentTime.<get-currentTime>|<get-currentTime>(){}[0]
+
+ final fun advanceTimeBy(kotlin.time/Duration) // kotlinx.coroutines.test/TestCoroutineScheduler.advanceTimeBy|advanceTimeBy(kotlin.time.Duration){}[0]
+ final fun advanceTimeBy(kotlin/Long) // kotlinx.coroutines.test/TestCoroutineScheduler.advanceTimeBy|advanceTimeBy(kotlin.Long){}[0]
+ final fun advanceUntilIdle() // kotlinx.coroutines.test/TestCoroutineScheduler.advanceUntilIdle|advanceUntilIdle(){}[0]
+ final fun runCurrent() // kotlinx.coroutines.test/TestCoroutineScheduler.runCurrent|runCurrent(){}[0]
+
+ final object Key : kotlin.coroutines/CoroutineContext.Key<kotlinx.coroutines.test/TestCoroutineScheduler> // kotlinx.coroutines.test/TestCoroutineScheduler.Key|null[0]
+}
+
+final val kotlinx.coroutines.test/currentTime // kotlinx.coroutines.test/currentTime|@kotlinx.coroutines.test.TestScope{}currentTime[0]
+ final fun (kotlinx.coroutines.test/TestScope).<get-currentTime>(): kotlin/Long // kotlinx.coroutines.test/currentTime.<get-currentTime>|<get-currentTime>@kotlinx.coroutines.test.TestScope(){}[0]
+final val kotlinx.coroutines.test/testTimeSource // kotlinx.coroutines.test/testTimeSource|@kotlinx.coroutines.test.TestScope{}testTimeSource[0]
+ final fun (kotlinx.coroutines.test/TestScope).<get-testTimeSource>(): kotlin.time/TimeSource.WithComparableMarks // kotlinx.coroutines.test/testTimeSource.<get-testTimeSource>|<get-testTimeSource>@kotlinx.coroutines.test.TestScope(){}[0]
+
+final var kotlinx.coroutines.test/catchNonTestRelatedExceptions // kotlinx.coroutines.test/catchNonTestRelatedExceptions|{}catchNonTestRelatedExceptions[0]
+ final fun <get-catchNonTestRelatedExceptions>(): kotlin/Boolean // kotlinx.coroutines.test/catchNonTestRelatedExceptions.<get-catchNonTestRelatedExceptions>|<get-catchNonTestRelatedExceptions>(){}[0]
+ final fun <set-catchNonTestRelatedExceptions>(kotlin/Boolean) // kotlinx.coroutines.test/catchNonTestRelatedExceptions.<set-catchNonTestRelatedExceptions>|<set-catchNonTestRelatedExceptions>(kotlin.Boolean){}[0]
+
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/advanceTimeBy(kotlin.time/Duration) // kotlinx.coroutines.test/advanceTimeBy|[email protected](kotlin.time.Duration){}[0]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/advanceTimeBy(kotlin/Long) // kotlinx.coroutines.test/advanceTimeBy|[email protected](kotlin.Long){}[0]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/advanceUntilIdle() // kotlinx.coroutines.test/advanceUntilIdle|[email protected](){}[0]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/runCurrent() // kotlinx.coroutines.test/runCurrent|[email protected](){}[0]
+final fun (kotlinx.coroutines/Dispatchers).kotlinx.coroutines.test/resetMain() // kotlinx.coroutines.test/resetMain|[email protected](){}[0]
+final fun (kotlinx.coroutines/Dispatchers).kotlinx.coroutines.test/setMain(kotlinx.coroutines/CoroutineDispatcher) // kotlinx.coroutines.test/setMain|[email protected](kotlinx.coroutines.CoroutineDispatcher){}[0]
+final fun kotlinx.coroutines.test/StandardTestDispatcher(kotlinx.coroutines.test/TestCoroutineScheduler? = ..., kotlin/String? = ...): kotlinx.coroutines.test/TestDispatcher // kotlinx.coroutines.test/StandardTestDispatcher|StandardTestDispatcher(kotlinx.coroutines.test.TestCoroutineScheduler?;kotlin.String?){}[0]
+final fun kotlinx.coroutines.test/TestScope(kotlin.coroutines/CoroutineContext = ...): kotlinx.coroutines.test/TestScope // kotlinx.coroutines.test/TestScope|TestScope(kotlin.coroutines.CoroutineContext){}[0]
+final fun kotlinx.coroutines.test/UnconfinedTestDispatcher(kotlinx.coroutines.test/TestCoroutineScheduler? = ..., kotlin/String? = ...): kotlinx.coroutines.test/TestDispatcher // kotlinx.coroutines.test/UnconfinedTestDispatcher|UnconfinedTestDispatcher(kotlinx.coroutines.test.TestCoroutineScheduler?;kotlin.String?){}[0]
+
+// Targets: [native, wasmWasi]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/runTest(kotlin.time/Duration = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>) // kotlinx.coroutines.test/runTest|[email protected](kotlin.time.Duration;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
+
+// Targets: [native, wasmWasi]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/runTest(kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>) // kotlinx.coroutines.test/runTest|[email protected](kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
+
+// Targets: [native, wasmWasi]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/runTestLegacy(kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>, kotlin/Int, kotlin/Any?) // kotlinx.coroutines.test/runTestLegacy|[email protected](kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>;kotlin.Int;kotlin.Any?){}[0]
+
+// Targets: [native, wasmWasi]
+final fun kotlinx.coroutines.test/runTest(kotlin.coroutines/CoroutineContext = ..., kotlin.time/Duration = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>) // kotlinx.coroutines.test/runTest|runTest(kotlin.coroutines.CoroutineContext;kotlin.time.Duration;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
+
+// Targets: [native, wasmWasi]
+final fun kotlinx.coroutines.test/runTest(kotlin.coroutines/CoroutineContext = ..., kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>) // kotlinx.coroutines.test/runTest|runTest(kotlin.coroutines.CoroutineContext;kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
+
+// Targets: [js, wasmJs]
+final class kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting { // kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting|null[0]
+ constructor <init>() // kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting.<init>|<init>(){}[0]
+
+ // Targets: [js]
+ final fun then(kotlin/Function1<kotlin/Unit, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting.then|then(kotlin.Function1<kotlin.Unit,kotlin.Unit>){}[0]
+
+ // Targets: [js]
+ final fun then(kotlin/Function1<kotlin/Unit, kotlin/Unit>, kotlin/Function1<kotlin/Throwable, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting.then|then(kotlin.Function1<kotlin.Unit,kotlin.Unit>;kotlin.Function1<kotlin.Throwable,kotlin.Unit>){}[0]
+
+ // Targets: [wasmJs]
+ final fun then(kotlin/Function1<kotlin.js/JsAny, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting.then|then(kotlin.Function1<kotlin.js.JsAny,kotlin.Unit>){}[0]
+
+ // Targets: [wasmJs]
+ final fun then(kotlin/Function1<kotlin.js/JsAny, kotlin/Unit>, kotlin/Function1<kotlin.js/JsAny, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting.then|then(kotlin.Function1<kotlin.js.JsAny,kotlin.Unit>;kotlin.Function1<kotlin.js.JsAny,kotlin.Unit>){}[0]
+}
+
+// Targets: [js, wasmJs]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/runTest(kotlin.time/Duration = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test/runTest|[email protected](kotlin.time.Duration;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
+
+// Targets: [js, wasmJs]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/runTest(kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test/runTest|[email protected](kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
+
+// Targets: [js, wasmJs]
+final fun (kotlinx.coroutines.test/TestScope).kotlinx.coroutines.test/runTestLegacy(kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>, kotlin/Int, kotlin/Any?): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test/runTestLegacy|[email protected](kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>;kotlin.Int;kotlin.Any?){}[0]
+
+// Targets: [js, wasmJs]
+final fun kotlinx.coroutines.test/runTest(kotlin.coroutines/CoroutineContext = ..., kotlin.time/Duration = ..., kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test/runTest|runTest(kotlin.coroutines.CoroutineContext;kotlin.time.Duration;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
+
+// Targets: [js, wasmJs]
+final fun kotlinx.coroutines.test/runTest(kotlin.coroutines/CoroutineContext = ..., kotlin/Long, kotlin.coroutines/SuspendFunction1<kotlinx.coroutines.test/TestScope, kotlin/Unit>): kotlinx.coroutines.test.internal/JsPromiseInterfaceForTesting // kotlinx.coroutines.test/runTest|runTest(kotlin.coroutines.CoroutineContext;kotlin.Long;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.test.TestScope,kotlin.Unit>){}[0]
diff --git a/kotlinx-coroutines-test/build.gradle.kts b/kotlinx-coroutines-test/build.gradle.kts
index 2c1d043..fb06e6c 100644
--- a/kotlinx-coroutines-test/build.gradle.kts
+++ b/kotlinx-coroutines-test/build.gradle.kts
@@ -2,12 +2,6 @@
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
kotlin {
- targets.withType(KotlinNativeTargetWithTests::class.java).configureEach {
- binaries.getTest("DEBUG").apply {
- optimized = true
- }
- }
-
sourceSets {
jvmTest {
dependencies {
diff --git a/kotlinx-coroutines-test/common/src/TestBuilders.kt b/kotlinx-coroutines-test/common/src/TestBuilders.kt
index 4fa8685..fa0ad5e 100644
--- a/kotlinx-coroutines-test/common/src/TestBuilders.kt
+++ b/kotlinx-coroutines-test/common/src/TestBuilders.kt
@@ -3,8 +3,8 @@
package kotlinx.coroutines.test
+import kotlinx.atomicfu.atomic
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
import kotlinx.coroutines.selects.*
import kotlin.coroutines.*
import kotlin.jvm.*
@@ -308,12 +308,17 @@
): TestResult = asSpecificImplementation().let { scope ->
scope.enter()
createTestResult {
+ val testBodyFinished = AtomicBoolean(false)
/** TODO: moving this [AbstractCoroutine.start] call outside [createTestResult] fails on JS. */
scope.start(CoroutineStart.UNDISPATCHED, scope) {
/* we're using `UNDISPATCHED` to avoid the event loop, but we do want to set up the timeout machinery
before any code executes, so we have to park here. */
yield()
- testBody()
+ try {
+ testBody()
+ } finally {
+ testBodyFinished.value = true
+ }
}
var timeoutError: Throwable? = null
var cancellationException: CancellationException? = null
@@ -336,17 +341,15 @@
if (exception is TimeoutCancellationException) {
dumpCoroutines()
val activeChildren = scope.children.filter(Job::isActive).toList()
- val completionCause = if (scope.isCancelled) scope.tryGetCompletionCause() else null
- var message = "After waiting for $timeout"
- if (completionCause == null)
- message += ", the test coroutine is not completing"
- if (activeChildren.isNotEmpty())
- message += ", there were active child jobs: $activeChildren"
- if (completionCause != null && activeChildren.isEmpty()) {
- message += if (scope.isCompleted)
- ", the test coroutine completed"
- else
- ", the test coroutine was not completed"
+ val message = "After waiting for $timeout, " + when {
+ testBodyFinished.value && activeChildren.isNotEmpty() ->
+ "there were active child jobs: $activeChildren. " +
+ "Use `TestScope.backgroundScope` " +
+ "to launch the coroutines that need to be cancelled when the test body finishes"
+ testBodyFinished.value ->
+ "the test completed, but only after the timeout"
+ else ->
+ "the test body did not run to completion"
}
timeoutError = UncompletedCoroutinesError(message)
cancellationException = CancellationException("The test timed out")
@@ -603,3 +606,11 @@
marker: Int,
unused2: Any?,
): TestResult = runTest(dispatchTimeoutMs = if (marker and 1 != 0) dispatchTimeoutMs else 60_000L, testBody)
+
+// Remove after https://youtrack.jetbrains.com/issue/KT-62423/
+private class AtomicBoolean(initial: Boolean) {
+ private val container = atomic(initial)
+ var value: Boolean
+ get() = container.value
+ set(value: Boolean) { container.value = value }
+}
diff --git a/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt b/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt
index 1b85bf9..bf1b62a 100644
--- a/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt
+++ b/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt
@@ -19,18 +19,18 @@
* ```
* @Test
* fun testEagerlyEnteringChildCoroutines() = runTest(UnconfinedTestDispatcher()) {
- * var entered = false
- * val deferred = CompletableDeferred<Unit>()
- * var completed = false
- * launch {
- * entered = true
- * deferred.await()
- * completed = true
- * }
- * assertTrue(entered) // `entered = true` already executed.
- * assertFalse(completed) // however, the child coroutine then suspended, so it is enqueued.
- * deferred.complete(Unit) // resume the coroutine.
- * assertTrue(completed) // now the child coroutine is immediately completed.
+ * var entered = false
+ * val deferred = CompletableDeferred<Unit>()
+ * var completed = false
+ * launch {
+ * entered = true
+ * deferred.await()
+ * completed = true
+ * }
+ * assertTrue(entered) // `entered = true` already executed.
+ * assertFalse(completed) // however, the child coroutine then suspended, so it is enqueued.
+ * deferred.complete(Unit) // resume the coroutine.
+ * assertTrue(completed) // now the child coroutine is immediately completed.
* }
* ```
*
@@ -42,20 +42,20 @@
* ```
* @Test
* fun testUnconfinedDispatcher() = runTest {
- * val values = mutableListOf<Int>()
- * val stateFlow = MutableStateFlow(0)
- * val job = launch(UnconfinedTestDispatcher(testScheduler)) {
- * stateFlow.collect {
- * values.add(it)
+ * val values = mutableListOf<Int>()
+ * val stateFlow = MutableStateFlow(0)
+ * val job = launch(UnconfinedTestDispatcher(testScheduler)) {
+ * stateFlow.collect {
+ * values.add(it)
+ * }
* }
- * }
- * stateFlow.value = 1
- * stateFlow.value = 2
- * stateFlow.value = 3
- * job.cancel()
- * // each assignment will immediately resume the collecting child coroutine,
- * // so no values will be skipped.
- * assertEquals(listOf(0, 1, 2, 3), values)
+ * stateFlow.value = 1
+ * stateFlow.value = 2
+ * stateFlow.value = 3
+ * job.cancel()
+ * // each assignment will immediately resume the collecting child coroutine,
+ * // so no values will be skipped.
+ * assertEquals(listOf(0, 1, 2, 3), values)
* }
* ```
*
diff --git a/kotlinx-coroutines-test/common/src/TestScope.kt b/kotlinx-coroutines-test/common/src/TestScope.kt
index 180e76d..10a98a1 100644
--- a/kotlinx-coroutines-test/common/src/TestScope.kt
+++ b/kotlinx-coroutines-test/common/src/TestScope.kt
@@ -63,16 +63,16 @@
* ```
* @Test
* fun testExampleBackgroundJob() = runTest {
- * val channel = Channel<Int>()
- * backgroundScope.launch {
- * var i = 0
- * while (true) {
- * channel.send(i++)
+ * val channel = Channel<Int>()
+ * backgroundScope.launch {
+ * var i = 0
+ * while (true) {
+ * channel.send(i++)
+ * }
* }
- * }
- * repeat(100) {
- * assertEquals(it, channel.receive())
- * }
+ * repeat(100) {
+ * assertEquals(it, channel.receive())
+ * }
* }
* ```
*/
diff --git a/kotlinx-coroutines-test/common/test/RunTestTest.kt b/kotlinx-coroutines-test/common/test/RunTestTest.kt
index 799bcae..a595299 100644
--- a/kotlinx-coroutines-test/common/test/RunTestTest.kt
+++ b/kotlinx-coroutines-test/common/test/RunTestTest.kt
@@ -135,6 +135,8 @@
@Test
@NoJs
@NoNative
+ @NoWasmWasi
+ @NoWasmJs
fun testListingActiveCoroutinesOnTimeout(): TestResult {
val name1 = "GoodUniqueName"
val name2 = "BadUniqueName"
@@ -471,4 +473,18 @@
runTest {
}
})
+
+ @Test
+ fun testCancellingTestScope() = testResultMap({
+ try {
+ it()
+ fail("unreached")
+ } catch (e: CancellationException) {
+ // expected
+ }
+ }) {
+ runTest {
+ cancel(CancellationException("Oh no", TestException()))
+ }
+ }
}
diff --git a/kotlinx-coroutines-test/js/src/TestBuilders.kt b/kotlinx-coroutines-test/js/src/TestBuilders.kt
index 8d30201..e526561 100644
--- a/kotlinx-coroutines-test/js/src/TestBuilders.kt
+++ b/kotlinx-coroutines-test/js/src/TestBuilders.kt
@@ -1,15 +1,17 @@
package kotlinx.coroutines.test
+
import kotlinx.coroutines.*
-import kotlin.js.*
+import kotlinx.coroutines.test.internal.*
-@Suppress("ACTUAL_WITHOUT_EXPECT", "ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE")
-public actual typealias TestResult = Promise<Unit>
+public actual typealias TestResult = JsPromiseInterfaceForTesting
-internal actual fun systemPropertyImpl(name: String): String? = null
+@Suppress("CAST_NEVER_SUCCEEDS")
internal actual fun createTestResult(testProcedure: suspend CoroutineScope.() -> Unit): TestResult =
GlobalScope.promise {
testProcedure()
- }
+ } as JsPromiseInterfaceForTesting
internal actual fun dumpCoroutines() { }
+
+internal actual fun systemPropertyImpl(name: String): String? = null
diff --git a/kotlinx-coroutines-test/js/src/internal/JsPromiseInterfaceForTesting.kt b/kotlinx-coroutines-test/js/src/internal/JsPromiseInterfaceForTesting.kt
new file mode 100644
index 0000000..753c51d
--- /dev/null
+++ b/kotlinx-coroutines-test/js/src/internal/JsPromiseInterfaceForTesting.kt
@@ -0,0 +1,19 @@
+package kotlinx.coroutines.test.internal
+
+/* This is a declaration of JS's `Promise<Unit>`. We need to keep it a separate class, because
+`actual typealias TestResult = Promise<Unit>` fails: you can't instantiate an `expect class` with a typealias to
+a parametric class. So, we make a non-parametric class just for this. */
+/**
+ * @suppress
+ */
+@JsName("Promise")
+public external class JsPromiseInterfaceForTesting {
+ /**
+ * @suppress
+ */
+ public fun then(onFulfilled: ((Unit) -> Unit), onRejected: ((Throwable) -> Unit)): JsPromiseInterfaceForTesting
+ /**
+ * @suppress
+ */
+ public fun then(onFulfilled: ((Unit) -> Unit)): JsPromiseInterfaceForTesting
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-test/jvm/src/migration/DelayController.kt b/kotlinx-coroutines-test/jvm/src/migration/DelayController.kt
deleted file mode 100644
index f1aa213..0000000
--- a/kotlinx-coroutines-test/jvm/src/migration/DelayController.kt
+++ /dev/null
@@ -1,206 +0,0 @@
-@file:Suppress("DEPRECATION_ERROR")
-
-package kotlinx.coroutines.test
-
-import kotlinx.coroutines.*
-
-/**
- * Control the virtual clock time of a [CoroutineDispatcher].
- *
- * Testing libraries may expose this interface to the tests instead of [TestCoroutineDispatcher].
- *
- * This interface is deprecated without replacement.
- * Instead, [TestCoroutineScheduler] is supposed to be used to control the virtual time.
- * Please see the
- * [migration guide](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md)
- * for an instruction on how to update the code for the new API.
- */
-@ExperimentalCoroutinesApi
-@Deprecated(
- "Use `TestCoroutineScheduler` to control virtual time.",
- level = DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public interface DelayController {
- /**
- * Returns the current virtual clock-time as it is known to this Dispatcher.
- *
- * @return The virtual clock-time
- */
- @ExperimentalCoroutinesApi
- public val currentTime: Long
-
- /**
- * Moves the Dispatcher's virtual clock forward by a specified amount of time.
- *
- * The amount the clock is progressed may be larger than the requested `delayTimeMillis` if the code under test uses
- * blocking coroutines.
- *
- * The virtual clock time will advance once for each delay resumed until the next delay exceeds the requested
- * `delayTimeMills`. In the following test, the virtual time will progress by 2_000 then 1 to resume three different
- * calls to delay.
- *
- * ```
- * @Test
- * fun advanceTimeTest() = runBlockingTest {
- * foo()
- * advanceTimeBy(2_000) // advanceTimeBy(2_000) will progress through the first two delays
- * // virtual time is 2_000, next resume is at 2_001
- * advanceTimeBy(2) // progress through the last delay of 501 (note 500ms were already advanced)
- * // virtual time is 2_0002
- * }
- *
- * fun CoroutineScope.foo() {
- * launch {
- * delay(1_000) // advanceTimeBy(2_000) will progress through this delay (resume @ virtual time 1_000)
- * // virtual time is 1_000
- * delay(500) // advanceTimeBy(2_000) will progress through this delay (resume @ virtual time 1_500)
- * // virtual time is 1_500
- * delay(501) // advanceTimeBy(2_000) will not progress through this delay (resume @ virtual time 2_001)
- * // virtual time is 2_001
- * }
- * }
- * ```
- *
- * @param delayTimeMillis The amount of time to move the CoroutineContext's clock forward.
- * @return The amount of delay-time that this Dispatcher's clock has been forwarded.
- */
- @ExperimentalCoroutinesApi
- public fun advanceTimeBy(delayTimeMillis: Long): Long
-
- /**
- * Immediately execute all pending tasks and advance the virtual clock-time to the last delay.
- *
- * If new tasks are scheduled due to advancing virtual time, they will be executed before `advanceUntilIdle`
- * returns.
- *
- * @return the amount of delay-time that this Dispatcher's clock has been forwarded in milliseconds.
- */
- @ExperimentalCoroutinesApi
- public fun advanceUntilIdle(): Long
-
- /**
- * Run any tasks that are pending at or before the current virtual clock-time.
- *
- * Calling this function will never advance the clock.
- */
- @ExperimentalCoroutinesApi
- public fun runCurrent()
-
- /**
- * Call after test code completes to ensure that the dispatcher is properly cleaned up.
- *
- * @throws AssertionError if any pending tasks are active, however it will not throw for suspended
- * coroutines.
- */
- @ExperimentalCoroutinesApi
- @Throws(AssertionError::class)
- public fun cleanupTestCoroutines()
-
- /**
- * Run a block of code in a paused dispatcher.
- *
- * By pausing the dispatcher any new coroutines will not execute immediately. After block executes, the dispatcher
- * will resume auto-advancing.
- *
- * This is useful when testing functions that start a coroutine. By pausing the dispatcher assertions or
- * setup may be done between the time the coroutine is created and started.
- */
- @Deprecated(
- "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
- level = DeprecationLevel.ERROR
- )
- // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
- public suspend fun pauseDispatcher(block: suspend () -> Unit)
-
- /**
- * Pause the dispatcher.
- *
- * When paused, the dispatcher will not execute any coroutines automatically, and you must call [runCurrent] or
- * [advanceTimeBy], or [advanceUntilIdle] to execute coroutines.
- */
- @Deprecated(
- "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
- level = DeprecationLevel.ERROR
- )
- // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
- public fun pauseDispatcher()
-
- /**
- * Resume the dispatcher from a paused state.
- *
- * Resumed dispatchers will automatically progress through all coroutines scheduled at the current time. To advance
- * time and execute coroutines scheduled in the future use, one of [advanceTimeBy],
- * or [advanceUntilIdle].
- */
- @Deprecated(
- "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
- level = DeprecationLevel.ERROR
- )
- // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
- public fun resumeDispatcher()
-}
-
-internal interface SchedulerAsDelayController : DelayController {
- val scheduler: TestCoroutineScheduler
-
- /** @suppress */
- @Deprecated(
- "This property delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
- ReplaceWith("this.scheduler.currentTime"),
- level = DeprecationLevel.ERROR
- )
- // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
- override val currentTime: Long
- get() = scheduler.currentTime
-
-
- /** @suppress */
- @Deprecated(
- "This function delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
- ReplaceWith("this.scheduler.apply { advanceTimeBy(delayTimeMillis); runCurrent() }"),
- level = DeprecationLevel.ERROR
- )
- // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
- override fun advanceTimeBy(delayTimeMillis: Long): Long {
- val oldTime = scheduler.currentTime
- scheduler.advanceTimeBy(delayTimeMillis)
- scheduler.runCurrent()
- return scheduler.currentTime - oldTime
- }
-
- /** @suppress */
- @Deprecated(
- "This function delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
- ReplaceWith("this.scheduler.advanceUntilIdle()"),
- level = DeprecationLevel.ERROR
- )
- // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
- override fun advanceUntilIdle(): Long {
- val oldTime = scheduler.currentTime
- scheduler.advanceUntilIdle()
- return scheduler.currentTime - oldTime
- }
-
- /** @suppress */
- @Deprecated(
- "This function delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
- ReplaceWith("this.scheduler.runCurrent()"),
- level = DeprecationLevel.ERROR
- )
- // Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
- override fun runCurrent(): Unit = scheduler.runCurrent()
-
- /** @suppress */
- @ExperimentalCoroutinesApi
- override fun cleanupTestCoroutines() {
- // process any pending cancellations or completions, but don't advance time
- scheduler.runCurrent()
- if (!scheduler.isIdle(strict = false)) {
- throw UncompletedCoroutinesError(
- "Unfinished coroutines during tear-down. Ensure all coroutines are" +
- " completed or cancelled by your test."
- )
- }
- }
-}
diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt b/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt
index 2e0155b..53d963a 100644
--- a/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt
+++ b/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt
@@ -1,4 +1,4 @@
-@file:Suppress("DEPRECATION")
+@file:Suppress("DEPRECATION", "DEPRECATION_ERROR")
@file:JvmName("TestBuildersKt")
@file:JvmMultifileClass
@@ -51,9 +51,9 @@
"Use `runTest` instead to support completing from other dispatchers. " +
"Please see the migration guide for details: " +
"https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md",
- level = DeprecationLevel.WARNING
+ level = DeprecationLevel.ERROR
)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun runBlockingTest(
context: CoroutineContext = EmptyCoroutineContext,
testBody: suspend TestCoroutineScope.() -> Unit
@@ -73,8 +73,8 @@
/**
* A version of [runBlockingTest] that works with [TestScope].
*/
-@Deprecated("Use `runTest` instead to support completing from other dispatchers.", level = DeprecationLevel.WARNING)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+@Deprecated("Use `runTest` instead to support completing from other dispatchers.", level = DeprecationLevel.ERROR)
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun runBlockingTestOnTestScope(
context: CoroutineContext = EmptyCoroutineContext,
testBody: suspend TestScope.() -> Unit
@@ -121,17 +121,17 @@
"Use `runTest` instead to support completing from other dispatchers. " +
"Please see the migration guide for details: " +
"https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md",
- level = DeprecationLevel.WARNING
+ level = DeprecationLevel.ERROR
)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun TestCoroutineScope.runBlockingTest(block: suspend TestCoroutineScope.() -> Unit): Unit =
runBlockingTest(coroutineContext, block)
/**
* Convenience method for calling [runBlockingTestOnTestScope] on an existing [TestScope].
*/
-@Deprecated("Use `runTest` instead to support completing from other dispatchers.", level = DeprecationLevel.WARNING)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+@Deprecated("Use `runTest` instead to support completing from other dispatchers.", level = DeprecationLevel.ERROR)
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun TestScope.runBlockingTest(block: suspend TestScope.() -> Unit): Unit =
runBlockingTestOnTestScope(coroutineContext, block)
@@ -147,9 +147,9 @@
"Use `runTest` instead to support completing from other dispatchers. " +
"Please see the migration guide for details: " +
"https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md",
- level = DeprecationLevel.WARNING
+ level = DeprecationLevel.ERROR
)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun TestCoroutineDispatcher.runBlockingTest(block: suspend TestCoroutineScope.() -> Unit): Unit =
runBlockingTest(this, block)
@@ -157,8 +157,8 @@
* This is an overload of [runTest] that works with [TestCoroutineScope].
*/
@ExperimentalCoroutinesApi
-@Deprecated("Use `runTest` instead.", level = DeprecationLevel.WARNING)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+@Deprecated("Use `runTest` instead.", level = DeprecationLevel.ERROR)
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun runTestWithLegacyScope(
context: CoroutineContext = EmptyCoroutineContext,
dispatchTimeoutMs: Long = DEFAULT_DISPATCH_TIMEOUT_MS,
@@ -196,8 +196,8 @@
* immediately from the test body. See the docs for [TestResult] for details.
*/
@ExperimentalCoroutinesApi
-@Deprecated("Use `TestScope.runTest` instead.", level = DeprecationLevel.WARNING)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+@Deprecated("Use `TestScope.runTest` instead.", level = DeprecationLevel.ERROR)
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun TestCoroutineScope.runTest(
dispatchTimeoutMs: Long = DEFAULT_DISPATCH_TIMEOUT_MS,
block: suspend TestCoroutineScope.() -> Unit
diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt
index 1c72a2f..7f80622 100644
--- a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt
+++ b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt
@@ -4,25 +4,14 @@
import kotlin.coroutines.*
/**
- * [CoroutineDispatcher] that performs both immediate and lazy execution of coroutines in tests
- * and uses a [TestCoroutineScheduler] to control its virtual clock.
- *
- * By default, [TestCoroutineDispatcher] is immediate. That means any tasks scheduled to be run without delay are
- * immediately executed. If they were scheduled with a delay, the virtual clock-time must be advanced via one of the
- * methods on the dispatcher's [scheduler].
- *
- * When switched to lazy execution using [pauseDispatcher] any coroutines started via [launch] or [async] will
- * not execute until a call to [DelayController.runCurrent] or the virtual clock-time has been advanced via one of the
- * methods on [DelayController].
- *
- * @see DelayController
+ * @suppress
*/
@Deprecated("The execution order of `TestCoroutineDispatcher` can be confusing, and the mechanism of " +
"pausing is typically misunderstood. Please use `StandardTestDispatcher` or `UnconfinedTestDispatcher` instead.",
- level = DeprecationLevel.WARNING)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+ level = DeprecationLevel.ERROR)
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public class TestCoroutineDispatcher(public override val scheduler: TestCoroutineScheduler = TestCoroutineScheduler()):
- TestDispatcher(), Delay, SchedulerAsDelayController
+ TestDispatcher(), Delay
{
private var dispatchImmediately = true
set(value) {
@@ -56,36 +45,25 @@
private fun post(block: Runnable, context: CoroutineContext) =
scheduler.registerEvent(this, 0, block, context) { false }
- /** @suppress */
- @Deprecated(
- "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
- level = DeprecationLevel.ERROR
- )
- override suspend fun pauseDispatcher(block: suspend () -> Unit) {
- val previous = dispatchImmediately
- dispatchImmediately = false
- try {
- block()
- } finally {
- dispatchImmediately = previous
+ val currentTime: Long
+ get() = scheduler.currentTime
+
+ fun advanceUntilIdle(): Long {
+ val oldTime = scheduler.currentTime
+ scheduler.advanceUntilIdle()
+ return scheduler.currentTime - oldTime
+ }
+
+ fun runCurrent(): Unit = scheduler.runCurrent()
+
+ fun cleanupTestCoroutines() {
+ // process any pending cancellations or completions, but don't advance time
+ scheduler.runCurrent()
+ if (!scheduler.isIdle(strict = false)) {
+ throw UncompletedCoroutinesError(
+ "Unfinished coroutines during tear-down. Ensure all coroutines are" +
+ " completed or cancelled by your test."
+ )
}
}
-
- /** @suppress */
- @Deprecated(
- "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
- level = DeprecationLevel.ERROR
- )
- override fun pauseDispatcher() {
- dispatchImmediately = false
- }
-
- /** @suppress */
- @Deprecated(
- "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
- level = DeprecationLevel.ERROR
- )
- override fun resumeDispatcher() {
- dispatchImmediately = true
- }
}
diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt
index eeff64d..4956b24 100644
--- a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt
+++ b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt
@@ -4,49 +4,8 @@
import kotlinx.coroutines.internal.*
import kotlin.coroutines.*
-/**
- * Access uncaught coroutine exceptions captured during test execution.
- */
-@Deprecated(
- "Deprecated for removal without a replacement. " +
- "Consider whether the default mechanism of handling uncaught exceptions is sufficient. " +
- "If not, try writing your own `CoroutineExceptionHandler` and " +
- "please report your use case at https://github.com/Kotlin/kotlinx.coroutines/issues.",
- level = DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public interface UncaughtExceptionCaptor {
- /**
- * List of uncaught coroutine exceptions.
- *
- * The returned list is a copy of the currently caught exceptions.
- * During [cleanupTestCoroutines] the first element of this list is rethrown if it is not empty.
- */
- public val uncaughtExceptions: List<Throwable>
-
- /**
- * Call after the test completes to ensure that there were no uncaught exceptions.
- *
- * The first exception in uncaughtExceptions is rethrown. All other exceptions are
- * printed using [Throwable.printStackTrace].
- *
- * @throws Throwable the first uncaught exception, if there are any uncaught exceptions.
- */
- public fun cleanupTestCoroutines()
-}
-
-/**
- * An exception handler that captures uncaught exceptions in tests.
- */
-@Suppress("DEPRECATION_ERROR")
-@Deprecated(
- "Deprecated for removal without a replacement. " +
- "It may be to define one's own `CoroutineExceptionHandler` if you just need to handle '" +
- "uncaught exceptions without a special `TestCoroutineScope` integration.", level = DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public class TestCoroutineExceptionHandler :
- AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler, UncaughtExceptionCaptor {
+internal class TestCoroutineExceptionHandler :
+ AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
private val _exceptions = mutableListOf<Throwable>()
private val _lock = SynchronizedObject()
private var _coroutinesCleanedUp = false
@@ -61,10 +20,10 @@
}
}
- public override val uncaughtExceptions: List<Throwable>
+ val uncaughtExceptions: List<Throwable>
get() = synchronized(_lock) { _exceptions.toList() }
- public override fun cleanupTestCoroutines() {
+ fun cleanupTestCoroutines() {
synchronized(_lock) {
_coroutinesCleanedUp = true
val exception = _exceptions.firstOrNull() ?: return
diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt
index d026e32..91d29a4 100644
--- a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt
+++ b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt
@@ -7,42 +7,24 @@
import kotlin.coroutines.*
/**
- * A scope which provides detailed control over the execution of coroutines for tests.
- *
- * This scope is deprecated in favor of [TestScope].
- * Please see the
- * [migration guide](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md)
- * for an instruction on how to update the code for the new API.
+ * @suppress
*/
@ExperimentalCoroutinesApi
@Deprecated("Use `TestScope` in combination with `runTest` instead." +
"Please see the migration guide for details: " +
"https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md",
- level = DeprecationLevel.WARNING)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+ level = DeprecationLevel.ERROR)
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public interface TestCoroutineScope : CoroutineScope {
/**
- * Called after the test completes.
- *
- * - It checks that there were no uncaught exceptions caught by its [CoroutineExceptionHandler].
- * If there were any, then the first one is thrown, whereas the rest are suppressed by it.
- * - It runs the tasks pending in the scheduler at the current time. If there are any uncompleted tasks afterwards,
- * it fails with [UncompletedCoroutinesError].
- * - It checks whether some new child [Job]s were created but not completed since this [TestCoroutineScope] was
- * created. If so, it fails with [UncompletedCoroutinesError].
- *
- * For backward compatibility, if the [CoroutineExceptionHandler] is an [UncaughtExceptionCaptor], its
- * [TestCoroutineExceptionHandler.cleanupTestCoroutines] behavior is performed.
- * Likewise, if the [ContinuationInterceptor] is a [DelayController], its [DelayController.cleanupTestCoroutines]
- * is called.
- *
- * @throws Throwable the first uncaught exception, if there are any uncaught exceptions.
- * @throws AssertionError if any pending tasks are active.
- * @throws IllegalStateException if called more than once.
+ * @suppress
*/
@ExperimentalCoroutinesApi
- @Deprecated("Please call `runTest`, which automatically performs the cleanup, instead of using this function.")
- // Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+ @Deprecated(
+ "Please call `runTest`, which automatically performs the cleanup, instead of using this function.",
+ level = DeprecationLevel.ERROR
+ )
+ // Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun cleanupTestCoroutines()
/**
@@ -90,14 +72,14 @@
try {
delayController.cleanupTestCoroutines()
false
- } catch (e: UncompletedCoroutinesError) {
+ } catch (_: UncompletedCoroutinesError) {
true
}
} else {
testScheduler.runCurrent()
!testScheduler.isIdle(strict = false)
}
- (coroutineContext[CoroutineExceptionHandler] as? UncaughtExceptionCaptor)?.cleanupTestCoroutines()
+ (coroutineContext[CoroutineExceptionHandler] as? TestCoroutineExceptionHandler)?.cleanupTestCoroutines()
synchronized(lock) {
if (cleanedUp)
throw IllegalStateException("Attempting to clean up a test coroutine scope more than once.")
@@ -123,9 +105,7 @@
}
/**
- * A coroutine scope for launching test coroutines using [TestCoroutineDispatcher].
- *
- * [createTestCoroutineScope] is a similar function that defaults to [StandardTestDispatcher].
+ * @suppress
*/
@Deprecated(
"This constructs a `TestCoroutineScope` with a deprecated `CoroutineDispatcher` by default. " +
@@ -134,51 +114,24 @@
"createTestCoroutineScope(TestCoroutineDispatcher() + TestCoroutineExceptionHandler() + context)",
"kotlin.coroutines.EmptyCoroutineContext"
),
- level = DeprecationLevel.WARNING
+ level = DeprecationLevel.ERROR
)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun TestCoroutineScope(context: CoroutineContext = EmptyCoroutineContext): TestCoroutineScope {
val scheduler = context[TestCoroutineScheduler] ?: TestCoroutineScheduler()
return createTestCoroutineScope(TestCoroutineDispatcher(scheduler) + TestCoroutineExceptionHandler() + context)
}
/**
- * A coroutine scope for launching test coroutines.
- *
- * This is a function for aiding in migration from [TestCoroutineScope] to [TestScope].
- * Please see the
- * [migration guide](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md)
- * for an instruction on how to update the code for the new API.
- *
- * It ensures that all the test module machinery is properly initialized.
- * - If [context] doesn't define a [TestCoroutineScheduler] for orchestrating the virtual time used for delay-skipping,
- * a new one is created, unless either
- * - a [TestDispatcher] is provided, in which case [TestDispatcher.scheduler] is used;
- * - at the moment of the creation of the scope, [Dispatchers.Main] is delegated to a [TestDispatcher], in which case
- * its [TestCoroutineScheduler] is used.
- * - If [context] doesn't have a [ContinuationInterceptor], a [StandardTestDispatcher] is created.
- * - A [CoroutineExceptionHandler] is created that makes [TestCoroutineScope.cleanupTestCoroutines] throw if there were
- * any uncaught exceptions, or forwards the exceptions further in a platform-specific manner if the cleanup was
- * already performed when an exception happened. Passing a [CoroutineExceptionHandler] is illegal, unless it's an
- * [UncaughtExceptionCaptor], in which case the behavior is preserved for the time being for backward compatibility.
- * If you need to have a specific [CoroutineExceptionHandler], please pass it to [launch] on an already-created
- * [TestCoroutineScope] and share your use case at
- * [our issue tracker](https://github.com/Kotlin/kotlinx.coroutines/issues).
- * - If [context] provides a [Job], that job is used for the new scope; otherwise, a [CompletableJob] is created.
- *
- * @throws IllegalArgumentException if [context] has both [TestCoroutineScheduler] and a [TestDispatcher] linked to a
- * different scheduler.
- * @throws IllegalArgumentException if [context] has a [ContinuationInterceptor] that is not a [TestDispatcher].
- * @throws IllegalArgumentException if [context] has an [CoroutineExceptionHandler] that is not an
- * [UncaughtExceptionCaptor].
+ * @suppress
*/
@ExperimentalCoroutinesApi
@Deprecated(
"This function was introduced in order to help migrate from TestCoroutineScope to TestScope. " +
"Please use TestScope() construction instead, or just runTest(), without creating a scope.",
- level = DeprecationLevel.WARNING
+ level = DeprecationLevel.ERROR
)
-// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.8.0 and removed as experimental in 1.9.0
+// Since 1.6.0, kept as warning in 1.7.0, ERROR in 1.9.0 and removed as experimental later
public fun createTestCoroutineScope(context: CoroutineContext = EmptyCoroutineContext): TestCoroutineScope {
val ctxWithDispatcher = context.withDelaySkipping()
var scope: TestCoroutineScopeImpl? = null
@@ -190,7 +143,7 @@
}
}
val exceptionHandler = when (val exceptionHandler = ctxWithDispatcher[CoroutineExceptionHandler]) {
- is UncaughtExceptionCaptor -> exceptionHandler
+ is TestCoroutineExceptionHandler -> exceptionHandler
null -> ownExceptionHandler
is TestCoroutineScopeExceptionHandler -> ownExceptionHandler
else -> throw IllegalArgumentException(
@@ -210,10 +163,10 @@
* the exception handler, instead of failing. */
private interface TestCoroutineScopeExceptionHandler : CoroutineExceptionHandler
-private inline val CoroutineContext.delayController: DelayController?
+private inline val CoroutineContext.delayController: TestCoroutineDispatcher?
get() {
val handler = this[ContinuationInterceptor]
- return handler as? DelayController
+ return handler as? TestCoroutineDispatcher
}
@@ -226,32 +179,6 @@
get() = coroutineContext.delayController?.currentTime ?: testScheduler.currentTime
/**
- * Advances the [testScheduler][TestCoroutineScope.testScheduler] by [delayTimeMillis] and runs the tasks up to that
- * moment (inclusive).
- *
- * @see TestCoroutineScheduler.advanceTimeBy
- */
-@ExperimentalCoroutinesApi
-@Deprecated(
- "The name of this function is misleading: it not only advances the time, but also runs the tasks " +
- "scheduled *at* the ending moment.",
- ReplaceWith("this.testScheduler.apply { advanceTimeBy(delayTimeMillis); runCurrent() }"),
- DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public fun TestCoroutineScope.advanceTimeBy(delayTimeMillis: Long): Unit =
- when (val controller = coroutineContext.delayController) {
- null -> {
- testScheduler.advanceTimeBy(delayTimeMillis)
- testScheduler.runCurrent()
- }
- else -> {
- controller.advanceTimeBy(delayTimeMillis)
- Unit
- }
- }
-
-/**
* Advances the [testScheduler][TestCoroutineScope.testScheduler] to the point where there are no tasks remaining.
* @see TestCoroutineScheduler.advanceUntilIdle
*/
@@ -270,75 +197,3 @@
public fun TestCoroutineScope.runCurrent() {
coroutineContext.delayController?.runCurrent() ?: testScheduler.runCurrent()
}
-
-@ExperimentalCoroutinesApi
-@Deprecated(
- "The test coroutine scope isn't able to pause its dispatchers in the general case. " +
- "Only `TestCoroutineDispatcher` supports pausing; pause it directly, or use a dispatcher that is always " +
- "\"paused\", like `StandardTestDispatcher`.",
- ReplaceWith(
- "(this.coroutineContext[ContinuationInterceptor]!! as DelayController).pauseDispatcher(block)",
- "kotlin.coroutines.ContinuationInterceptor"
- ),
- DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public suspend fun TestCoroutineScope.pauseDispatcher(block: suspend () -> Unit) {
- delayControllerForPausing.pauseDispatcher(block)
-}
-
-@ExperimentalCoroutinesApi
-@Deprecated(
- "The test coroutine scope isn't able to pause its dispatchers in the general case. " +
- "Only `TestCoroutineDispatcher` supports pausing; pause it directly, or use a dispatcher that is always " +
- "\"paused\", like `StandardTestDispatcher`.",
- ReplaceWith(
- "(this.coroutineContext[ContinuationInterceptor]!! as DelayController).pauseDispatcher()",
- "kotlin.coroutines.ContinuationInterceptor"
- ),
- level = DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public fun TestCoroutineScope.pauseDispatcher() {
- delayControllerForPausing.pauseDispatcher()
-}
-
-@ExperimentalCoroutinesApi
-@Deprecated(
- "The test coroutine scope isn't able to pause its dispatchers in the general case. " +
- "Only `TestCoroutineDispatcher` supports pausing; pause it directly, or use a dispatcher that is always " +
- "\"paused\", like `StandardTestDispatcher`.",
- ReplaceWith(
- "(this.coroutineContext[ContinuationInterceptor]!! as DelayController).resumeDispatcher()",
- "kotlin.coroutines.ContinuationInterceptor"
- ),
- level = DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public fun TestCoroutineScope.resumeDispatcher() {
- delayControllerForPausing.resumeDispatcher()
-}
-
-/**
- * List of uncaught coroutine exceptions, for backward compatibility.
- *
- * The returned list is a copy of the exceptions caught during execution.
- * During [TestCoroutineScope.cleanupTestCoroutines] the first element of this list is rethrown if it is not empty.
- *
- * Exceptions are only collected in this list if the [UncaughtExceptionCaptor] is in the test context.
- */
-@Deprecated(
- "This list is only populated if `UncaughtExceptionCaptor` is in the test context, and so can be " +
- "easily misused. It is only present for backward compatibility and will be removed in the subsequent " +
- "releases. If you need to check the list of exceptions, please consider creating your own " +
- "`CoroutineExceptionHandler`.",
- level = DeprecationLevel.ERROR
-)
-// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
-public val TestCoroutineScope.uncaughtExceptions: List<Throwable>
- get() = (coroutineContext[CoroutineExceptionHandler] as? UncaughtExceptionCaptor)?.uncaughtExceptions
- ?: emptyList()
-
-private val TestCoroutineScope.delayControllerForPausing: DelayController
- get() = coroutineContext.delayController
- ?: throw IllegalStateException("This scope isn't able to pause its dispatchers")
diff --git a/kotlinx-coroutines-test/jvm/test/MultithreadingTest.kt b/kotlinx-coroutines-test/jvm/test/MultithreadingTest.kt
index 4761769..3df302e 100644
--- a/kotlinx-coroutines-test/jvm/test/MultithreadingTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/MultithreadingTest.kt
@@ -4,6 +4,7 @@
import kotlin.coroutines.*
import kotlin.test.*
+@Suppress("DEPRECATION", "DEPRECATION_ERROR")
class MultithreadingTest {
@Test
diff --git a/kotlinx-coroutines-test/jvm/test/migration/RunBlockingTestOnTestScopeTest.kt b/kotlinx-coroutines-test/jvm/test/migration/RunBlockingTestOnTestScopeTest.kt
index aec937d..0e8f3f7 100644
--- a/kotlinx-coroutines-test/jvm/test/migration/RunBlockingTestOnTestScopeTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/migration/RunBlockingTestOnTestScopeTest.kt
@@ -8,7 +8,7 @@
import kotlin.test.assertFailsWith
/** Copy of [RunTestTest], but for [runBlockingTestOnTestScope], where applicable. */
-@Suppress("DEPRECATION")
+@Suppress("DEPRECATION", "DEPRECATION_ERROR")
class RunBlockingTestOnTestScopeTest {
@Test
diff --git a/kotlinx-coroutines-test/jvm/test/migration/RunTestLegacyScopeTest.kt b/kotlinx-coroutines-test/jvm/test/migration/RunTestLegacyScopeTest.kt
index caae1a5..379abdc 100644
--- a/kotlinx-coroutines-test/jvm/test/migration/RunTestLegacyScopeTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/migration/RunTestLegacyScopeTest.kt
@@ -9,7 +9,7 @@
import kotlin.test.assertFailsWith
/** Copy of [RunTestTest], but for [TestCoroutineScope] */
-@Suppress("DEPRECATION")
+@Suppress("DEPRECATION", "DEPRECATION_ERROR")
class RunTestLegacyScopeTest {
@Test
diff --git a/kotlinx-coroutines-test/jvm/test/migration/TestBuildersTest.kt b/kotlinx-coroutines-test/jvm/test/migration/TestBuildersTest.kt
index d58d0f5..6973f19 100644
--- a/kotlinx-coroutines-test/jvm/test/migration/TestBuildersTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/migration/TestBuildersTest.kt
@@ -76,32 +76,6 @@
}
@Test
- fun whenInAsync_runBlocking_nestsProperly() {
- // this is not a supported use case, but it is possible so ensure it works
-
- val dispatcher = TestCoroutineDispatcher()
- val scope = TestCoroutineScope(dispatcher)
- val deferred = scope.async {
- delay(1_000)
- var retval = 2
- runBlockingTest {
- delay(1_000)
- retval++
- }
- retval
- }
-
- scope.advanceTimeBy(1_000)
- scope.launch {
- assertRunsFast {
- assertEquals(3, deferred.getCompleted())
- }
- }
- scope.runCurrent() // execute the launch without changing to immediate dispatch (testing internals)
- scope.cleanupTestCoroutines()
- }
-
- @Test
fun whenInRunBlocking_runBlockingTest_nestsProperly() {
// this is not a supported use case, but it is possible so ensure it works
diff --git a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineDispatcherOrderTest.kt b/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineDispatcherOrderTest.kt
deleted file mode 100644
index 93792b5..0000000
--- a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineDispatcherOrderTest.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package kotlinx.coroutines.test
-
-import kotlinx.coroutines.testing.*
-import kotlinx.atomicfu.*
-import kotlinx.coroutines.*
-import kotlin.test.*
-
-@Suppress("DEPRECATION", "DEPRECATION_ERROR")
-class TestCoroutineDispatcherOrderTest: OrderedExecutionTestBase() {
-
- @Test
- fun testAdvanceTimeBy_progressesOnEachDelay() {
- val dispatcher = TestCoroutineDispatcher()
- val scope = TestCoroutineScope(dispatcher)
-
- expect(1)
- scope.launch {
- expect(2)
- delay(1_000)
- assertEquals(1_000, dispatcher.currentTime)
- expect(4)
- delay(5_00)
- assertEquals(1_500, dispatcher.currentTime)
- expect(5)
- delay(501)
- assertEquals(2_001, dispatcher.currentTime)
- expect(7)
- }
- expect(3)
- assertEquals(0, dispatcher.currentTime)
- dispatcher.advanceTimeBy(2_000)
- expect(6)
- assertEquals(2_000, dispatcher.currentTime)
- dispatcher.advanceTimeBy(2)
- expect(8)
- assertEquals(2_002, dispatcher.currentTime)
- scope.cleanupTestCoroutines()
- finish(9)
- }
-}
diff --git a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineDispatcherTest.kt b/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineDispatcherTest.kt
index d5f3e0f..eee5eea 100644
--- a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineDispatcherTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineDispatcherTest.kt
@@ -5,17 +5,6 @@
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
class TestCoroutineDispatcherTest {
- @Test
- fun whenDispatcherPaused_doesNotAutoProgressCurrent() {
- val subject = TestCoroutineDispatcher()
- subject.pauseDispatcher()
- val scope = CoroutineScope(subject)
- var executed = 0
- scope.launch {
- executed++
- }
- assertEquals(0, executed)
- }
@Test
fun whenDispatcherResumed_doesAutoProgressCurrent() {
@@ -44,30 +33,4 @@
assertEquals(1, executed)
}
- @Test
- fun whenDispatcherPaused_thenResume_itDoesDispatchCurrent() {
- val subject = TestCoroutineDispatcher()
- subject.pauseDispatcher()
- val scope = CoroutineScope(subject)
- var executed = 0
- scope.launch {
- executed++
- }
-
- assertEquals(0, executed)
- subject.resumeDispatcher()
- assertEquals(1, executed)
- }
-
- @Test
- fun whenDispatcherHasUncompletedCoroutines_itThrowsErrorInCleanup() {
- val subject = TestCoroutineDispatcher()
- subject.pauseDispatcher()
- val scope = CoroutineScope(subject)
- scope.launch {
- delay(1_000)
- }
- assertFailsWith<UncompletedCoroutinesError> { subject.cleanupTestCoroutines() }
- }
-
}
diff --git a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineExceptionHandlerTest.kt b/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineExceptionHandlerTest.kt
deleted file mode 100644
index 72fae5d..0000000
--- a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineExceptionHandlerTest.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package kotlinx.coroutines.test
-
-import kotlin.test.*
-
-@Suppress("DEPRECATION_ERROR")
-class TestCoroutineExceptionHandlerTest {
- @Test
- fun whenExceptionsCaught_availableViaProperty() {
- val subject = TestCoroutineExceptionHandler()
- val expected = IllegalArgumentException()
- subject.handleException(subject, expected)
- assertEquals(listOf(expected), subject.uncaughtExceptions)
- }
-}
diff --git a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineScopeTest.kt b/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineScopeTest.kt
index c201623..d275556 100644
--- a/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineScopeTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/migration/TestCoroutineScopeTest.kt
@@ -1,4 +1,4 @@
-@file:Suppress("DEPRECATION")
+@file:Suppress("DEPRECATION", "DEPRECATION_ERROR")
package kotlinx.coroutines.test
diff --git a/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingOrderTest.kt b/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingOrderTest.kt
index a8315fc..5ba1216 100644
--- a/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingOrderTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingOrderTest.kt
@@ -4,7 +4,7 @@
import kotlinx.coroutines.*
import kotlin.test.*
-@Suppress("DEPRECATION")
+@Suppress("DEPRECATION", "DEPRECATION_ERROR")
class TestRunBlockingOrderTest: OrderedExecutionTestBase() {
@Test
@@ -74,4 +74,4 @@
}
finish(2)
}
-}
\ No newline at end of file
+}
diff --git a/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingTest.kt b/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingTest.kt
index f7d420d..a116386 100644
--- a/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingTest.kt
+++ b/kotlinx-coroutines-test/jvm/test/migration/TestRunBlockingTest.kt
@@ -237,7 +237,7 @@
delay(SLOW)
executed = true
}
- advanceTimeBy(SLOW)
+ delay(SLOW)
assertTrue(deferred.isCompleted)
assertTrue(executed)
@@ -317,49 +317,6 @@
}
@Test
- fun pauseDispatcher_disablesAutoAdvance_forCurrent() = runBlockingTest {
- var mutable = 0
- pauseDispatcher {
- launch {
- mutable++
- }
- assertEquals(0, mutable)
- runCurrent()
- assertEquals(1, mutable)
- }
- }
-
- @Test
- fun pauseDispatcher_disablesAutoAdvance_forDelay() = runBlockingTest {
- var mutable = 0
- pauseDispatcher {
- launch {
- mutable++
- delay(SLOW)
- mutable++
- }
- assertEquals(0, mutable)
- runCurrent()
- assertEquals(1, mutable)
- advanceTimeBy(SLOW)
- assertEquals(2, mutable)
- }
- }
-
- @Test
- fun pauseDispatcher_withDelay_resumesAfterPause() = runBlockingTest {
- var mutable = 0
- assertRunsFast {
- pauseDispatcher {
- delay(1_000)
- mutable++
- }
- }
- assertEquals(1, mutable)
- }
-
-
- @Test
fun testWithTestContextThrowingAnAssertionError() {
assertFailsWith<TestException> {
runBlockingTest {
diff --git a/kotlinx-coroutines-test/wasmJs/src/TestBuilders.kt b/kotlinx-coroutines-test/wasmJs/src/TestBuilders.kt
index 7794b8e..ed9f9c9 100644
--- a/kotlinx-coroutines-test/wasmJs/src/TestBuilders.kt
+++ b/kotlinx-coroutines-test/wasmJs/src/TestBuilders.kt
@@ -1,15 +1,16 @@
package kotlinx.coroutines.test
+
import kotlinx.coroutines.*
+import kotlinx.coroutines.test.internal.*
import kotlin.js.*
-@Suppress("ACTUAL_WITHOUT_EXPECT", "ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE")
-public actual typealias TestResult = Promise<JsAny?>
-
-internal actual fun systemPropertyImpl(name: String): String? = null
+public actual typealias TestResult = JsPromiseInterfaceForTesting
internal actual fun createTestResult(testProcedure: suspend CoroutineScope.() -> Unit): TestResult =
GlobalScope.promise {
testProcedure()
- }
+ }.unsafeCast()
-internal actual fun dumpCoroutines() { }
\ No newline at end of file
+internal actual fun dumpCoroutines() { }
+
+internal actual fun systemPropertyImpl(name: String): String? = null
diff --git a/kotlinx-coroutines-test/wasmJs/src/internal/JsPromiseInterfaceForTesting.kt b/kotlinx-coroutines-test/wasmJs/src/internal/JsPromiseInterfaceForTesting.kt
new file mode 100644
index 0000000..e6697db
--- /dev/null
+++ b/kotlinx-coroutines-test/wasmJs/src/internal/JsPromiseInterfaceForTesting.kt
@@ -0,0 +1,19 @@
+package kotlinx.coroutines.test.internal
+
+/* This is a declaration of JS's `Promise<Unit>`. We need to keep it a separate class, because
+`actual typealias TestResult = Promise<Unit>` fails: you can't instantiate an `expect class` with a typealias to
+a parametric class. So, we make a non-parametric class just for this. */
+/**
+ * @suppress
+ */
+@JsName("Promise")
+public external class JsPromiseInterfaceForTesting {
+ /**
+ * @suppress
+ */
+ public fun then(onFulfilled: ((JsAny) -> Unit), onRejected: ((JsAny) -> Unit)): JsPromiseInterfaceForTesting
+ /**
+ * @suppress
+ */
+ public fun then(onFulfilled: ((JsAny) -> Unit)): JsPromiseInterfaceForTesting
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-test/wasmJs/src/internal/TestMainDispatcher.kt b/kotlinx-coroutines-test/wasmJs/src/internal/TestMainDispatcher.kt
index 175a846..be0b168 100644
--- a/kotlinx-coroutines-test/wasmJs/src/internal/TestMainDispatcher.kt
+++ b/kotlinx-coroutines-test/wasmJs/src/internal/TestMainDispatcher.kt
@@ -1,7 +1,7 @@
package kotlinx.coroutines.test.internal
import kotlinx.coroutines.*
-@Suppress("INVISIBLE_MEMBER")
+@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
internal actual fun Dispatchers.getTestMainDispatcher(): TestMainDispatcher =
when (val mainDispatcher = Main) {
is TestMainDispatcher -> mainDispatcher
diff --git a/kotlinx-coroutines-test/wasmWasi/src/TestBuilders.kt b/kotlinx-coroutines-test/wasmWasi/src/TestBuilders.kt
new file mode 100644
index 0000000..7c407d3
--- /dev/null
+++ b/kotlinx-coroutines-test/wasmWasi/src/TestBuilders.kt
@@ -0,0 +1,15 @@
+package kotlinx.coroutines.test
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.internal.*
+import kotlin.coroutines.*
+
+@Suppress("ACTUAL_WITHOUT_EXPECT")
+public actual typealias TestResult = Unit
+
+internal actual fun systemPropertyImpl(name: String): String? = null
+
+internal actual fun createTestResult(testProcedure: suspend CoroutineScope.() -> Unit) =
+ runTestCoroutine(EmptyCoroutineContext, testProcedure)
+
+internal actual fun dumpCoroutines() { }
\ No newline at end of file
diff --git a/kotlinx-coroutines-test/wasmWasi/src/internal/TestMainDispatcher.kt b/kotlinx-coroutines-test/wasmWasi/src/internal/TestMainDispatcher.kt
new file mode 100644
index 0000000..1419f67
--- /dev/null
+++ b/kotlinx-coroutines-test/wasmWasi/src/internal/TestMainDispatcher.kt
@@ -0,0 +1,9 @@
+package kotlinx.coroutines.test.internal
+import kotlinx.coroutines.*
+
+@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+internal actual fun Dispatchers.getTestMainDispatcher(): TestMainDispatcher =
+ when (val mainDispatcher = Main) {
+ is TestMainDispatcher -> mainDispatcher
+ else -> TestMainDispatcher(mainDispatcher).also { injectMain(it) }
+ }
\ No newline at end of file
diff --git a/kotlinx-coroutines-test/wasmWasi/test/Helpers.kt b/kotlinx-coroutines-test/wasmWasi/test/Helpers.kt
new file mode 100644
index 0000000..ed3afa3
--- /dev/null
+++ b/kotlinx-coroutines-test/wasmWasi/test/Helpers.kt
@@ -0,0 +1,10 @@
+package kotlinx.coroutines.test
+
+actual fun testResultChain(block: () -> TestResult, after: (Result<Unit>) -> TestResult): TestResult {
+ try {
+ block()
+ after(Result.success(Unit))
+ } catch (e: Throwable) {
+ after(Result.failure(e))
+ }
+}
diff --git a/patches/b-271358556_K2_compilation.patch b/patches/b-271358556_K2_compilation.patch
new file mode 100644
index 0000000..a01a5df
--- /dev/null
+++ b/patches/b-271358556_K2_compilation.patch
@@ -0,0 +1,29 @@
+--- a/kotlinx-coroutines-core/concurrent/src/internal/LockFreeLinkedList.kt
++++ b/kotlinx-coroutines-core/concurrent/src/internal/LockFreeLinkedList.kt
+@@ -1,4 +1,4 @@
+-@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE")
++@file:Suppress("NO_EXPLICIT_VISIBILITY_IN_API_MODE", "ACTUAL_WITHOUT_EXPECT")
+
+ package kotlinx.coroutines.internal
+
+--- a/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt
++++ b/kotlinx-coroutines-core/jvm/src/internal/Concurrent.kt
+@@ -8,7 +8,7 @@ internal actual typealias ReentrantLock = java.util.concurrent.locks.ReentrantLo
+
+ internal actual inline fun <T> ReentrantLock.withLock(action: () -> T) = this.withLockJvm(action)
+
+-@Suppress("ACTUAL_WITHOUT_EXPECT") // Visibility
++@Suppress("ACTUAL_WITHOUT_EXPECT", "NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // Visibility
+ internal actual typealias WorkaroundAtomicReference<T> = java.util.concurrent.atomic.AtomicReference<T>
+
+ // BenignDataRace is OptionalExpectation and doesn't have to be here
+--- a/test-utils/jvm/src/TestBase.kt
++++ b/test-utils/jvm/src/TestBase.kt
+@@ -57,6 +57,7 @@ internal actual fun lastResortReportException(error: Throwable) {
+ * }
+ * ```
+ */
++@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS")
+ actual open class TestBase(
+ private var disableOutCheck: Boolean,
+ private val errorCatching: ErrorCatching.Impl = ErrorCatching.Impl()
diff --git a/reactive/README.md b/reactive/README.md
index 35706ac..d7f234c 100644
--- a/reactive/README.md
+++ b/reactive/README.md
@@ -7,5 +7,5 @@
* [kotlinx-coroutines-reactive](kotlinx-coroutines-reactive/README.md) -- utilities for [Reactive Streams](https://www.reactive-streams.org)
* [kotlinx-coroutines-reactor](kotlinx-coroutines-reactor/README.md) -- utilities for [Reactor](https://projectreactor.io)
-* [kotlinx-coroutines-rx2](kotlinx-coroutines-rx2/README.md) -- utilities for [RxJava 2.x](https://github.com/ReactiveX/RxJava)
+* [kotlinx-coroutines-rx2](kotlinx-coroutines-rx2/README.md) -- utilities for [RxJava 2.x](https://github.com/ReactiveX/RxJava/tree/2.x)
* [kotlinx-coroutines-rx3](kotlinx-coroutines-rx3/README.md) -- utilities for [RxJava 3.x](https://github.com/ReactiveX/RxJava)
diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt
index db464c8..44541ee 100644
--- a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt
+++ b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt
@@ -26,7 +26,7 @@
*
* // Now add ReactorContext
* withContext(Context.of("answer", "42").asCoroutineContext()) {
- * flux.awaitFirst() // Will print "Context{'key'='value'}"
+ * flux.awaitFirst() // Will print "Context{'key'='value'}"
* }
* ```
*
diff --git a/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt b/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt
index d780f51..5371ff3 100644
--- a/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt
+++ b/reactive/kotlinx-coroutines-reactor/src/Scheduler.kt
@@ -47,6 +47,4 @@
}
private fun Disposable.asDisposableHandle(): DisposableHandle =
- object : DisposableHandle {
- override fun dispose() = [email protected]()
- }
+ DisposableHandle { [email protected]() }
diff --git a/test-utils/build.gradle.kts b/test-utils/build.gradle.kts
index 66074fb..3461d52 100644
--- a/test-utils/build.gradle.kts
+++ b/test-utils/build.gradle.kts
@@ -24,5 +24,10 @@
api("org.jetbrains.kotlin:kotlin-test-wasm-js:${version("kotlin")}")
}
}
+ val wasmWasiMain by getting {
+ dependencies {
+ api("org.jetbrains.kotlin:kotlin-test-wasm-wasi:${version("kotlin")}")
+ }
+ }
}
}
diff --git a/test-utils/common/src/TestBase.common.kt b/test-utils/common/src/TestBase.common.kt
index 5c0cba4..c1c84e3 100644
--- a/test-utils/common/src/TestBase.common.kt
+++ b/test-utils/common/src/TestBase.common.kt
@@ -206,6 +206,12 @@
@OptionalExpectation
expect annotation class NoNative()
+@OptionalExpectation
+expect annotation class NoWasmJs()
+
+@OptionalExpectation
+expect annotation class NoWasmWasi()
+
expect val isStressTest: Boolean
expect val stressTestMultiplier: Int
expect val stressTestMultiplierSqrt: Int
@@ -225,6 +231,9 @@
unhandled: List<(Throwable) -> Boolean> = emptyList(),
block: suspend CoroutineScope.() -> Unit
): TestResult
+
+ override fun hasError(): Boolean
+ override fun reportError(error: Throwable)
}
public suspend inline fun hang(onCancellation: () -> Unit) {
@@ -252,6 +261,7 @@
public class RecoverableTestException(message: String? = null) : RuntimeException(message)
public class RecoverableTestCancellationException(message: String? = null) : CancellationException(message)
+// Erases identity and equality checks for tests
public fun wrapperDispatcher(context: CoroutineContext): CoroutineContext {
val dispatcher = context[ContinuationInterceptor] as CoroutineDispatcher
return object : CoroutineDispatcher() {
@@ -282,3 +292,8 @@
* and run such tests only on JVM and K/N.
*/
public expect val isBoundByJsTestTimeout: Boolean
+
+/**
+ * `true` if this platform has the same event loop for `DefaultExecutor` and [Dispatchers.Unconfined]
+ */
+public expect val usesSharedEventLoop: Boolean
diff --git a/test-utils/js/src/TestBase.kt b/test-utils/js/src/TestBase.kt
index 61887af..c6223e2 100644
--- a/test-utils/js/src/TestBase.kt
+++ b/test-utils/js/src/TestBase.kt
@@ -12,8 +12,14 @@
actual val stressTestMultiplier: Int = 1
actual val stressTestMultiplierSqrt: Int = 1
-@Suppress("ACTUAL_WITHOUT_EXPECT", "ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE")
-actual typealias TestResult = Promise<Unit>
+@JsName("Promise")
+external class MyPromise {
+ fun then(onFulfilled: ((Unit) -> Unit), onRejected: ((Throwable) -> Unit)): MyPromise
+ fun then(onFulfilled: ((Unit) -> Unit)): MyPromise
+}
+
+/** Always a `Promise<Unit>` */
+public actual typealias TestResult = MyPromise
internal actual fun lastResortReportException(error: Throwable) {
println(error)
@@ -86,7 +92,8 @@
checkFinishCall()
}
lastTestPromise = result
- return result
+ @Suppress("CAST_NEVER_SUCCEEDS")
+ return result as MyPromise
}
}
@@ -95,3 +102,5 @@
actual val isBoundByJsTestTimeout = true
actual val isJavaAndWindows: Boolean get() = false
+
+actual val usesSharedEventLoop: Boolean = false
diff --git a/test-utils/jvm/src/TestBase.kt b/test-utils/jvm/src/TestBase.kt
index 31ccd7e..2d897d2 100644
--- a/test-utils/jvm/src/TestBase.kt
+++ b/test-utils/jvm/src/TestBase.kt
@@ -44,19 +44,20 @@
*
* ```
* class MyTest : TestBase() {
- * @Test
- * fun testSomething() = runBlocking { // run in the context of the main thread
- * expect(1) // initiate action counter
- * launch { // use the context of the main thread
- * expect(3) // the body of this coroutine in going to be executed in the 3rd step
- * }
- * expect(2) // launch just scheduled coroutine for execution later, so this line is executed second
- * yield() // yield main thread to the launched job
- * finish(4) // fourth step is the last one. `finish` must be invoked or test fails
- * }
+ * @Test
+ * fun testSomething() = runBlocking { // run in the context of the main thread
+ * expect(1) // initiate action counter
+ * launch { // use the context of the main thread
+ * expect(3) // the body of this coroutine in going to be executed in the 3rd step
+ * }
+ * expect(2) // launch just scheduled coroutine for execution later, so this line is executed second
+ * yield() // yield main thread to the launched job
+ * finish(4) // fourth step is the last one. `finish` must be invoked or test fails
+ * }
* }
* ```
*/
+@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS")
actual open class TestBase(
private var disableOutCheck: Boolean,
private val errorCatching: ErrorCatching.Impl = ErrorCatching.Impl()
@@ -186,3 +187,5 @@
* which makes such tests flaky.
*/
actual val isJavaAndWindows: Boolean = System.getProperty("os.name")!!.contains("Windows")
+
+actual val usesSharedEventLoop: Boolean = false
diff --git a/test-utils/native/src/TestBase.kt b/test-utils/native/src/TestBase.kt
index d0e0c1a..eac3dde 100644
--- a/test-utils/native/src/TestBase.kt
+++ b/test-utils/native/src/TestBase.kt
@@ -61,3 +61,5 @@
public actual val isBoundByJsTestTimeout = false
public actual val isJavaAndWindows: Boolean get() = false
+
+actual val usesSharedEventLoop: Boolean = false
diff --git a/test-utils/wasmJs/src/TestBase.kt b/test-utils/wasmJs/src/TestBase.kt
index 0edd291..021dc5e 100644
--- a/test-utils/wasmJs/src/TestBase.kt
+++ b/test-utils/wasmJs/src/TestBase.kt
@@ -6,14 +6,20 @@
actual val VERBOSE = false
-actual typealias NoJs = Ignore
+actual typealias NoWasmJs = Ignore
actual val isStressTest: Boolean = false
actual val stressTestMultiplier: Int = 1
actual val stressTestMultiplierSqrt: Int = 1
-@Suppress("ACTUAL_WITHOUT_EXPECT", "ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE")
-actual typealias TestResult = Promise<JsAny?>
+@JsName("Promise")
+external class MyPromise : JsAny {
+ fun then(onFulfilled: ((JsAny?) -> Unit), onRejected: ((JsAny) -> Unit)): MyPromise
+ fun then(onFulfilled: ((JsAny?) -> Unit)): MyPromise
+}
+
+/** Always a `Promise<Unit>` */
+public actual typealias TestResult = MyPromise
internal actual fun lastResortReportException(error: Throwable) {
println(error)
@@ -87,7 +93,7 @@
checkFinishCall()
}
lastTestPromise = result
- return result
+ return result.unsafeCast()
}
}
@@ -96,3 +102,5 @@
actual val isBoundByJsTestTimeout = true
actual val isJavaAndWindows: Boolean get() = false
+
+actual val usesSharedEventLoop: Boolean = false
diff --git a/test-utils/wasmWasi/src/TestBase.kt b/test-utils/wasmWasi/src/TestBase.kt
new file mode 100644
index 0000000..795ed37
--- /dev/null
+++ b/test-utils/wasmWasi/src/TestBase.kt
@@ -0,0 +1,70 @@
+package kotlinx.coroutines.testing
+
+import kotlin.test.*
+import kotlinx.coroutines.*
+import kotlinx.coroutines.internal.*
+
+actual val VERBOSE = false
+
+actual typealias NoWasmWasi = Ignore
+
+actual val isStressTest: Boolean = false
+actual val stressTestMultiplier: Int = 1
+actual val stressTestMultiplierSqrt: Int = 1
+
+actual typealias TestResult = Unit
+
+internal actual fun lastResortReportException(error: Throwable) {
+ println(error)
+}
+
+actual open class TestBase(
+ private val errorCatching: ErrorCatching.Impl
+): OrderedExecutionTestBase(), ErrorCatching by errorCatching {
+
+ actual constructor(): this(errorCatching = ErrorCatching.Impl())
+
+ actual fun println(message: Any?) {
+ kotlin.io.println(message)
+ }
+
+ public actual fun runTest(
+ expected: ((Throwable) -> Boolean)?,
+ unhandled: List<(Throwable) -> Boolean>,
+ block: suspend CoroutineScope.() -> Unit
+ ): TestResult {
+ var exCount = 0
+ var ex: Throwable? = null
+ try {
+ runTestCoroutine(block = block, context = CoroutineExceptionHandler { _, e ->
+ if (e is CancellationException) return@CoroutineExceptionHandler // are ignored
+ exCount++
+ when {
+ exCount > unhandled.size ->
+ error("Too many unhandled exceptions $exCount, expected ${unhandled.size}, got: $e", e)
+ !unhandled[exCount - 1](e) ->
+ error("Unhandled exception was unexpected: $e", e)
+ }
+ })
+ } catch (e: Throwable) {
+ ex = e
+ if (expected != null) {
+ if (!expected(e))
+ error("Unexpected exception: $e", e)
+ } else
+ throw e
+ } finally {
+ if (ex == null && expected != null) kotlin.error("Exception was expected but none produced")
+ }
+ if (exCount < unhandled.size)
+ kotlin.error("Too few unhandled exceptions $exCount, expected ${unhandled.size}")
+ }
+}
+
+actual val isNative = false
+
+actual val isBoundByJsTestTimeout = true
+
+actual val isJavaAndWindows: Boolean get() = false
+
+actual val usesSharedEventLoop: Boolean = true
diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md
index 3b2087d..e0fc6a1 100644
--- a/ui/coroutines-guide-ui.md
+++ b/ui/coroutines-guide-ui.md
@@ -110,7 +110,7 @@
`app/build.gradle` file:
```groovy
-implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"
+implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
```
You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your
diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt
index 5ce3315..9261b2e 100644
--- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt
+++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt
@@ -27,18 +27,18 @@
* Example of usage:
* ```
* suspend fun updateUiElement(val text: String) {
- * /*
- * * If it is known that updateUiElement can be invoked both from the Main thread and from other threads,
- * * `immediate` dispatcher is used as a performance optimization to avoid unnecessary dispatch.
- * *
- * * In that case, when `updateUiElement` is invoked from the Main thread, `uiElement.text` will be
- * * invoked immediately without any dispatching, otherwise, the `Dispatchers.Main` dispatch cycle via
- * * `Handler.post` will be triggered.
- * */
- * withContext(Dispatchers.Main.immediate) {
- * uiElement.text = text
- * }
- * // Do context-independent logic such as logging
+ * /*
+ * * If it is known that updateUiElement can be invoked both from the Main thread and from other threads,
+ * * `immediate` dispatcher is used as a performance optimization to avoid unnecessary dispatch.
+ * *
+ * * In that case, when `updateUiElement` is invoked from the Main thread, `uiElement.text` will be
+ * * invoked immediately without any dispatching, otherwise, the `Dispatchers.Main` dispatch cycle via
+ * * `Handler.post` will be triggered.
+ * */
+ * withContext(Dispatchers.Main.immediate) {
+ * uiElement.text = text
+ * }
+ * // Do context-independent logic such as logging
* }
* ```
*/