From defd0ec493ebe7b6649892d8f1060fa9e0e602dc Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Tue, 9 Mar 2021 12:02:40 +0300 Subject: [PATCH 01/55] Fix a test that was flaky because of BlockHound (#2572) `kotlinx.coroutines.debug.internal.DebugProbesImpl.updateState` sometimes parks in order to acquire a lock, which leads BlockHound to detect an illegal blocking call. Now, blocking calls inside `DebugProbesImpl` are permitted. Though allowing `updateState` would suffice, the other users of the lock seem safe, too, and are allowed even when it doesn't make much sense to call them from coroutines. --- .../src/CoroutinesBlockHoundIntegration.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt b/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt index 7dd7d58d21..190476c41a 100644 --- a/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt +++ b/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt @@ -18,6 +18,7 @@ public class CoroutinesBlockHoundIntegration : BlockHoundIntegration { allowBlockingWhenEnqueuingTasks() allowServiceLoaderInvocationsOnInit() allowBlockingCallsInReflectionImpl() + allowBlockingCallsInDebugProbes() /* The predicates that define that BlockHound should only report blocking calls from threads that are part of the coroutine thread pool and currently execute a CPU-bound coroutine computation. */ addDynamicThreadPredicate { isSchedulerWorker(it) } @@ -48,6 +49,17 @@ public class CoroutinesBlockHoundIntegration : BlockHoundIntegration { } } + /** + * Allow blocking calls inside [kotlinx.coroutines.debug.internal.DebugProbesImpl]. + */ + private fun BlockHound.Builder.allowBlockingCallsInDebugProbes() { + for (method in listOf("install", "uninstall", "hierarchyToString", "dumpCoroutinesInfo", "dumpDebuggerInfo", + "dumpCoroutinesSynchronized", "updateRunningState", "updateState")) + { + allowBlockingCallsInside("kotlinx.coroutines.debug.internal.DebugProbesImpl", method) + } + } + /** * Allows blocking inside [kotlinx.coroutines.internal.ThreadSafeHeap]. */ From a8d55d6c93cf1e7c9b06e6833282e6d46d704ecd Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Tue, 9 Mar 2021 16:38:59 +0100 Subject: [PATCH 02/55] Replace deprecated '*compile' configurations. (#2574) They were deprecated long time ago and were removed in Gradle 7 --- build.gradle | 4 ++-- .../src/main/kotlin/kotlin-jvm-conventions.gradle.kts | 10 +++++----- integration-testing/build.gradle | 8 ++++---- kotlinx-coroutines-debug/build.gradle | 2 +- reactive/kotlinx-coroutines-rx2/build.gradle | 8 ++++---- reactive/kotlinx-coroutines-rx3/build.gradle | 8 ++++---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 80124e7ea0..171b16b68c 100644 --- a/build.gradle +++ b/build.gradle @@ -163,9 +163,9 @@ configure(subprojects.findAll { !sourceless.contains(it.name) && it.name != core apply plugin: "kotlin-${platform}-conventions" dependencies { // See comment below for rationale, it will be replaced with "project" dependency - compile project(":$coreModule") + api project(":$coreModule") // the only way IDEA can resolve test classes - testCompile project(":$coreModule").kotlin.targets.jvm.compilations.test.output.allOutputs + testImplementation project(":$coreModule").kotlin.targets.jvm.compilations.test.output.allOutputs } } diff --git a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts index 89007718c8..5f3193428c 100644 --- a/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/kotlin-jvm-conventions.gradle.kts @@ -22,12 +22,12 @@ if (rootProject.extra.get("jvm_ir_enabled") as Boolean) { } dependencies { - testCompile(kotlin("test")) + testImplementation(kotlin("test")) // Workaround to make addSuppressed work in tests - testCompile(kotlin("reflect")) - testCompile(kotlin("stdlib-jdk7")) - testCompile(kotlin("test-junit")) - testCompile("junit:junit:${version("junit")}") + testImplementation(kotlin("reflect")) + testImplementation(kotlin("stdlib-jdk7")) + testImplementation(kotlin("test-junit")) + testImplementation("junit:junit:${version("junit")}") } tasks.compileKotlin { diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index c5a551a05f..6efa3a14e6 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -92,10 +92,10 @@ task coreAgentTest(type: Test) { } dependencies { - testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - testCompile 'junit:junit:4.12' - npmTestCompile 'org.apache.commons:commons-compress:1.18' - npmTestCompile 'com.google.code.gson:gson:2.8.5' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + testImplementation 'junit:junit:4.12' + npmTestImplementation 'org.apache.commons:commons-compress:1.18' + npmTestImplementation 'com.google.code.gson:gson:2.8.5' debugAgentTestCompile project(':kotlinx-coroutines-core') debugAgentTestCompile project(':kotlinx-coroutines-debug') coreAgentTestCompile project(':kotlinx-coroutines-core') diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index 46f894d1a4..faaed91206 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -23,7 +23,7 @@ dependencies { shadowDeps "net.bytebuddy:byte-buddy:$byte_buddy_version" shadowDeps "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" compileOnly "io.projectreactor.tools:blockhound:$blockhound_version" - testCompile "io.projectreactor.tools:blockhound:$blockhound_version" + testImplementation "io.projectreactor.tools:blockhound:$blockhound_version" api "net.java.dev.jna:jna:$jna_version" api "net.java.dev.jna:jna-platform:$jna_version" } diff --git a/reactive/kotlinx-coroutines-rx2/build.gradle b/reactive/kotlinx-coroutines-rx2/build.gradle index 73f76c3dfe..01c226a990 100644 --- a/reactive/kotlinx-coroutines-rx2/build.gradle +++ b/reactive/kotlinx-coroutines-rx2/build.gradle @@ -3,10 +3,10 @@ */ dependencies { - compile project(':kotlinx-coroutines-reactive') - testCompile project(':kotlinx-coroutines-reactive').sourceSets.test.output - testCompile "org.reactivestreams:reactive-streams-tck:$reactive_streams_version" - compile "io.reactivex.rxjava2:rxjava:$rxjava2_version" + api project(':kotlinx-coroutines-reactive') + testImplementation project(':kotlinx-coroutines-reactive').sourceSets.test.output + testImplementation "org.reactivestreams:reactive-streams-tck:$reactive_streams_version" + api "io.reactivex.rxjava2:rxjava:$rxjava2_version" } tasks.withType(dokka.getClass()) { diff --git a/reactive/kotlinx-coroutines-rx3/build.gradle b/reactive/kotlinx-coroutines-rx3/build.gradle index a5de40d8df..1760270d76 100644 --- a/reactive/kotlinx-coroutines-rx3/build.gradle +++ b/reactive/kotlinx-coroutines-rx3/build.gradle @@ -4,10 +4,10 @@ targetCompatibility = JavaVersion.VERSION_1_8 dependencies { - compile project(':kotlinx-coroutines-reactive') - testCompile project(':kotlinx-coroutines-reactive').sourceSets.test.output - testCompile "org.reactivestreams:reactive-streams-tck:$reactive_streams_version" - compile "io.reactivex.rxjava3:rxjava:$rxjava3_version" + api project(':kotlinx-coroutines-reactive') + testImplementation project(':kotlinx-coroutines-reactive').sourceSets.test.output + testImplementation "org.reactivestreams:reactive-streams-tck:$reactive_streams_version" + api "io.reactivex.rxjava3:rxjava:$rxjava3_version" } compileTestKotlin { From 187f0aa803361459deb8243fb09e8780216a0b48 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 15 Mar 2021 15:59:02 +0300 Subject: [PATCH 03/55] Properly reference coroutines-guide-ui.md when opening page from a website --- docs/topics/coroutines-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/coroutines-guide.md b/docs/topics/coroutines-guide.md index 239b2a451a..3e9fa90490 100644 --- a/docs/topics/coroutines-guide.md +++ b/docs/topics/coroutines-guide.md @@ -32,6 +32,6 @@ In order to use coroutines as well as follow the examples in this guide, you nee ## Additional references -* [Guide to UI programming with coroutines](../../ui/coroutines-guide-ui.md) +* [Guide to UI programming with coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md) * [Coroutines design document (KEEP)](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md) * [Full kotlinx.coroutines API reference](https://kotlin.github.io/kotlinx.coroutines) From 25a33d4e6359919e7596a7ef77c74c751aad0b38 Mon Sep 17 00:00:00 2001 From: Tobias Preuss Date: Tue, 16 Mar 2021 19:05:48 +0100 Subject: [PATCH 04/55] Fix docs for filter. (#2555) --- kotlinx-coroutines-core/common/src/flow/operators/Transform.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt index d163d9c09e..80a2cf27d4 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.internal.unsafeFlow as flow import kotlinx.coroutines.flow.unsafeTransform as transform /** - * Returns a flow containing only values of the original flow that matches the given [predicate]. + * Returns a flow containing only values of the original flow that match the given [predicate]. */ public inline fun Flow.filter(crossinline predicate: suspend (T) -> Boolean): Flow = transform { value -> if (predicate(value)) return@transform emit(value) From cbf3f244e8f16cc116fbb363940f34cf7ad162d2 Mon Sep 17 00:00:00 2001 From: Chao Zhang Date: Wed, 17 Mar 2021 01:56:22 -0700 Subject: [PATCH 05/55] Fix typo in flow/Builders.kt (#2589) --- kotlinx-coroutines-core/common/src/flow/Builders.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/common/src/flow/Builders.kt b/kotlinx-coroutines-core/common/src/flow/Builders.kt index 10dd3aefb2..3a43ccb7e0 100644 --- a/kotlinx-coroutines-core/common/src/flow/Builders.kt +++ b/kotlinx-coroutines-core/common/src/flow/Builders.kt @@ -293,7 +293,7 @@ public fun channelFlow(@BuilderInference block: suspend ProducerScope.() * Adjacent applications of [callbackFlow], [flowOn], [buffer], [produceIn], and [broadcastIn] are * always fused so that only one properly configured channel is used for execution. * - * Example of usage that converts a multi-short callback API to a flow. + * Example of usage that converts a multi-shot callback API to a flow. * For single-shot callbacks use [suspendCancellableCoroutine]. * * ``` From e15970b81e1bffac6f61e5babd08b61b15c727cd Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Fri, 19 Mar 2021 01:26:56 +0300 Subject: [PATCH 06/55] Fix Job.children docs (#2592) Fixes #2566 --- kotlinx-coroutines-core/common/src/Job.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt index 31e2ef22b2..c80463f61c 100644 --- a/kotlinx-coroutines-core/common/src/Job.kt +++ b/kotlinx-coroutines-core/common/src/Job.kt @@ -217,11 +217,10 @@ public interface Job : CoroutineContext.Element { * immediately cancels all its children. * * Parent cannot complete until all its children are complete. Parent waits for all its children to * complete in _completing_ or _cancelling_ state. - * * Uncaught exception in a child, by default, cancels parent. In particular, this applies to - * children created with [launch][CoroutineScope.launch] coroutine builder. Note that - * [async][CoroutineScope.async] and other future-like - * coroutine builders do not have uncaught exceptions by definition, since all their exceptions are - * caught and are encapsulated in their result. + * * Uncaught exception in a child, by default, cancels parent. This applies even to + * children created with [async][CoroutineScope.async] and other future-like + * coroutine builders, even though their exceptions are caught and are encapsulated in their result. + * This default behavior can be overridden with [SupervisorJob]. */ public val children: Sequence From 81e51a34a16cc9f65507f86ed7bded0e9f40afab Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 19 Mar 2021 01:27:20 +0300 Subject: [PATCH 07/55] Mention default buffering in flattenMerge documentation (#2583) Fixes #2546 --- kotlinx-coroutines-core/common/src/flow/operators/Merge.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt index 0427537558..d1cbe72b5b 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt @@ -129,6 +129,9 @@ public fun merge(vararg flows: Flow): Flow = flows.asIterable().merge( * Applications of [flowOn], [buffer], [produceIn], and [broadcastIn] _after_ this operator are fused with * its concurrent merging so that only one properly configured channel is used for execution of merging logic. * + * When [concurrency] is greater than 1, this operator is [buffered][buffer] by default + * and size of its output buffer can be changed by applying subsequent [buffer] operator. + * * @param concurrency controls the number of in-flight flows, at most [concurrency] flows are collected * at the same time. By default it is equal to [DEFAULT_CONCURRENCY]. */ From 6f13927ea3835f433ebfd309ed3767386ef420e7 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 22 Mar 2021 15:09:30 +0300 Subject: [PATCH 08/55] Do not initialize CoroutineExceptionHandler in Job (#2513) * It slightly cuts down startup time * It simplifies the code in general * It do not serve its purpose, coroutines are already protected from StackOverflowError with nested event-loops where applicable --- kotlinx-coroutines-core/common/src/Job.kt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt index c80463f61c..15e8a1bb3e 100644 --- a/kotlinx-coroutines-core/common/src/Job.kt +++ b/kotlinx-coroutines-core/common/src/Job.kt @@ -113,16 +113,7 @@ public interface Job : CoroutineContext.Element { /** * Key for [Job] instance in the coroutine context. */ - public companion object Key : CoroutineContext.Key { - init { - /* - * Here we make sure that CoroutineExceptionHandler is always initialized in advance, so - * that if a coroutine fails due to StackOverflowError we don't fail to report this error - * trying to initialize CoroutineExceptionHandler - */ - CoroutineExceptionHandler - } - } + public companion object Key : CoroutineContext.Key // ------------ state query ------------ From f2940d5d6d3177cc74ea1a5fb8b730b11d361181 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 22 Mar 2021 16:11:06 +0300 Subject: [PATCH 09/55] Update Gradle for Kotlin 1.5, add space-dev repository (#2598) * Update Gradle to 4.8.3, add space-dev repository * Update atomicfu and binary compatibility validator --- build.gradle | 1 + buildSrc/build.gradle.kts | 1 + gradle.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 171b16b68c..08d07782a7 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,7 @@ buildscript { } maven { url "https://jetbrains.bintray.com/kotlin-native-dependencies" } maven { url "https://plugins.gradle.org/m2/" } + maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } } dependencies { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 6c373a01f4..c763d424fd 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -19,6 +19,7 @@ repositories { maven("https://plugins.gradle.org/m2") maven("https://dl.bintray.com/kotlin/kotlin-dev") } + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") if (buildSnapshotTrain) { mavenLocal() diff --git a/gradle.properties b/gradle.properties index 40b1596105..0f3ccc1b55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ kotlin_version=1.4.30 # Dependencies junit_version=4.12 -atomicfu_version=0.15.1 +atomicfu_version=0.15.2 knit_version=0.2.3 html_version=0.6.8 lincheck_version=2.10 @@ -21,7 +21,7 @@ rxjava2_version=2.2.8 rxjava3_version=3.0.2 javafx_version=11.0.2 javafx_plugin_version=0.0.8 -binary_compatibility_validator_version=0.4.0 +binary_compatibility_validator_version=0.5.0 blockhound_version=1.0.2.RELEASE jna_version=5.5.0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3a9638d5b1..d7ae858e5a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,6 +4,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 05f7d5df2f6223c9f25027740251ff95a682863a Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 23 Mar 2021 12:22:30 +0300 Subject: [PATCH 10/55] Simplify internal coroutines machinery (#2512) * Merge onStartInternal and onStart to reduce the number of methods and make code a bit simpler * Rework initParentJob * Always establish a parent-child relationship when creating a subclass of AbstractCoroutine. That's our own internal class that we have full control of and it never has a chance to leak to the user-code (so cancellation handlers will be installed etc.). Force implementors of AbstractCoroutine deliberately choose whether parent-child relationship should be established * As a consequence, get rid of parentContext in all our coroutine classes that are not ScopeCoroutine * Remove some dead code * Get rid of an additional parent field from ScopeCoroutine Leverage already presented information in our implementation, just expose it via an already present internal interface --- .../src/ListenableFuture.kt | 2 +- .../test/ListenableFutureTest.kt | 10 ++- .../src/future/Future.kt | 2 +- .../test/future/FutureTest.kt | 10 ++- .../api/kotlinx-coroutines-core.api | 10 +-- .../common/src/AbstractCoroutine.kt | 68 ++++----------- .../common/src/Builders.common.kt | 5 +- .../common/src/CompletableDeferred.kt | 2 +- kotlinx-coroutines-core/common/src/Job.kt | 11 +++ .../common/src/JobSupport.kt | 13 +-- .../common/src/channels/Broadcast.kt | 8 +- .../common/src/channels/ChannelCoroutine.kt | 4 +- .../common/src/channels/Produce.kt | 2 +- .../common/src/internal/Scopes.kt | 7 +- .../common/src/intrinsics/Undispatched.kt | 8 +- .../common/test/AbstractCoroutineTest.kt | 8 +- .../common/test/CancelledParentAttachTest.kt | 85 +++++++++++++++++++ kotlinx-coroutines-core/jvm/src/Builders.kt | 3 +- .../jvm/src/channels/Actor.kt | 6 +- .../{JoinStrTest.kt => JoinStressTest.kt} | 2 +- .../jvm/test/RunBlockingTest.kt | 13 ++- .../jvm/test/channels/ActorLazyTest.kt | 14 ++- .../jvm/test/channels/ActorTest.kt | 17 +++- .../jvm/test/exceptions/SuppressionTests.kt | 8 +- .../native/src/Builders.kt | 2 +- .../src/Publish.kt | 2 +- .../src/ReactiveFlow.kt | 6 +- .../test/CancelledParentAttachTest.kt | 20 +++++ .../kotlinx-coroutines-reactor/src/Mono.kt | 2 +- .../src/RxCompletable.kt | 2 +- .../kotlinx-coroutines-rx2/src/RxMaybe.kt | 2 +- .../src/RxObservable.kt | 4 +- .../kotlinx-coroutines-rx2/src/RxSingle.kt | 2 +- .../src/RxCompletable.kt | 2 +- .../kotlinx-coroutines-rx3/src/RxMaybe.kt | 2 +- .../src/RxObservable.kt | 2 +- .../kotlinx-coroutines-rx3/src/RxSingle.kt | 2 +- 37 files changed, 259 insertions(+), 109 deletions(-) create mode 100644 kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt rename kotlinx-coroutines-core/jvm/test/{JoinStrTest.kt => JoinStressTest.kt} (97%) create mode 100644 reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt diff --git a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt index 53019c4bbe..35e0aeb379 100644 --- a/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt +++ b/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt @@ -299,7 +299,7 @@ private class ToContinuation( */ private class ListenableFutureCoroutine( context: CoroutineContext -) : AbstractCoroutine(context) { +) : AbstractCoroutine(context, initParentJob = true, active = true) { // JobListenableFuture propagates external cancellation to `this` coroutine. See JobListenableFuture. @JvmField diff --git a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt index 9dca9e9b46..c463174a8d 100644 --- a/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt +++ b/integration/kotlinx-coroutines-guava/test/ListenableFutureTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.guava @@ -747,4 +747,12 @@ class ListenableFutureTest : TestBase() { latch.countDown() return future } + + @Test + fun testCancelledParent() = runTest({ it is CancellationException }) { + cancel() + future { expectUnreached() } + future(start = CoroutineStart.ATOMIC) { } + future(start = CoroutineStart.UNDISPATCHED) { } + } } diff --git a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt index b3b45e9dbc..7e9c349c66 100644 --- a/integration/kotlinx-coroutines-jdk8/src/future/Future.kt +++ b/integration/kotlinx-coroutines-jdk8/src/future/Future.kt @@ -48,7 +48,7 @@ public fun CoroutineScope.future( private class CompletableFutureCoroutine( context: CoroutineContext, private val future: CompletableFuture -) : AbstractCoroutine(context), BiConsumer { +) : AbstractCoroutine(context, initParentJob = true, active = true), BiConsumer { override fun accept(value: T?, exception: Throwable?) { cancel() } diff --git a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt index 998aaa0835..08e5cdad93 100644 --- a/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt +++ b/integration/kotlinx-coroutines-jdk8/test/future/FutureTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.future @@ -567,4 +567,12 @@ class FutureTest : TestBase() { assertFailsWith { stage.await() } finish(4) } + + @Test + fun testCancelledParent() = runTest({ it is java.util.concurrent.CancellationException }) { + cancel() + future { expectUnreached() } + future(start = CoroutineStart.ATOMIC) { } + future(start = CoroutineStart.UNDISPATCHED) { } + } } diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 1d16d31086..8a35d18687 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -1,7 +1,5 @@ public abstract class kotlinx/coroutines/AbstractCoroutine : kotlinx/coroutines/JobSupport, kotlin/coroutines/Continuation, kotlinx/coroutines/CoroutineScope, kotlinx/coroutines/Job { - protected final field parentContext Lkotlin/coroutines/CoroutineContext; - public fun (Lkotlin/coroutines/CoroutineContext;Z)V - public synthetic fun (Lkotlin/coroutines/CoroutineContext;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lkotlin/coroutines/CoroutineContext;ZZ)V protected fun afterResume (Ljava/lang/Object;)V protected fun cancellationExceptionMessage ()Ljava/lang/String; public final fun getContext ()Lkotlin/coroutines/CoroutineContext; @@ -10,10 +8,8 @@ public abstract class kotlinx/coroutines/AbstractCoroutine : kotlinx/coroutines/ protected fun onCancelled (Ljava/lang/Throwable;Z)V protected fun onCompleted (Ljava/lang/Object;)V protected final fun onCompletionInternal (Ljava/lang/Object;)V - protected fun onStart ()V public final fun resumeWith (Ljava/lang/Object;)V public final fun start (Lkotlinx/coroutines/CoroutineStart;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V - public final fun start (Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function1;)V } public final class kotlinx/coroutines/AwaitKt { @@ -89,6 +85,7 @@ public final class kotlinx/coroutines/CancellableContinuationKt { public abstract interface class kotlinx/coroutines/ChildHandle : kotlinx/coroutines/DisposableHandle { public abstract fun childCancelled (Ljava/lang/Throwable;)Z + public abstract fun getParent ()Lkotlinx/coroutines/Job; } public abstract interface class kotlinx/coroutines/ChildJob : kotlinx/coroutines/Job { @@ -420,6 +417,7 @@ public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlin public final fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public final fun getOnJoin ()Lkotlinx/coroutines/selects/SelectClause0; protected fun handleJobException (Ljava/lang/Throwable;)Z + protected final fun initParentJob (Lkotlinx/coroutines/Job;)V public final fun invokeOnCompletion (Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle; public final fun invokeOnCompletion (ZZLkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle; public fun isActive ()Z @@ -431,6 +429,7 @@ public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlin public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; protected fun onCancelling (Ljava/lang/Throwable;)V protected fun onCompletionInternal (Ljava/lang/Object;)V + protected fun onStart ()V public final fun parentCancelled (Lkotlinx/coroutines/ParentJob;)V public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; public fun plus (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job; @@ -473,6 +472,7 @@ public final class kotlinx/coroutines/NonDisposableHandle : kotlinx/coroutines/C public static final field INSTANCE Lkotlinx/coroutines/NonDisposableHandle; public fun childCancelled (Ljava/lang/Throwable;)Z public fun dispose ()V + public fun getParent ()Lkotlinx/coroutines/Job; public fun toString ()Ljava/lang/String; } diff --git a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt index af392b63f5..439a9ac7a5 100644 --- a/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt +++ b/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines import kotlinx.coroutines.CoroutineStart.* import kotlinx.coroutines.intrinsics.* import kotlin.coroutines.* -import kotlin.jvm.* /** * Abstract base class for implementation of coroutines in coroutine builders. @@ -26,6 +25,9 @@ import kotlin.jvm.* * * [onCancelled] in invoked when the coroutine completes with an exception (cancelled). * * @param parentContext the context of the parent coroutine. + * @param initParentJob specifies whether the parent-child relationship should be instantiated directly + * in `AbstractCoroutine` constructor. If set to `false`, it's the responsibility of the child class + * to invoke [initParentJob] manually. * @param active when `true` (by default), the coroutine is created in the _active_ state, otherwise it is created in the _new_ state. * See [Job] for details. * @@ -33,13 +35,22 @@ import kotlin.jvm.* */ @InternalCoroutinesApi public abstract class AbstractCoroutine( - /** - * The context of the parent coroutine. - */ - @JvmField - protected val parentContext: CoroutineContext, - active: Boolean = true + parentContext: CoroutineContext, + initParentJob: Boolean, + active: Boolean ) : JobSupport(active), Job, Continuation, CoroutineScope { + + init { + /* + * Setup parent-child relationship between the parent in the context and the current coroutine. + * It may cause this coroutine to become _cancelling_ if the parent is already cancelled. + * It is dangerous to install parent-child relationship here if the coroutine class + * operates its state from within onCancelled or onCancelling + * (with exceptions for rx integrations that can't have any parent) + */ + if (initParentJob) initParentJob(parentContext[Job]) + } + /** * The context of this coroutine that includes this coroutine as a [Job]. */ @@ -53,28 +64,6 @@ public abstract class AbstractCoroutine( override val isActive: Boolean get() = super.isActive - /** - * Initializes the parent job from the `parentContext` of this coroutine that was passed to it during construction. - * It shall be invoked at most once after construction after all other initialization. - * - * Invocation of this function may cause this coroutine to become cancelled if the parent is already cancelled, - * in which case it synchronously invokes all the corresponding handlers. - * @suppress **This is unstable API and it is subject to change.** - */ - internal fun initParentJob() { - initParentJobInternal(parentContext[Job]) - } - - /** - * This function is invoked once when a non-active coroutine (constructed with `active` set to `false) - * is [started][start]. - */ - protected open fun onStart() {} - - internal final override fun onStartInternal() { - onStart() - } - /** * This function is invoked once when the job was completed normally with the specified [value], * right before all the waiters for the coroutine's completion are notified. @@ -127,26 +116,6 @@ public abstract class AbstractCoroutine( /** * Starts this coroutine with the given code [block] and [start] strategy. * This function shall be invoked at most once on this coroutine. - * - * First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it - * during construction. Second, it starts the coroutine based on [start] parameter: - * - * * [DEFAULT] uses [startCoroutineCancellable]. - * * [ATOMIC] uses [startCoroutine]. - * * [UNDISPATCHED] uses [startCoroutineUndispatched]. - * * [LAZY] does nothing. - */ - public fun start(start: CoroutineStart, block: suspend () -> T) { - initParentJob() - start(block, this) - } - - /** - * Starts this coroutine with the given code [block] and [start] strategy. - * This function shall be invoked at most once on this coroutine. - * - * First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it - * during construction. Second, it starts the coroutine based on [start] parameter: * * * [DEFAULT] uses [startCoroutineCancellable]. * * [ATOMIC] uses [startCoroutine]. @@ -154,7 +123,6 @@ public abstract class AbstractCoroutine( * * [LAZY] does nothing. */ public fun start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) { - initParentJob() start(block, receiver, this) } } diff --git a/kotlinx-coroutines-core/common/src/Builders.common.kt b/kotlinx-coroutines-core/common/src/Builders.common.kt index 93b3ee4849..09ae9b685d 100644 --- a/kotlinx-coroutines-core/common/src/Builders.common.kt +++ b/kotlinx-coroutines-core/common/src/Builders.common.kt @@ -96,7 +96,7 @@ public fun CoroutineScope.async( private open class DeferredCoroutine( parentContext: CoroutineContext, active: Boolean -) : AbstractCoroutine(parentContext, active), Deferred, SelectClause1 { +) : AbstractCoroutine(parentContext, true, active = active), Deferred, SelectClause1 { override fun getCompleted(): T = getCompletedInternal() as T override suspend fun await(): T = awaitInternal() as T override val onAwait: SelectClause1 get() = this @@ -167,7 +167,6 @@ public suspend fun withContext( } // SLOW PATH -- use new dispatcher val coroutine = DispatchedCoroutine(newContext, uCont) - coroutine.initParentJob() block.startCoroutineCancellable(coroutine, coroutine) coroutine.getResult() } @@ -188,7 +187,7 @@ public suspend inline operator fun CoroutineDispatcher.invoke( private open class StandaloneCoroutine( parentContext: CoroutineContext, active: Boolean -) : AbstractCoroutine(parentContext, active) { +) : AbstractCoroutine(parentContext, initParentJob = true, active = active) { override fun handleJobException(exception: Throwable): Boolean { handleCoroutineException(context, exception) return true diff --git a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt index c80737968e..5e76593df2 100644 --- a/kotlinx-coroutines-core/common/src/CompletableDeferred.kt +++ b/kotlinx-coroutines-core/common/src/CompletableDeferred.kt @@ -80,7 +80,7 @@ public fun CompletableDeferred(value: T): CompletableDeferred = Completab private class CompletableDeferredImpl( parent: Job? ) : JobSupport(true), CompletableDeferred, SelectClause1 { - init { initParentJobInternal(parent) } + init { initParentJob(parent) } override val onCancelComplete get() = true override fun getCompleted(): T = getCompletedInternal() as T override suspend fun await(): T = awaitInternal() as T diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt index 15e8a1bb3e..1a98135a24 100644 --- a/kotlinx-coroutines-core/common/src/Job.kt +++ b/kotlinx-coroutines-core/common/src/Job.kt @@ -457,6 +457,14 @@ public interface ParentJob : Job { @InternalCoroutinesApi @Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases") public interface ChildHandle : DisposableHandle { + + /** + * Returns the parent of the current parent-child relationship. + * @suppress **This is unstable API and it is subject to change.** + */ + @InternalCoroutinesApi + public val parent: Job? + /** * Child is cancelling its parent by invoking this method. * This method is invoked by the child twice. The first time child report its root cause as soon as possible, @@ -650,6 +658,9 @@ private fun Throwable?.orCancellation(job: Job): Throwable = this ?: JobCancella */ @InternalCoroutinesApi public object NonDisposableHandle : DisposableHandle, ChildHandle { + + override val parent: Job? get() = null + /** * Does not do anything. * @suppress diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt index 5b516ae27f..a7dbbf8b31 100644 --- a/kotlinx-coroutines-core/common/src/JobSupport.kt +++ b/kotlinx-coroutines-core/common/src/JobSupport.kt @@ -96,7 +96,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren ~ waits for start >> start / join / await invoked ## ACTIVE: state == EMPTY_ACTIVE | is JobNode | is NodeList - + onStartInternal / onStart (lazy coroutine is started) + + onStart (lazy coroutine is started) ~ active coroutine is working (or scheduled to execution) >> childCancelled / cancelImpl invoked ## CANCELLING: state is Finishing, state.rootCause != null @@ -139,7 +139,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren * Initializes parent job. * It shall be invoked at most once after construction after all other initialization. */ - internal fun initParentJobInternal(parent: Job?) { + protected fun initParentJob(parent: Job?) { assert { parentHandle == null } if (parent == null) { parentHandle = NonDisposableHandle @@ -393,12 +393,12 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren is Empty -> { // EMPTY_X state -- no completion handlers if (state.isActive) return FALSE // already active if (!_state.compareAndSet(state, EMPTY_ACTIVE)) return RETRY - onStartInternal() + onStart() return TRUE } is InactiveNodeList -> { // LIST state -- inactive with a list of completion handlers if (!_state.compareAndSet(state, state.list)) return RETRY - onStartInternal() + onStart() return TRUE } else -> return FALSE // not a new state @@ -409,7 +409,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren * Override to provide the actual [start] action. * This function is invoked exactly once when non-active coroutine is [started][start]. */ - internal open fun onStartInternal() {} + protected open fun onStart() {} public final override fun getCancellationException(): CancellationException = when (val state = this.state) { @@ -1311,7 +1311,7 @@ private class Empty(override val isActive: Boolean) : Incomplete { } internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob { - init { initParentJobInternal(parent) } + init { initParentJob(parent) } override val onCancelComplete get() = true /* * Check whether parent is able to handle exceptions as well. @@ -1459,6 +1459,7 @@ private class InvokeOnCancelling( internal class ChildHandleNode( @JvmField val childJob: ChildJob ) : JobCancellingNode(), ChildHandle { + override val parent: Job get() = job override fun invoke(cause: Throwable?) = childJob.parentCancelled(job) override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause) } diff --git a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt index 07e7597627..3ed4bc7fff 100644 --- a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt +++ b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt @@ -127,7 +127,13 @@ private open class BroadcastCoroutine( parentContext: CoroutineContext, protected val _channel: BroadcastChannel, active: Boolean -) : AbstractCoroutine(parentContext, active), ProducerScope, BroadcastChannel by _channel { +) : AbstractCoroutine(parentContext, initParentJob = false, active = active), + ProducerScope, BroadcastChannel by _channel { + + init { + initParentJob(parentContext[Job]) + } + override val isActive: Boolean get() = super.isActive override val channel: SendChannel diff --git a/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt b/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt index b2b257def2..57b2797de6 100644 --- a/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt +++ b/kotlinx-coroutines-core/common/src/channels/ChannelCoroutine.kt @@ -11,8 +11,10 @@ import kotlin.coroutines.* internal open class ChannelCoroutine( parentContext: CoroutineContext, protected val _channel: Channel, + initParentJob: Boolean, active: Boolean -) : AbstractCoroutine(parentContext, active), Channel by _channel { +) : AbstractCoroutine(parentContext, initParentJob, active), Channel by _channel { + val channel: Channel get() = this override fun cancel() { diff --git a/kotlinx-coroutines-core/common/src/channels/Produce.kt b/kotlinx-coroutines-core/common/src/channels/Produce.kt index 3c183587f3..3342fb6ec9 100644 --- a/kotlinx-coroutines-core/common/src/channels/Produce.kt +++ b/kotlinx-coroutines-core/common/src/channels/Produce.kt @@ -139,7 +139,7 @@ internal fun CoroutineScope.produce( internal open class ProducerCoroutine( parentContext: CoroutineContext, channel: Channel -) : ChannelCoroutine(parentContext, channel, active = true), ProducerScope { +) : ChannelCoroutine(parentContext, channel, true, active = true), ProducerScope { override val isActive: Boolean get() = super.isActive diff --git a/kotlinx-coroutines-core/common/src/internal/Scopes.kt b/kotlinx-coroutines-core/common/src/internal/Scopes.kt index 98db37de8d..ad8d86ed5a 100644 --- a/kotlinx-coroutines-core/common/src/internal/Scopes.kt +++ b/kotlinx-coroutines-core/common/src/internal/Scopes.kt @@ -15,12 +15,13 @@ import kotlin.jvm.* internal open class ScopeCoroutine( context: CoroutineContext, @JvmField val uCont: Continuation // unintercepted continuation -) : AbstractCoroutine(context, true), CoroutineStackFrame { +) : AbstractCoroutine(context, true, true), CoroutineStackFrame { + final override val callerFrame: CoroutineStackFrame? get() = uCont as? CoroutineStackFrame final override fun getStackTraceElement(): StackTraceElement? = null - final override val isScopedCoroutine: Boolean get() = true - internal val parent: Job? get() = parentContext[Job] + final override val isScopedCoroutine: Boolean get() = true + internal val parent: Job? get() = parentHandle?.parent override fun afterCompletion(state: Any?) { // Resume in a cancellable way by default when resuming from another context diff --git a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt index 1273634efa..38e870ef9c 100644 --- a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt +++ b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt @@ -82,11 +82,9 @@ private inline fun startDirect(completion: Continuation, block: (Continua * This function shall be invoked at most once on this coroutine. * This function checks cancellation of the outer [Job] on fast-path. * - * First, this function initializes the parent job from the `parentContext` of this coroutine that was passed to it - * during construction. Second, it starts the coroutine using [startCoroutineUninterceptedOrReturn]. + * It starts the coroutine using [startCoroutineUninterceptedOrReturn]. */ internal fun ScopeCoroutine.startUndispatchedOrReturn(receiver: R, block: suspend R.() -> T): Any? { - initParentJob() return undispatchedResult({ true }) { block.startCoroutineUninterceptedOrReturn(receiver, this) } @@ -96,8 +94,8 @@ internal fun ScopeCoroutine.startUndispatchedOrReturn(receiver: R, blo * Same as [startUndispatchedOrReturn], but ignores [TimeoutCancellationException] on fast-path. */ internal fun ScopeCoroutine.startUndispatchedOrReturnIgnoreTimeout( - receiver: R, block: suspend R.() -> T): Any? { - initParentJob() + receiver: R, block: suspend R.() -> T +): Any? { return undispatchedResult({ e -> !(e is TimeoutCancellationException && e.coroutine === this) }) { block.startCoroutineUninterceptedOrReturn(receiver, this) } diff --git a/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt b/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt index ce20837e25..ebe88ce156 100644 --- a/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt +++ b/kotlinx-coroutines-core/common/test/AbstractCoroutineTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -13,7 +13,7 @@ class AbstractCoroutineTest : TestBase() { fun testNotifications() = runTest { expect(1) val coroutineContext = coroutineContext // workaround for KT-22984 - val coroutine = object : AbstractCoroutine(coroutineContext, false) { + val coroutine = object : AbstractCoroutine(coroutineContext, true, false) { override fun onStart() { expect(3) } @@ -53,7 +53,7 @@ class AbstractCoroutineTest : TestBase() { fun testNotificationsWithException() = runTest { expect(1) val coroutineContext = coroutineContext // workaround for KT-22984 - val coroutine = object : AbstractCoroutine(coroutineContext + NonCancellable, false) { + val coroutine = object : AbstractCoroutine(coroutineContext + NonCancellable, true, false) { override fun onStart() { expect(3) } @@ -91,4 +91,4 @@ class AbstractCoroutineTest : TestBase() { coroutine.resumeWithException(TestException2()) finish(10) } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt b/kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt new file mode 100644 index 0000000000..749bbfc921 --- /dev/null +++ b/kotlinx-coroutines-core/common/test/CancelledParentAttachTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +import kotlinx.coroutines.channels.* +import kotlinx.coroutines.flow.internal.* +import kotlin.test.* + +class CancelledParentAttachTest : TestBase() { + + @Test + fun testAsync() = CoroutineStart.values().forEach(::testAsyncCancelledParent) + + private fun testAsyncCancelledParent(start: CoroutineStart) = + runTest({ it is CancellationException }) { + cancel() + expect(1) + val d = async(start = start) { 42 } + expect(2) + d.invokeOnCompletion { + finish(3) + reset() + } + } + + @Test + fun testLaunch() = CoroutineStart.values().forEach(::testLaunchCancelledParent) + + private fun testLaunchCancelledParent(start: CoroutineStart) = + runTest({ it is CancellationException }) { + cancel() + expect(1) + val d = launch(start = start) { } + expect(2) + d.invokeOnCompletion { + finish(3) + reset() + } + } + + @Test + fun testProduce() = + runTest({ it is CancellationException }) { + cancel() + expect(1) + val d = produce { } + expect(2) + (d as Job).invokeOnCompletion { + finish(3) + reset() + } + } + + @Test + fun testBroadcast() = CoroutineStart.values().forEach(::testBroadcastCancelledParent) + + private fun testBroadcastCancelledParent(start: CoroutineStart) = + runTest({ it is CancellationException }) { + cancel() + expect(1) + val bc = broadcast(start = start) {} + expect(2) + (bc as Job).invokeOnCompletion { + finish(3) + reset() + } + } + + @Test + fun testScopes() { + testScope { coroutineScope { } } + testScope { supervisorScope { } } + testScope { flowScope { } } + testScope { withTimeout(Long.MAX_VALUE) { } } + testScope { withContext(Job()) { } } + testScope { withContext(CoroutineName("")) { } } + } + + private inline fun testScope(crossinline block: suspend () -> Unit) = runTest({ it is CancellationException }) { + cancel() + block() + } +} diff --git a/kotlinx-coroutines-core/jvm/src/Builders.kt b/kotlinx-coroutines-core/jvm/src/Builders.kt index c1b878ce2c..edb4303198 100644 --- a/kotlinx-coroutines-core/jvm/src/Builders.kt +++ b/kotlinx-coroutines-core/jvm/src/Builders.kt @@ -63,7 +63,8 @@ private class BlockingCoroutine( parentContext: CoroutineContext, private val blockedThread: Thread, private val eventLoop: EventLoop? -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, true, true) { + override val isScopedCoroutine: Boolean get() = true override fun afterCompletion(state: Any?) { diff --git a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt index 0212d740bb..3a208c0f70 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt @@ -127,7 +127,11 @@ private open class ActorCoroutine( parentContext: CoroutineContext, channel: Channel, active: Boolean -) : ChannelCoroutine(parentContext, channel, active), ActorScope { +) : ChannelCoroutine(parentContext, channel, initParentJob = false, active = active), ActorScope { + + init { + initParentJob(parentContext[Job]) + } override fun onCancelling(cause: Throwable?) { _channel.cancel(cause?.let { diff --git a/kotlinx-coroutines-core/jvm/test/JoinStrTest.kt b/kotlinx-coroutines-core/jvm/test/JoinStressTest.kt similarity index 97% rename from kotlinx-coroutines-core/jvm/test/JoinStrTest.kt rename to kotlinx-coroutines-core/jvm/test/JoinStressTest.kt index 5090e7c06c..6d47418564 100644 --- a/kotlinx-coroutines-core/jvm/test/JoinStrTest.kt +++ b/kotlinx-coroutines-core/jvm/test/JoinStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines diff --git a/kotlinx-coroutines-core/jvm/test/RunBlockingTest.kt b/kotlinx-coroutines-core/jvm/test/RunBlockingTest.kt index e20362ff93..de38df6b26 100644 --- a/kotlinx-coroutines-core/jvm/test/RunBlockingTest.kt +++ b/kotlinx-coroutines-core/jvm/test/RunBlockingTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -171,4 +171,15 @@ class RunBlockingTest : TestBase() { } rb.hashCode() // unused } + + @Test + fun testCancelledParent() { + val job = Job() + job.cancel() + assertFailsWith { + runBlocking(job) { + expectUnreached() + } + } + } } diff --git a/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt index ae95e694cd..d3b2ff1265 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ActorLazyTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -78,4 +78,14 @@ class ActorLazyTest : TestBase() { job.join() finish(5) } -} \ No newline at end of file + + @Test + fun testCancelledParent() = runTest({ it is CancellationException }) { + cancel() + expect(1) + actor(start = CoroutineStart.LAZY) { + expectUnreached() + } + finish(2) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt index bdca5039d2..1d7613eded 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -173,11 +173,24 @@ class ActorTest(private val capacity: Int) : TestBase() { fun testCloseFreshActor() = runTest { for (start in CoroutineStart.values()) { val job = launch { - val actor = actor(start = start) { for (i in channel) {} } + val actor = actor(start = start) { + for (i in channel) { + } + } actor.close() } job.join() } } + + @Test + fun testCancelledParent() = runTest({ it is CancellationException }) { + cancel() + expect(1) + actor { + expectUnreached() + } + finish(2) + } } diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/SuppressionTests.kt b/kotlinx-coroutines-core/jvm/test/exceptions/SuppressionTests.kt index 6034fccbca..edce175d4a 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/SuppressionTests.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/SuppressionTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.exceptions @@ -15,8 +15,8 @@ class SuppressionTests : TestBase() { @Test fun testNotificationsWithException() = runTest { expect(1) - val coroutineContext = kotlin.coroutines.coroutineContext // workaround for KT-22984 - val coroutine = object : AbstractCoroutine(coroutineContext, false) { + val coroutineContext = kotlin.coroutines.coroutineContext + NonCancellable // workaround for KT-22984 + val coroutine = object : AbstractCoroutine(coroutineContext, true, false) { override fun onStart() { expect(3) } @@ -82,4 +82,4 @@ class SuppressionTests : TestBase() { assertTrue(e.cause!!.suppressed.isEmpty()) } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/native/src/Builders.kt b/kotlinx-coroutines-core/native/src/Builders.kt index 7425a05542..30c187fa96 100644 --- a/kotlinx-coroutines-core/native/src/Builders.kt +++ b/kotlinx-coroutines-core/native/src/Builders.kt @@ -53,7 +53,7 @@ public fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, bl private class BlockingCoroutine( parentContext: CoroutineContext, private val eventLoop: EventLoop? -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, true, true) { override val isScopedCoroutine: Boolean get() = true @Suppress("UNCHECKED_CAST") diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 35878b04b2..156205624e 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -80,7 +80,7 @@ public class PublisherCoroutine( parentContext: CoroutineContext, private val subscriber: Subscriber, private val exceptionOnCancelHandler: (Throwable, CoroutineContext) -> Unit -) : AbstractCoroutine(parentContext, true), ProducerScope, Subscription, SelectClause2> { +) : AbstractCoroutine(parentContext, false, true), ProducerScope, Subscription, SelectClause2> { override val channel: SendChannel get() = this // Mutex is locked when either nRequested == 0 or while subscriber.onXXX is being invoked diff --git a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt index 614e9ead1e..1f04ea538c 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt @@ -184,7 +184,11 @@ public class FlowSubscription( @JvmField public val flow: Flow, @JvmField public val subscriber: Subscriber, context: CoroutineContext -) : Subscription, AbstractCoroutine(context, true) { +) : Subscription, AbstractCoroutine(context, initParentJob = false, true) { + /* + * We deliberately set initParentJob to false and do not establish parent-child + * relationship because FlowSubscription doesn't support it + */ private val requested = atomic(0L) private val producer = atomic?>(createInitialContinuation()) diff --git a/reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt b/reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt new file mode 100644 index 0000000000..f846882ab2 --- /dev/null +++ b/reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.reactive + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import org.junit.* + + +class CancelledParentAttachTest : TestBase() { + + @Test + fun testFlow() = runTest { + val f = flowOf(1, 2, 3).cancellable() + val j = Job().also { it.cancel() } + f.asPublisher(j).asFlow().collect() + } +} diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index 2d595c961f..e146dca9f6 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -58,7 +58,7 @@ private fun monoInternal( private class MonoCoroutine( parentContext: CoroutineContext, private val sink: MonoSink -) : AbstractCoroutine(parentContext, true), Disposable { +) : AbstractCoroutine(parentContext, false, true), Disposable { @Volatile private var disposed = false diff --git a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt index d0a43fb1a4..3400278cb3 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt @@ -53,7 +53,7 @@ private fun rxCompletableInternal( private class RxCompletableCoroutine( parentContext: CoroutineContext, private val subscriber: CompletableEmitter -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, false, true) { override fun onCompleted(value: Unit) { try { subscriber.onComplete() diff --git a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt index f5ed48b9a9..c4ca54cf48 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt @@ -54,7 +54,7 @@ private fun rxMaybeInternal( private class RxMaybeCoroutine( parentContext: CoroutineContext, private val subscriber: MaybeEmitter -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, false, true) { override fun onCompleted(value: T) { try { if (value == null) subscriber.onComplete() else subscriber.onSuccess(value) diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index 6d11cb9c35..4f4706d8ad 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -68,10 +68,10 @@ private const val OPEN = 0 // open channel, still working private const val CLOSED = -1 // closed, but have not signalled onCompleted/onError yet private const val SIGNALLED = -2 // already signalled subscriber onCompleted/onError -private class RxObservableCoroutine( +private class RxObservableCoroutine( parentContext: CoroutineContext, private val subscriber: ObservableEmitter -) : AbstractCoroutine(parentContext, true), ProducerScope, SelectClause2> { +) : AbstractCoroutine(parentContext, false, true), ProducerScope, SelectClause2> { override val channel: SendChannel get() = this // Mutex is locked when while subscriber.onXXX is being invoked diff --git a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt index b8012b6de9..3b81445312 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt @@ -53,7 +53,7 @@ private fun rxSingleInternal( private class RxSingleCoroutine( parentContext: CoroutineContext, private val subscriber: SingleEmitter -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, false, true) { override fun onCompleted(value: T) { try { subscriber.onSuccess(value) diff --git a/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt b/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt index f4c5d7e768..88137675d8 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt @@ -39,7 +39,7 @@ private fun rxCompletableInternal( private class RxCompletableCoroutine( parentContext: CoroutineContext, private val subscriber: CompletableEmitter -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, false, true) { override fun onCompleted(value: Unit) { try { subscriber.onComplete() diff --git a/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt index ca1d5b59ee..1c10266470 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt @@ -40,7 +40,7 @@ private fun rxMaybeInternal( private class RxMaybeCoroutine( parentContext: CoroutineContext, private val subscriber: MaybeEmitter -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, false, true) { override fun onCompleted(value: T) { try { if (value == null) subscriber.onComplete() else subscriber.onSuccess(value) diff --git a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt index 7bd07750fe..bd9239e7db 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt @@ -57,7 +57,7 @@ private const val SIGNALLED = -2 // already signalled subscriber onCompleted/on private class RxObservableCoroutine( parentContext: CoroutineContext, private val subscriber: ObservableEmitter -) : AbstractCoroutine(parentContext, true), ProducerScope, SelectClause2> { +) : AbstractCoroutine(parentContext, false, true), ProducerScope, SelectClause2> { override val channel: SendChannel get() = this // Mutex is locked when while subscriber.onXXX is being invoked diff --git a/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt b/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt index f4d07fb40f..fb6020eab3 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt @@ -39,7 +39,7 @@ private fun rxSingleInternal( private class RxSingleCoroutine( parentContext: CoroutineContext, private val subscriber: SingleEmitter -) : AbstractCoroutine(parentContext, true) { +) : AbstractCoroutine(parentContext, false, true) { override fun onCompleted(value: T) { try { subscriber.onSuccess(value) From 7c6c72fb1d8bd2991d9956c1a791d8599a02f35f Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Tue, 23 Mar 2021 18:10:08 +0300 Subject: [PATCH 11/55] Allow extra suspension in Lincheck tests for `Semaphore.acquire()` (#2602) --- .../jvm/test/lincheck/SemaphoreLincheckTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt index 84ce773c15..2b471d7f26 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/SemaphoreLincheckTest.kt @@ -16,7 +16,7 @@ abstract class SemaphoreLincheckTestBase(permits: Int) : AbstractLincheckTest() @Operation fun tryAcquire() = semaphore.tryAcquire() - @Operation(promptCancellation = true) + @Operation(promptCancellation = true, allowExtraSuspension = true) suspend fun acquire() = semaphore.acquire() @Operation(handleExceptionsAsResult = [IllegalStateException::class]) From 5f9e52c5644976dfb9e2e31bde573682c06e6ff9 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 24 Mar 2021 11:02:18 +0300 Subject: [PATCH 12/55] Update SharedFlow documentation (#2437) * Update SharedFlow documentation --- kotlinx-coroutines-core/common/src/flow/SharedFlow.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt index 048cd8b364..ac9e66b0d5 100644 --- a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt @@ -33,7 +33,7 @@ import kotlin.native.concurrent.* * * [SharedFlow] is useful for broadcasting events that happen inside an application to subscribers that can come and go. * For example, the following class encapsulates an event bus that distributes events to all subscribers - * in a _rendezvous_ manner, suspending until all subscribers process each event: + * in a _rendezvous_ manner, suspending until all subscribers receive emitted event: * * ``` * class EventBus { From e608dfbb950831a865da7580b0f9b2b0dcfe7667 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 24 Mar 2021 11:51:12 +0300 Subject: [PATCH 13/55] Rework reusability control in cancellable continuation (#2581) * Rework reusability control in cancellable continuation * Update initCancellability documentation and implementation to be aligned with current invariants * Make parentHandle non-volatile and ensure there are no races around it * Establish new reusability invariants - Reusable continuation can be used _only_ if it states is not REUSABLE_CLAIMED - If it is, spin-loop and wait for release - Now the parent is attached to reusable continuation only if it was suspended at least once. Otherwise, the state machine can return via fast-path and no one will be able to release intercepted continuation (-> detach from parent) - It implies that the parent is attached after trySuspend call and can be concurrently reused, this is where new invariant comes into play * Leverage the fact that it's non-atomic and do not check it for cancellation prematurely. It increases the performance of fast-path, but potentially affects rare cancellation cases Fixes #2564 --- .../common/src/CancellableContinuation.kt | 8 +- .../common/src/CancellableContinuationImpl.kt | 116 +++++++++++------- .../common/src/CoroutineDispatcher.kt | 7 +- .../common/src/JobSupport.kt | 2 + .../src/internal/DispatchedContinuation.kt | 51 ++++++-- .../jvm/test/CancelledAwaitStressTest.kt | 4 +- .../jvm/test/FieldWalker.kt | 4 +- ...leCancellableContinuationLeakStressTest.kt | 41 +++++++ 8 files changed, 167 insertions(+), 66 deletions(-) create mode 100644 kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationLeakStressTest.kt diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt index 8f589912a0..b133b7935d 100644 --- a/kotlinx-coroutines-core/common/src/CancellableContinuation.kt +++ b/kotlinx-coroutines-core/common/src/CancellableContinuation.kt @@ -104,8 +104,10 @@ public interface CancellableContinuation : Continuation { public fun completeResume(token: Any) /** - * Legacy function that turned on cancellation behavior in [suspendCancellableCoroutine] before kotlinx.coroutines 1.1.0. - * This function does nothing and is left only for binary compatibility with old compiled code. + * Internal function that setups cancellation behavior in [suspendCancellableCoroutine]. + * It's illegal to call this function in any non-`kotlinx.coroutines` code and + * such calls lead to undefined behaviour. + * Exposed in our ABI since 1.0.0 withing `suspendCancellableCoroutine` body. * * @suppress **This is unstable API and it is subject to change.** */ @@ -332,7 +334,7 @@ internal suspend inline fun suspendCancellableCoroutineReusable( internal fun getOrCreateCancellableContinuation(delegate: Continuation): CancellableContinuationImpl { // If used outside of our dispatcher if (delegate !is DispatchedContinuation) { - return CancellableContinuationImpl(delegate, MODE_CANCELLABLE_REUSABLE) + return CancellableContinuationImpl(delegate, MODE_CANCELLABLE) } /* * Attempt to claim reusable instance. diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt index 1a8f35663a..c310623c5d 100644 --- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt +++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt @@ -72,10 +72,7 @@ internal open class CancellableContinuationImpl( */ private val _state = atomic(Active) - private val _parentHandle = atomic(null) - private var parentHandle: DisposableHandle? - get() = _parentHandle.value - set(value) { _parentHandle.value = value } + private var parentHandle: DisposableHandle? = null internal val state: Any? get() = _state.value @@ -93,7 +90,21 @@ internal open class CancellableContinuationImpl( } public override fun initCancellability() { - setupCancellation() + /* + * Invariant: at the moment of invocation, `this` has not yet + * leaked to user code and no one is able to invoke `resume` or `cancel` + * on it yet. Also, this function is not invoked for reusable continuations. + */ + val handle = installParentHandle() + ?: return // fast path -- don't do anything without parent + // now check our state _after_ registering, could have completed while we were registering, + // but only if parent was cancelled. Parent could be in a "cancelling" state for a while, + // so we are helping it and cleaning the node ourselves + if (isCompleted) { + // Can be invoked concurrently in 'parentCancelled', no problems here + handle.dispose() + parentHandle = NonDisposableHandle + } } private fun isReusable(): Boolean = delegate is DispatchedContinuation<*> && delegate.isReusable(this) @@ -118,40 +129,6 @@ internal open class CancellableContinuationImpl( return true } - /** - * Setups parent cancellation and checks for postponed cancellation in the case of reusable continuations. - * It is only invoked from an internal [getResult] function for reusable continuations - * and from [suspendCancellableCoroutine] to establish a cancellation before registering CC anywhere. - */ - private fun setupCancellation() { - if (checkCompleted()) return - if (parentHandle !== null) return // fast path 2 -- was already initialized - val parent = delegate.context[Job] ?: return // fast path 3 -- don't do anything without parent - val handle = parent.invokeOnCompletion( - onCancelling = true, - handler = ChildContinuation(this).asHandler - ) - parentHandle = handle - // now check our state _after_ registering (could have completed while we were registering) - // Also note that we do not dispose parent for reusable continuations, dispatcher will do that for us - if (isCompleted && !isReusable()) { - handle.dispose() // it is Ok to call dispose twice -- here and in disposeParentHandle - parentHandle = NonDisposableHandle // release it just in case, to aid GC - } - } - - private fun checkCompleted(): Boolean { - val completed = isCompleted - if (!resumeMode.isReusableMode) return completed // Do not check postponed cancellation for non-reusable continuations - val dispatched = delegate as? DispatchedContinuation<*> ?: return completed - val cause = dispatched.checkPostponedCancellation(this) ?: return completed - if (!completed) { - // Note: this cancel may fail if one more concurrent cancel is currently being invoked - cancel(cause) - } - return true - } - public override val callerFrame: CoroutineStackFrame? get() = delegate as? CoroutineStackFrame @@ -188,7 +165,9 @@ internal open class CancellableContinuationImpl( */ private fun cancelLater(cause: Throwable): Boolean { if (!resumeMode.isReusableMode) return false - val dispatched = (delegate as? DispatchedContinuation<*>) ?: return false + // Ensure that we are postponing cancellation to the right instance + if (!isReusable()) return false + val dispatched = delegate as DispatchedContinuation<*> return dispatched.postponeCancellation(cause) } @@ -216,7 +195,7 @@ internal open class CancellableContinuationImpl( private inline fun callCancelHandlerSafely(block: () -> Unit) { try { - block() + block() } catch (ex: Throwable) { // Handler should never fail, if it does -- it is an unhandled exception handleCoroutineException( @@ -276,9 +255,33 @@ internal open class CancellableContinuationImpl( @PublishedApi internal fun getResult(): Any? { - setupCancellation() - if (trySuspend()) return COROUTINE_SUSPENDED + val isReusable = isReusable() + // trySuspend may fail either if 'block' has resumed/cancelled a continuation + // or we got async cancellation from parent. + if (trySuspend()) { + /* + * We were neither resumed nor cancelled, time to suspend. + * But first we have to install parent cancellation handle (if we didn't yet), + * so CC could be properly resumed on parent cancellation. + */ + if (parentHandle == null) { + installParentHandle() + } + /* + * Release the continuation after installing the handle (if needed). + * If we were successful, then do nothing, it's ok to reuse the instance now. + * Otherwise, dispose the handle by ourselves. + */ + if (isReusable) { + releaseClaimedReusableContinuation() + } + return COROUTINE_SUSPENDED + } // otherwise, onCompletionInternal was already invoked & invoked tryResume, and the result is in the state + if (isReusable) { + // release claimed reusable continuation for the future reuse + releaseClaimedReusableContinuation() + } val state = this.state if (state is CompletedExceptionally) throw recoverStackTrace(state.cause, this) // if the parent job was already cancelled, then throw the corresponding cancellation exception @@ -296,6 +299,28 @@ internal open class CancellableContinuationImpl( return getSuccessfulResult(state) } + private fun installParentHandle(): DisposableHandle? { + val parent = context[Job] ?: return null // don't do anything without a parent + // Install the handle + val handle = parent.invokeOnCompletion( + onCancelling = true, + handler = ChildContinuation(this).asHandler + ) + parentHandle = handle + return handle + } + + /** + * Tries to release reusable continuation. It can fail is there was an asynchronous cancellation, + * in which case it detaches from the parent and cancels this continuation. + */ + private fun releaseClaimedReusableContinuation() { + // Cannot be casted if e.g. invoked from `installParentHandleReusable` for context without dispatchers, but with Job in it + val cancellationCause = (delegate as? DispatchedContinuation<*>)?.tryReleaseClaimedContinuation(this) ?: return + detachChild() + cancel(cancellationCause) + } + override fun resumeWith(result: Result) = resumeImpl(result.toState(this), resumeMode) @@ -462,11 +487,10 @@ internal open class CancellableContinuationImpl( /** * Detaches from the parent. - * Invariant: used from [CoroutineDispatcher.releaseInterceptedContinuation] iff [isReusable] is `true` */ internal fun detachChild() { - val handle = parentHandle - handle?.dispose() + val handle = parentHandle ?: return + handle.dispose() parentHandle = NonDisposableHandle } diff --git a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt index b2b887988d..10be4a3d9e 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt @@ -101,7 +101,12 @@ public abstract class CoroutineDispatcher : @InternalCoroutinesApi public override fun releaseInterceptedContinuation(continuation: Continuation<*>) { - (continuation as DispatchedContinuation<*>).reusableCancellableContinuation?.detachChild() + /* + * Unconditional cast is safe here: we only return DispatchedContinuation from `interceptContinuation`, + * any ClassCastException can only indicate compiler bug + */ + val dispatched = continuation as DispatchedContinuation<*> + dispatched.release() } /** diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt index a7dbbf8b31..438e7f5722 100644 --- a/kotlinx-coroutines-core/common/src/JobSupport.kt +++ b/kotlinx-coroutines-core/common/src/JobSupport.kt @@ -1228,6 +1228,8 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren * thrown and not a JobCancellationException. */ val cont = AwaitContinuation(uCont.intercepted(), this) + // we are mimicking suspendCancellableCoroutine here and call initCancellability, too. + cont.initCancellability() cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion(cont).asHandler)) cont.getResult() } diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt index 2874e7d592..45b9699c84 100644 --- a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt +++ b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt @@ -46,17 +46,15 @@ internal class DispatchedContinuation( * 4) [Throwable] continuation was cancelled with this cause while being in [suspendCancellableCoroutineReusable], * [CancellableContinuationImpl.getResult] will check for cancellation later. * - * [REUSABLE_CLAIMED] state is required to prevent the lost resume in the channel. - * AbstractChannel.receive method relies on the fact that the following pattern + * [REUSABLE_CLAIMED] state is required to prevent double-use of the reused continuation. + * In the `getResult`, we have the following code: * ``` - * suspendCancellableCoroutineReusable { cont -> - * val result = pollFastPath() - * if (result != null) cont.resume(result) + * if (trySuspend()) { + * // <- at this moment current continuation can be redispatched and claimed again. + * attachChildToParent() + * releaseClaimedContinuation() * } * ``` - * always succeeds. - * To make it always successful, we actually postpone "reusable" cancellation - * to this phase and set cancellation only at the moment of instantiation. */ private val _reusableCancellableContinuation = atomic(null) @@ -66,9 +64,9 @@ internal class DispatchedContinuation( public fun isReusable(requester: CancellableContinuationImpl<*>): Boolean { /* * Reusability control: - * `null` -> no reusability at all, false + * `null` -> no reusability at all, `false` * If current state is not CCI, then we are within `suspendCancellableCoroutineReusable`, true - * Else, if result is CCI === requester. + * Else, if result is CCI === requester, then it's our reusable continuation * Identity check my fail for the following pattern: * ``` * loop: @@ -82,6 +80,27 @@ internal class DispatchedContinuation( return true } + + /** + * Awaits until previous call to `suspendCancellableCoroutineReusable` will + * stop mutating cached instance + */ + public fun awaitReusability() { + _reusableCancellableContinuation.loop { it -> + if (it !== REUSABLE_CLAIMED) return + } + } + + public fun release() { + /* + * Called from `releaseInterceptedContinuation`, can be concurrent with + * the code in `getResult` right after `trySuspend` returned `true`, so we have + * to wait for a release here. + */ + awaitReusability() + reusableCancellableContinuation?.detachChild() + } + /** * Claims the continuation for [suspendCancellableCoroutineReusable] block, * so all cancellations will be postponed. @@ -103,11 +122,20 @@ internal class DispatchedContinuation( _reusableCancellableContinuation.value = REUSABLE_CLAIMED return null } + // potentially competing with cancel state is CancellableContinuationImpl<*> -> { if (_reusableCancellableContinuation.compareAndSet(state, REUSABLE_CLAIMED)) { return state as CancellableContinuationImpl } } + state === REUSABLE_CLAIMED -> { + // Do nothing, wait until reusable instance will be returned from + // getResult() of a previous `suspendCancellableCoroutineReusable` + } + state is Throwable -> { + // Also do nothing, Throwable can only indicate that the CC + // is in REUSABLE_CLAIMED state, but with postponed cancellation + } else -> error("Inconsistent state $state") } } @@ -127,14 +155,13 @@ internal class DispatchedContinuation( * * See [CancellableContinuationImpl.getResult]. */ - fun checkPostponedCancellation(continuation: CancellableContinuation<*>): Throwable? { + fun tryReleaseClaimedContinuation(continuation: CancellableContinuation<*>): Throwable? { _reusableCancellableContinuation.loop { state -> // not when(state) to avoid Intrinsics.equals call when { state === REUSABLE_CLAIMED -> { if (_reusableCancellableContinuation.compareAndSet(REUSABLE_CLAIMED, continuation)) return null } - state === null -> return null state is Throwable -> { require(_reusableCancellableContinuation.compareAndSet(state, null)) return state diff --git a/kotlinx-coroutines-core/jvm/test/CancelledAwaitStressTest.kt b/kotlinx-coroutines-core/jvm/test/CancelledAwaitStressTest.kt index 55c05c55b0..c7c2c04e59 100644 --- a/kotlinx-coroutines-core/jvm/test/CancelledAwaitStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/CancelledAwaitStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -52,4 +52,4 @@ class CancelledAwaitStressTest : TestBase() { private fun keepMe(a: ByteArray) { // does nothing, makes sure the variable is kept in state-machine } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/FieldWalker.kt b/kotlinx-coroutines-core/jvm/test/FieldWalker.kt index e8079ebdfa..c4232d6e60 100644 --- a/kotlinx-coroutines-core/jvm/test/FieldWalker.kt +++ b/kotlinx-coroutines-core/jvm/test/FieldWalker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -56,7 +56,7 @@ object FieldWalker { * Reflectively starts to walk through object graph and map to all the reached object to their path * in from root. Use [showPath] do display a path if needed. */ - private fun walkRefs(root: Any?, rootStatics: Boolean): Map { + private fun walkRefs(root: Any?, rootStatics: Boolean): IdentityHashMap { val visited = IdentityHashMap() if (root == null) return visited visited[root] = Ref.RootRef diff --git a/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationLeakStressTest.kt b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationLeakStressTest.kt new file mode 100644 index 0000000000..8a20e0843f --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationLeakStressTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +import kotlinx.coroutines.channels.* +import org.junit.Test +import kotlin.test.* + +class ReusableCancellableContinuationLeakStressTest : TestBase() { + + @Suppress("UnnecessaryVariable") + private suspend fun ReceiveChannel.receiveBatch(): T { + val r = receive() // DO NOT MERGE LINES, otherwise TCE will kick in + return r + } + + private val iterations = 100_000 * stressTestMultiplier + + class Leak(val i: Int) + + @Test // Simplified version of #2564 + fun testReusableContinuationLeak() = runTest { + val channel = produce(capacity = 1) { // from the main thread + (0 until iterations).forEach { + send(Leak(it)) + } + } + + launch(Dispatchers.Default) { + repeat (iterations) { + val value = channel.receiveBatch() + assertEquals(it, value.i) + } + (channel as Job).join() + + FieldWalker.assertReachableCount(0, coroutineContext.job, false) { it is Leak } + } + } +} From ed8e8d1ae0de54cb684db9e5817f2234ce7e9f90 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 18 Mar 2021 16:58:55 +0300 Subject: [PATCH 14/55] Introduce ChannelResult and receiveCatching/onReceiveCatching * Introduce three-state ChannelResult, a domain-specific Result class counterpart * Introduce receiveCatching/onReceiveCatching, make it public * Get rid of receiveOrClosed/onReceiveOrClosed without migrations, it was @InternalCoroutinesApi anyway Fixes #330 --- .../api/kotlinx-coroutines-core.api | 41 ++--- .../common/src/channels/AbstractChannel.kt | 26 ++-- .../common/src/channels/Channel.kt | 137 ++++++++-------- .../common/src/flow/Channels.kt | 8 +- .../test/channels/BasicOperationsTest.kt | 20 +-- .../channels/ChannelReceiveCatchingTest.kt | 146 ++++++++++++++++++ .../channels/ChannelReceiveOrClosedTest.kt | 135 ---------------- .../ChannelUndeliveredElementFailureTest.kt | 8 +- .../common/test/channels/TestChannelKind.kt | 8 +- .../test/selects/SelectArrayChannelTest.kt | 28 ++-- .../selects/SelectRendezvousChannelTest.kt | 32 ++-- .../ChannelUndeliveredElementStressTest.kt | 4 +- .../test/channels/TickerChannelCommonTest.kt | 6 +- 13 files changed, 299 insertions(+), 300 deletions(-) create mode 100644 kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt delete mode 100644 kotlinx-coroutines-core/common/test/channels/ChannelReceiveOrClosedTest.kt diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 8a35d18687..7d2fa1f447 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -625,6 +625,26 @@ public final class kotlinx/coroutines/channels/ChannelKt { public static synthetic fun Channel$default (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel; } +public final class kotlinx/coroutines/channels/ChannelResult { + public static final field Companion Lkotlinx/coroutines/channels/ChannelResult$Companion; + public static final synthetic fun box-impl (Ljava/lang/Object;)Lkotlinx/coroutines/channels/ChannelResult; + public static synthetic fun constructor-impl (Ljava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)Ljava/lang/Object; + public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Ljava/lang/Object;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Ljava/lang/Object;Ljava/lang/Object;)Z + public static final fun exceptionOrNull-impl (Ljava/lang/Object;)Ljava/lang/Throwable; + public static final fun getOrNull-impl (Ljava/lang/Object;)Ljava/lang/Object; + public static final fun getOrThrow-impl (Ljava/lang/Object;)Ljava/lang/Object; + public fun hashCode ()I + public static fun hashCode-impl (Ljava/lang/Object;)I + public static final fun isClosed-impl (Ljava/lang/Object;)Z + public static final fun isFailure-impl (Ljava/lang/Object;)Z + public static final fun isSuccess-impl (Ljava/lang/Object;)Z + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Ljava/lang/Object;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ljava/lang/Object; +} + public final class kotlinx/coroutines/channels/ChannelsKt { public static final fun all (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -789,14 +809,14 @@ public abstract interface class kotlinx/coroutines/channels/ReceiveChannel { public abstract synthetic fun cancel (Ljava/lang/Throwable;)Z public abstract fun cancel (Ljava/util/concurrent/CancellationException;)V public abstract fun getOnReceive ()Lkotlinx/coroutines/selects/SelectClause1; - public abstract fun getOnReceiveOrClosed ()Lkotlinx/coroutines/selects/SelectClause1; + public abstract fun getOnReceiveCatching ()Lkotlinx/coroutines/selects/SelectClause1; public abstract fun getOnReceiveOrNull ()Lkotlinx/coroutines/selects/SelectClause1; public abstract fun isClosedForReceive ()Z public abstract fun isEmpty ()Z public abstract fun iterator ()Lkotlinx/coroutines/channels/ChannelIterator; public abstract fun poll ()Ljava/lang/Object; public abstract fun receive (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun receiveOrClosed-WVj179g (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun receiveCatching-JP2dKIU (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun receiveOrNull (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -832,23 +852,6 @@ public final class kotlinx/coroutines/channels/TickerMode : java/lang/Enum { public static fun values ()[Lkotlinx/coroutines/channels/TickerMode; } -public final class kotlinx/coroutines/channels/ValueOrClosed { - public static final field Companion Lkotlinx/coroutines/channels/ValueOrClosed$Companion; - public static final synthetic fun box-impl (Ljava/lang/Object;)Lkotlinx/coroutines/channels/ValueOrClosed; - public fun equals (Ljava/lang/Object;)Z - public static fun equals-impl (Ljava/lang/Object;Ljava/lang/Object;)Z - public static final fun equals-impl0 (Ljava/lang/Object;Ljava/lang/Object;)Z - public static final fun getCloseCause-impl (Ljava/lang/Object;)Ljava/lang/Throwable; - public static final fun getValue-impl (Ljava/lang/Object;)Ljava/lang/Object; - public static final fun getValueOrNull-impl (Ljava/lang/Object;)Ljava/lang/Object; - public fun hashCode ()I - public static fun hashCode-impl (Ljava/lang/Object;)I - public static final fun isClosed-impl (Ljava/lang/Object;)Z - public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ljava/lang/Object;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ljava/lang/Object; -} - public final class kotlinx/coroutines/debug/internal/DebugCoroutineInfo { public fun (Lkotlinx/coroutines/debug/internal/DebugCoroutineInfoImpl;Lkotlin/coroutines/CoroutineContext;)V public final fun getContext ()Lkotlin/coroutines/CoroutineContext; diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt index 9721583e83..180598c94f 100644 --- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt @@ -623,7 +623,7 @@ internal abstract class AbstractChannel( } @Suppress("UNCHECKED_CAST") - public final override suspend fun receiveOrClosed(): ValueOrClosed { + public final override suspend fun receiveCatching(): ChannelResult { // fast path -- try poll non-blocking val result = pollInternal() if (result !== POLL_FAILED) return result.toResult() @@ -742,10 +742,10 @@ internal abstract class AbstractChannel( } } - final override val onReceiveOrClosed: SelectClause1> - get() = object : SelectClause1> { + final override val onReceiveCatching: SelectClause1> + get() = object : SelectClause1> { @Suppress("UNCHECKED_CAST") - override fun registerSelectClause1(select: SelectInstance, block: suspend (ValueOrClosed) -> R) { + override fun registerSelectClause1(select: SelectInstance, block: suspend (ChannelResult) -> R) { registerSelectReceiveMode(select, RECEIVE_RESULT, block as suspend (Any?) -> R) } } @@ -776,7 +776,7 @@ internal abstract class AbstractChannel( } RECEIVE_RESULT -> { if (!select.trySelect()) return - startCoroutineUnintercepted(ValueOrClosed.closed(value.closeCause), select.completion) + startCoroutineUnintercepted(ChannelResult.closed(value.closeCause), select.completion) } RECEIVE_NULL_ON_CLOSE -> { if (value.closeCause == null) { @@ -905,7 +905,7 @@ internal abstract class AbstractChannel( @JvmField val receiveMode: Int ) : Receive() { fun resumeValue(value: E): Any? = when (receiveMode) { - RECEIVE_RESULT -> ValueOrClosed.value(value) + RECEIVE_RESULT -> ChannelResult.value(value) else -> value } @@ -990,7 +990,7 @@ internal abstract class AbstractChannel( @Suppress("UNCHECKED_CAST") override fun completeResumeReceive(value: E) { block.startCoroutineCancellable( - if (receiveMode == RECEIVE_RESULT) ValueOrClosed.value(value) else value, + if (receiveMode == RECEIVE_RESULT) ChannelResult.value(value) else value, select.completion, resumeOnCancellationFun(value) ) @@ -1000,7 +1000,7 @@ internal abstract class AbstractChannel( if (!select.trySelect()) return when (receiveMode) { RECEIVE_THROWS_ON_CLOSE -> select.resumeSelectWithException(closed.receiveException) - RECEIVE_RESULT -> block.startCoroutineCancellable(ValueOrClosed.closed(closed.closeCause), select.completion) + RECEIVE_RESULT -> block.startCoroutineCancellable(ChannelResult.closed(closed.closeCause), select.completion) RECEIVE_NULL_ON_CLOSE -> if (closed.closeCause == null) { block.startCoroutineCancellable(null, select.completion) } else { @@ -1128,9 +1128,9 @@ internal class Closed( override val offerResult get() = this override val pollResult get() = this - override fun tryResumeSend(otherOp: PrepareOp?): Symbol? = RESUME_TOKEN.also { otherOp?.finishPrepare() } + override fun tryResumeSend(otherOp: PrepareOp?): Symbol = RESUME_TOKEN.also { otherOp?.finishPrepare() } override fun completeResumeSend() {} - override fun tryResumeReceive(value: E, otherOp: PrepareOp?): Symbol? = RESUME_TOKEN.also { otherOp?.finishPrepare() } + override fun tryResumeReceive(value: E, otherOp: PrepareOp?): Symbol = RESUME_TOKEN.also { otherOp?.finishPrepare() } override fun completeResumeReceive(value: E) {} override fun resumeSendClosed(closed: Closed<*>) = assert { false } // "Should be never invoked" override fun toString(): String = "Closed@$hexAddress[$closeCause]" @@ -1143,8 +1143,8 @@ internal abstract class Receive : LockFreeLinkedListNode(), ReceiveOrClose } @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") -private inline fun Any?.toResult(): ValueOrClosed = - if (this is Closed<*>) ValueOrClosed.closed(closeCause) else ValueOrClosed.value(this as E) +private inline fun Any?.toResult(): ChannelResult = + if (this is Closed<*>) ChannelResult.closed(closeCause) else ChannelResult.value(this as E) @Suppress("NOTHING_TO_INLINE") -private inline fun Closed<*>.toResult(): ValueOrClosed = ValueOrClosed.closed(closeCause) +private inline fun Closed<*>.toResult(): ChannelResult = ChannelResult.closed(closeCause) diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index b8b81aac53..824a57bfb1 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -244,8 +244,9 @@ public interface ReceiveChannel { /** * Retrieves and removes an element from this channel if it's not empty, or suspends the caller while this channel is empty. - * This method returns [ValueOrClosed] with the value of an element successfully retrieved from the channel - * or the close cause if the channel was closed. + * This method returns [ChannelResult] with the value of an element successfully retrieved from the channel + * or the close cause if the channel was closed. Closed cause may be `null` if the channel was closed normally. + * The result cannot be [failed][ChannelResult.isFailure] without being [closed][ChannelResult.isClosed]. * * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this * function is suspended, this function immediately resumes with a [CancellationException]. @@ -257,25 +258,17 @@ public interface ReceiveChannel { * Note that this function does not check for cancellation when it is not suspended. * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. * - * This function can be used in [select] invocations with the [onReceiveOrClosed] clause. + * This function can be used in [select] invocations with the [onReceiveCatching] clause. * Use [poll] to try receiving from this channel without waiting. - * - * @suppress *This is an internal API, do not use*: Inline classes ABI is not stable yet and - * [KT-27524](https://youtrack.jetbrains.com/issue/KT-27524) needs to be fixed. */ - @InternalCoroutinesApi // until https://youtrack.jetbrains.com/issue/KT-27524 is fixed - public suspend fun receiveOrClosed(): ValueOrClosed + public suspend fun receiveCatching(): ChannelResult /** - * Clause for the [select] expression of the [receiveOrClosed] suspending function that selects with the [ValueOrClosed] with a value + * Clause for the [select] expression of the [onReceiveCatching] suspending function that selects with the [ChannelResult] with a value * that is received from the channel or with a close cause if the channel * [is closed for `receive`][isClosedForReceive]. - * - * @suppress *This is an internal API, do not use*: Inline classes ABI is not stable yet and - * [KT-27524](https://youtrack.jetbrains.com/issue/KT-27524) needs to be fixed. */ - @InternalCoroutinesApi // until https://youtrack.jetbrains.com/issue/KT-27524 is fixed - public val onReceiveOrClosed: SelectClause1> + public val onReceiveCatching: SelectClause1> /** * Retrieves and removes an element from this channel if its not empty, or returns `null` if the channel is empty @@ -321,104 +314,96 @@ public interface ReceiveChannel { } /** - * A discriminated union of [ReceiveChannel.receiveOrClosed] result - * that encapsulates either an element of type [T] successfully received from the channel or a close cause. + * A discriminated union of channel operation result. + * It encapsulates successful or failed result of a channel operation, or a failed operation to a closed channel with + * an optional cause. + * + * Successful result represents a successful operation with value of type [T], for example, result of [Channel.receiveCatching] + * operation or a successfully sent element as a result of [Channel.trySend]. * - * :todo: Do not make it public before resolving todos in the code of this class. + * Failed result represents a failed operation attempt to a channel, but it doesn't necessary indicate that the channel is failed. + * E.g. when the channel is full, [Channel.trySend] returns failed result, but the channel itself is not in the failed state. * - * @suppress *This is an internal API, do not use*: Inline classes ABI is not stable yet and - * [KT-27524](https://youtrack.jetbrains.com/issue/KT-27524) needs to be fixed. + * Closed result represents an operation attempt to a closed channel and also implies that the operation was failed. */ -@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS", "EXPERIMENTAL_FEATURE_WARNING") -@InternalCoroutinesApi // until https://youtrack.jetbrains.com/issue/KT-27524 is fixed -public inline class ValueOrClosed +@Suppress("UNCHECKED_CAST") +public inline class ChannelResult internal constructor(private val holder: Any?) { /** - * Returns `true` if this instance represents a received element. - * In this case [isClosed] returns `false`. - * todo: it is commented for now, because it is not used + * Returns `true` if this instance represents a successful + * operation outcome. + * + * In this case [isFailure] and [isClosed] return false. */ - //public val isValue: Boolean get() = holder !is Closed + public val isSuccess: Boolean get() = holder !is Closed /** - * Returns `true` if this instance represents a close cause. - * In this case [isValue] returns `false`. + * Returns true if this instance represents unsuccessful operation. + * + * In this case [isSuccess] returns false, but it does not imply + * that the channel is failed or closed. + * + * Example of failed operation without an exception and channel being closed + * is [Channel.trySend] attempt to a channel that is full. */ - public val isClosed: Boolean get() = holder is Closed + public val isFailure: Boolean get() = holder is Failed /** - * Returns the received value if this instance represents a received value, or throws an [IllegalStateException] otherwise. + * Returns `true` if this instance represents unsuccessful operation + * to a closed or cancelled channel. * - * :todo: Decide, if it is needed, how it shall be named with relation to [valueOrThrow]: + * In this case [isSuccess] returns false, [isFailure] returns `true`, but it does not imply + * that [exceptionOrNull] returns non-null value. * - * So we have the following methods on `ValueOrClosed`: `value`, `valueOrNull`, `valueOrThrow`. - * On the other hand, the channel has the following `receive` variants: - * * `receive` which corresponds to `receiveOrClosed().valueOrThrow`... huh? - * * `receiveOrNull` which corresponds to `receiveOrClosed().valueOrNull` - * * `receiveOrClosed` - * For the sake of simplicity consider dropping this version of `value` and rename [valueOrThrow] to simply `value`. + * It can happen if the channel was [closed][Channel.close] normally without an exception. */ - @Suppress("UNCHECKED_CAST") - public val value: T - get() = if (holder is Closed) error(DEFAULT_CLOSE_MESSAGE) else holder as T + public val isClosed: Boolean get() = holder is Closed /** - * Returns the received value if this element represents a received value, or `null` otherwise. - * :todo: Decide if it shall be made into extension that is available only for non-null T. - * Note: it might become inconsistent with kotlin.Result + * Returns the encapsulated value if this instance represents success or `null` if it represents failed result. */ - @Suppress("UNCHECKED_CAST") - public val valueOrNull: T? - get() = if (holder is Closed) null else holder as T + public fun getOrNull(): T? = if (holder !is Failed) holder as T else null /** - * :todo: Decide, if it is needed, how it shall be named with relation to [value]. - * Note that `valueOrThrow` rethrows the cause adding no meaningful information about the call site, - * so if one is sure that `ValueOrClosed` always holds a value, this very property should be used. - * Otherwise, it could be very hard to locate the source of the exception. - * todo: it is commented for now, because it is not used + * Returns the encapsulated value if this instance represents success or throws an exception if it is closed or failed. */ - //@Suppress("UNCHECKED_CAST") - //public val valueOrThrow: T - // get() = if (holder is Closed) throw holder.exception else holder as T + public fun getOrThrow(): T { + if (holder !is Failed) return holder as T + if (holder is Closed && holder.cause != null) throw holder.cause + error("Trying to call 'getOrThrow' on a failed channel result: $holder") + } /** - * Returns the close cause of the channel if this instance represents a close cause, or throws - * an [IllegalStateException] otherwise. + * Returns the encapsulated exception if this instance represents failure or null if it is success + * or unsuccessful operation to closed channel. */ - @Suppress("UNCHECKED_CAST") - public val closeCause: Throwable? get() = - if (holder is Closed) holder.cause else error("Channel was not closed") + public fun exceptionOrNull(): Throwable? = (holder as? Closed)?.cause - /** - * @suppress - */ - public override fun toString(): String = - when (holder) { - is Closed -> holder.toString() - else -> "Value($holder)" + internal open class Failed { + override fun toString(): String = "Failed" } - internal class Closed(@JvmField val cause: Throwable?) { - // todo: it is commented for now, because it is not used - //val exception: Throwable get() = cause ?: ClosedReceiveChannelException(DEFAULT_CLOSE_MESSAGE) + internal class Closed(@JvmField val cause: Throwable?): Failed() { override fun equals(other: Any?): Boolean = other is Closed && cause == other.cause override fun hashCode(): Int = cause.hashCode() override fun toString(): String = "Closed($cause)" } - /** - * todo: consider making value/closed constructors public in the future. - */ internal companion object { @Suppress("NOTHING_TO_INLINE") - internal inline fun value(value: E): ValueOrClosed = - ValueOrClosed(value) + internal inline fun value(value: E): ChannelResult = + ChannelResult(value) @Suppress("NOTHING_TO_INLINE") - internal inline fun closed(cause: Throwable?): ValueOrClosed = - ValueOrClosed(Closed(cause)) + internal inline fun closed(cause: Throwable?): ChannelResult = + ChannelResult(Closed(cause)) } + + public override fun toString(): String = + when (holder) { + is Closed -> holder.toString() + else -> "Value($holder)" + } } /** diff --git a/kotlinx-coroutines-core/common/src/flow/Channels.kt b/kotlinx-coroutines-core/common/src/flow/Channels.kt index e883c3b468..5b126f0ed7 100644 --- a/kotlinx-coroutines-core/common/src/flow/Channels.kt +++ b/kotlinx-coroutines-core/common/src/flow/Channels.kt @@ -30,7 +30,7 @@ public suspend fun FlowCollector.emitAll(channel: ReceiveChannel): Uni emitAllImpl(channel, consume = true) private suspend fun FlowCollector.emitAllImpl(channel: ReceiveChannel, consume: Boolean) { - // Manually inlined "consumeEach" implementation that does not use iterator but works via "receiveOrClosed". + // Manually inlined "consumeEach" implementation that does not use iterator but works via "receiveCatching". // It has smaller and more efficient spilled state which also allows to implement a manual kludge to // fix retention of the last emitted value. // See https://youtrack.jetbrains.com/issue/KT-16222 @@ -47,9 +47,9 @@ private suspend fun FlowCollector.emitAllImpl(channel: ReceiveChannel, // L$1 <- channel // L$2 <- cause // L$3 <- this$run (actually equal to this) - val result = run { channel.receiveOrClosed() } + val result = run { channel.receiveCatching() } if (result.isClosed) { - result.closeCause?.let { throw it } + result.exceptionOrNull()?.let { throw it } break // returns normally when result.closeCause == null } // result is spilled here to the coroutine state and retained after the call, even though @@ -58,7 +58,7 @@ private suspend fun FlowCollector.emitAllImpl(channel: ReceiveChannel, // L$1 <- channel // L$2 <- cause // L$3 <- result - emit(result.value) + emit(result.getOrThrow()) } } catch (e: Throwable) { cause = e diff --git a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt index 91d941b32c..e178f124a7 100644 --- a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -35,7 +35,7 @@ class BasicOperationsTest : TestBase() { } @Test - fun testReceiveOrClosed() = runTest { + fun testReceiveCatching() = runTest { TestChannelKind.values().forEach { kind -> testReceiveOrClosed(kind) } } @@ -139,22 +139,22 @@ class BasicOperationsTest : TestBase() { } expect(1) - val result = channel.receiveOrClosed() - assertEquals(1, result.value) - assertEquals(1, result.valueOrNull) - assertTrue(ValueOrClosed.value(1) == result) + val result = channel.receiveCatching() + assertEquals(1, result.getOrThrow()) + assertEquals(1, result.getOrNull()) + assertTrue(ChannelResult.value(1) == result) expect(3) launch { expect(4) channel.close() } - val closed = channel.receiveOrClosed() + val closed = channel.receiveCatching() expect(5) - assertNull(closed.valueOrNull) + assertNull(closed.getOrNull()) assertTrue(closed.isClosed) - assertNull(closed.closeCause) - assertTrue(ValueOrClosed.closed(closed.closeCause) == closed) + assertNull(closed.exceptionOrNull()) + assertTrue(ChannelResult.closed(closed.exceptionOrNull()) == closed) finish(6) } diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt new file mode 100644 index 0000000000..4344b1055d --- /dev/null +++ b/kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.channels + +import kotlinx.coroutines.* +import kotlin.test.* + +class ChannelReceiveCatchingTest : TestBase() { + @Test + fun testChannelOfThrowables() = runTest { + val channel = Channel() + launch { + channel.send(TestException1()) + channel.close(TestException2()) + } + + val element = channel.receiveCatching() + assertTrue(element.getOrThrow() is TestException1) + assertTrue(element.getOrNull() is TestException1) + + val closed = channel.receiveCatching() + assertTrue(closed.isClosed) + assertTrue(closed.isFailure) + assertTrue(closed.exceptionOrNull() is TestException2) + } + + @Test + @Suppress("ReplaceAssertBooleanWithAssertEquality") // inline classes test + fun testNullableIntChanel() = runTest { + val channel = Channel() + launch { + expect(2) + channel.send(1) + expect(3) + channel.send(null) + + expect(6) + channel.close() + } + + expect(1) + val element = channel.receiveCatching() + assertEquals(1, element.getOrThrow()) + assertEquals(1, element.getOrNull()) + assertEquals("Value(1)", element.toString()) + assertTrue(ChannelResult.value(1) == element) // Don't box + assertFalse(element.isFailure) + assertFalse(element.isClosed) + + expect(4) + val nullElement = channel.receiveCatching() + assertNull(nullElement.getOrThrow()) + assertNull(nullElement.getOrNull()) + assertEquals("Value(null)", nullElement.toString()) + assertTrue(ChannelResult.value(null) == nullElement) // Don't box + assertFalse(element.isFailure) + assertFalse(element.isClosed) + + expect(5) + val closed = channel.receiveCatching() + assertTrue(closed.isClosed) + assertTrue(closed.isFailure) + + val closed2 = channel.receiveCatching() + assertTrue(closed2.isClosed) + assertTrue(closed.isFailure) + assertNull(closed2.exceptionOrNull()) + finish(7) + } + + @Test + @ExperimentalUnsignedTypes + fun testUIntChannel() = runTest { + val channel = Channel() + launch { + expect(2) + channel.send(1u) + yield() + expect(4) + channel.send((Long.MAX_VALUE - 1).toUInt()) + expect(5) + } + + expect(1) + val element = channel.receiveCatching() + assertEquals(1u, element.getOrThrow()) + + expect(3) + val element2 = channel.receiveCatching() + assertEquals((Long.MAX_VALUE - 1).toUInt(), element2.getOrThrow()) + finish(6) + } + + @Test + fun testCancelChannel() = runTest { + val channel = Channel() + launch { + expect(2) + channel.cancel() + } + + expect(1) + val closed = channel.receiveCatching() + assertTrue(closed.isClosed) + assertTrue(closed.isFailure) + finish(3) + } + + @Test + @ExperimentalUnsignedTypes + fun testReceiveResultChannel() = runTest { + val channel = Channel>() + launch { + channel.send(ChannelResult.value(1u)) + channel.send(ChannelResult.closed(TestException1())) + channel.close(TestException2()) + } + + val intResult = channel.receiveCatching() + assertEquals(1u, intResult.getOrThrow().getOrThrow()) + assertFalse(intResult.isFailure) + assertFalse(intResult.isClosed) + + val closeCauseResult = channel.receiveCatching() + assertTrue(closeCauseResult.getOrThrow().exceptionOrNull() is TestException1) + + val closeCause = channel.receiveCatching() + assertTrue(closeCause.isClosed) + assertTrue(closeCause.isFailure) + assertTrue(closeCause.exceptionOrNull() is TestException2) + } + + @Test + fun testToString() = runTest { + val channel = Channel(1) + channel.send("message") + channel.close(TestException1("OK")) + assertEquals("Value(message)", channel.receiveCatching().toString()) + // toString implementation for exception differs on every platform + val str = channel.receiveCatching().toString() + if (!str.matches("Closed\\(.*TestException1: OK\\)".toRegex())) + error("Unexpected string: '$str'") + } +} diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelReceiveOrClosedTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelReceiveOrClosedTest.kt deleted file mode 100644 index e58b0deed5..0000000000 --- a/kotlinx-coroutines-core/common/test/channels/ChannelReceiveOrClosedTest.kt +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.channels - -import kotlinx.coroutines.* -import kotlin.test.* - -class ChannelReceiveOrClosedTest : TestBase() { - @Test - fun testChannelOfThrowables() = runTest { - val channel = Channel() - launch { - channel.send(TestException1()) - channel.close(TestException2()) - } - - val element = channel.receiveOrClosed() - assertTrue(element.value is TestException1) - assertTrue(element.valueOrNull is TestException1) - - val closed = channel.receiveOrClosed() - assertTrue(closed.isClosed) - assertTrue(closed.closeCause is TestException2) - } - - @Test - @Suppress("ReplaceAssertBooleanWithAssertEquality") // inline classes test - fun testNullableIntChanel() = runTest { - val channel = Channel() - launch { - expect(2) - channel.send(1) - expect(3) - channel.send(null) - - expect(6) - channel.close() - } - - expect(1) - val element = channel.receiveOrClosed() - assertEquals(1, element.value) - assertEquals(1, element.valueOrNull) - assertEquals("Value(1)", element.toString()) - assertTrue(ValueOrClosed.value(1) == element) // Don't box - - expect(4) - val nullElement = channel.receiveOrClosed() - assertNull(nullElement.value) - assertNull(nullElement.valueOrNull) - assertEquals("Value(null)", nullElement.toString()) - assertTrue(ValueOrClosed.value(null) == nullElement) // Don't box - - expect(5) - val closed = channel.receiveOrClosed() - assertTrue(closed.isClosed) - - val closed2 = channel.receiveOrClosed() - assertTrue(closed2.isClosed) - assertNull(closed2.closeCause) - finish(7) - } - - @Test - @ExperimentalUnsignedTypes - fun testUIntChannel() = runTest { - val channel = Channel() - launch { - expect(2) - channel.send(1u) - yield() - expect(4) - channel.send((Long.MAX_VALUE - 1).toUInt()) - expect(5) - } - - expect(1) - val element = channel.receiveOrClosed() - assertEquals(1u, element.value) - - expect(3) - val element2 = channel.receiveOrClosed() - assertEquals((Long.MAX_VALUE - 1).toUInt(), element2.value) - finish(6) - } - - @Test - fun testCancelChannel() = runTest { - val channel = Channel() - launch { - expect(2) - channel.cancel() - } - - expect(1) - val closed = channel.receiveOrClosed() - assertTrue(closed.isClosed) - finish(3) - } - - @Test - @ExperimentalUnsignedTypes - fun testReceiveResultChannel() = runTest { - val channel = Channel>() - launch { - channel.send(ValueOrClosed.value(1u)) - channel.send(ValueOrClosed.closed(TestException1())) - channel.close(TestException2()) - } - - val intResult = channel.receiveOrClosed() - assertEquals(1u, intResult.value.value) - - val closeCauseResult = channel.receiveOrClosed() - assertTrue(closeCauseResult.value.closeCause is TestException1) - - val closeCause = channel.receiveOrClosed() - assertTrue(closeCause.isClosed) - assertTrue(closeCause.closeCause is TestException2) - } - - @Test - fun testToString() = runTest { - val channel = Channel(1) - channel.send("message") - channel.close(TestException1("OK")) - assertEquals("Value(message)", channel.receiveOrClosed().toString()) - // toString implementation for exception differs on every platform - val str = channel.receiveOrClosed().toString() - if (!str.matches("Closed\\(.*TestException1: OK\\)".toRegex())) - error("Unexpected string: '$str'") - } -} diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt index d2ef3d2691..37db7e4526 100644 --- a/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -99,7 +99,7 @@ class ChannelUndeliveredElementFailureTest : TestBase() { fun testReceiveOrClosedCancelledFail() = runTest(unhandled = shouldBeUnhandled) { val channel = Channel(onUndeliveredElement = onCancelFail) val job = launch(start = CoroutineStart.UNDISPATCHED) { - channel.receiveOrClosed() + channel.receiveCatching() expectUnreached() // will be cancelled before it dispatches } channel.send(item) @@ -111,7 +111,7 @@ class ChannelUndeliveredElementFailureTest : TestBase() { val channel = Channel(onUndeliveredElement = onCancelFail) val job = launch(start = CoroutineStart.UNDISPATCHED) { select { - channel.onReceiveOrClosed { + channel.onReceiveCatching { expectUnreached() } } @@ -140,4 +140,4 @@ class ChannelUndeliveredElementFailureTest : TestBase() { expectUnreached() } -} \ 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 993be78e17..d28d912d17 100644 --- a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt +++ b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -43,7 +43,7 @@ private class ChannelViaBroadcast( override suspend fun receive(): E = sub.receive() override suspend fun receiveOrNull(): E? = sub.receiveOrNull() - override suspend fun receiveOrClosed(): ValueOrClosed = sub.receiveOrClosed() + override suspend fun receiveCatching(): ChannelResult = sub.receiveCatching() override fun poll(): E? = sub.poll() override fun iterator(): ChannelIterator = sub.iterator() @@ -57,6 +57,6 @@ private class ChannelViaBroadcast( get() = sub.onReceive override val onReceiveOrNull: SelectClause1 get() = sub.onReceiveOrNull - override val onReceiveOrClosed: SelectClause1> - get() = sub.onReceiveOrClosed + override val onReceiveCatching: SelectClause1> + get() = sub.onReceiveCatching } diff --git a/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt b/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt index a4f8c3ba2a..0158c84307 100644 --- a/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.selects @@ -295,10 +295,10 @@ class SelectArrayChannelTest : TestBase() { } expect(2) select { - channel.onReceiveOrClosed { + channel.onReceiveCatching { expect(5) assertTrue(it.isClosed) - assertNull(it.closeCause) + assertNull(it.exceptionOrNull()) } } @@ -316,10 +316,10 @@ class SelectArrayChannelTest : TestBase() { } expect(2) select { - channel.onReceiveOrClosed { + channel.onReceiveCatching { expect(5) assertTrue(it.isClosed) - assertTrue(it.closeCause is TestException) + assertTrue(it.exceptionOrNull() is TestException) } } @@ -327,16 +327,16 @@ class SelectArrayChannelTest : TestBase() { } @Test - fun testSelectReceiveOrClosed() = runTest { + fun testSelectReceiveCatching() = runTest { val c = Channel(1) val iterations = 10 expect(1) val job = launch { repeat(iterations) { select { - c.onReceiveOrClosed { v -> + c.onReceiveCatching { v -> expect(4 + it * 2) - assertEquals(it, v.value) + assertEquals(it, v.getOrNull()) } } } @@ -360,9 +360,9 @@ class SelectArrayChannelTest : TestBase() { launch { expect(3) val res = select { - c.onReceiveOrClosed { v -> + c.onReceiveCatching { v -> expect(6) - assertEquals(42, v.value) + assertEquals(42, v.getOrNull()) yield() // back to main expect(8) "OK" @@ -396,9 +396,9 @@ class SelectArrayChannelTest : TestBase() { expect(1) select { expect(2) - channel.onReceiveOrClosed { + channel.onReceiveCatching { assertTrue(it.isClosed) - assertNull(it.closeCause) + assertNull(it.exceptionOrNull()) finish(3) } } @@ -412,9 +412,9 @@ class SelectArrayChannelTest : TestBase() { expect(1) select { expect(2) - channel.onReceiveOrClosed { + channel.onReceiveCatching { assertFalse(it.isClosed) - assertEquals(42, it.value) + assertEquals(42, it.getOrNull()) finish(3) } } diff --git a/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt b/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt index 2027630f20..6a1576761a 100644 --- a/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913 @@ -306,7 +306,7 @@ class SelectRendezvousChannelTest : TestBase() { } @Test - fun testSelectReceiveOrClosedWaitClosed() = runTest { + fun testSelectReceiveCatchingWaitClosed() = runTest { expect(1) val channel = Channel(Channel.RENDEZVOUS) launch { @@ -316,10 +316,10 @@ class SelectRendezvousChannelTest : TestBase() { } expect(2) select { - channel.onReceiveOrClosed { + channel.onReceiveCatching { expect(5) assertTrue(it.isClosed) - assertNull(it.closeCause) + assertNull(it.exceptionOrNull()) } } @@ -327,7 +327,7 @@ class SelectRendezvousChannelTest : TestBase() { } @Test - fun testSelectReceiveOrClosedWaitClosedWithCause() = runTest { + fun testSelectReceiveCatchingWaitClosedWithCause() = runTest { expect(1) val channel = Channel(Channel.RENDEZVOUS) launch { @@ -337,10 +337,10 @@ class SelectRendezvousChannelTest : TestBase() { } expect(2) select { - channel.onReceiveOrClosed { + channel.onReceiveCatching { expect(5) assertTrue(it.isClosed) - assertTrue(it.closeCause is TestException) + assertTrue(it.exceptionOrNull() is TestException) } } @@ -348,31 +348,31 @@ class SelectRendezvousChannelTest : TestBase() { } @Test - fun testSelectReceiveOrClosedForClosedChannel() = runTest { + fun testSelectReceiveCatchingForClosedChannel() = runTest { val channel = Channel() channel.close() expect(1) select { expect(2) - channel.onReceiveOrClosed { + channel.onReceiveCatching { assertTrue(it.isClosed) - assertNull(it.closeCause) + assertNull(it.exceptionOrNull()) finish(3) } } } @Test - fun testSelectReceiveOrClosed() = runTest { + fun testSelectReceiveCatching() = runTest { val channel = Channel(Channel.RENDEZVOUS) val iterations = 10 expect(1) val job = launch { repeat(iterations) { select { - channel.onReceiveOrClosed { v -> + channel.onReceiveCatching { v -> expect(4 + it * 2) - assertEquals(it, v.value) + assertEquals(it, v.getOrThrow()) } } } @@ -390,15 +390,15 @@ class SelectRendezvousChannelTest : TestBase() { } @Test - fun testSelectReceiveOrClosedDispatch() = runTest { + fun testSelectReceiveCatchingDispatch() = runTest { val c = Channel(Channel.RENDEZVOUS) expect(1) launch { expect(3) val res = select { - c.onReceiveOrClosed { v -> + c.onReceiveCatching { v -> expect(6) - assertEquals(42, v.value) + assertEquals(42, v.getOrThrow()) yield() // back to main expect(8) "OK" diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt index 1188329a4c..3f502ba9fb 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -190,7 +190,7 @@ class ChannelUndeliveredElementStressTest(private val kind: TestChannelKind) : T 2 -> select { channel.onReceive { it } } 3 -> channel.receiveOrNull() ?: error("Should not be closed") 4 -> select { channel.onReceiveOrNull { it ?: error("Should not be closed") } } - 5 -> channel.receiveOrClosed().value + 5 -> channel.receiveCatching().getOrThrow() 6 -> { val iterator = channel.iterator() check(iterator.hasNext()) { "Should not be closed" } diff --git a/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt b/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt index 5178907875..6ce2f20d5a 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -112,13 +112,13 @@ class TickerChannelCommonTest(private val channelFactory: Channel) : TestBase() var sum = 0 var n = 0 whileSelect { - this@averageInTimeWindow.onReceiveOrClosed { + this@averageInTimeWindow.onReceiveCatching { if (it.isClosed) { // Send leftovers and bail out if (n != 0) send(sum / n.toDouble()) false } else { - sum += it.value + sum += it.getOrThrow() ++n true } From a67fd8e222628916e678d34915c14c6bab161808 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 22 Mar 2021 21:06:45 +0300 Subject: [PATCH 15/55] Introduce trySend and tryReceive channel operations as a future replacement for error-prone offer, poll and receiveOrNull Fixes #974 --- .../api/kotlinx-coroutines-core.api | 21 ++++- .../common/src/channels/AbstractChannel.kt | 43 +++++++--- .../common/src/channels/Channel.kt | 82 +++++++++++++------ .../src/channels/ConflatedBroadcastChannel.kt | 10 +-- .../common/src/flow/operators/Delay.kt | 2 +- .../test/channels/BasicOperationsTest.kt | 2 +- .../channels/ChannelReceiveCatchingTest.kt | 6 +- .../common/test/channels/TestChannelKind.kt | 4 +- .../jvm/src/channels/Actor.kt | 5 ++ ...annelCancelUndeliveredElementStressTest.kt | 4 +- .../api/kotlinx-coroutines-reactive.api | 1 + .../src/Publish.kt | 8 +- .../src/RxObservable.kt | 6 ++ .../src/RxObservable.kt | 6 +- 14 files changed, 143 insertions(+), 57 deletions(-) diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 7d2fa1f447..062e466e04 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -555,6 +555,7 @@ public abstract interface class kotlinx/coroutines/channels/ActorScope : kotlinx public final class kotlinx/coroutines/channels/ActorScope$DefaultImpls { public static synthetic fun cancel (Lkotlinx/coroutines/channels/ActorScope;)V + public static fun poll (Lkotlinx/coroutines/channels/ActorScope;)Ljava/lang/Object; } public abstract interface class kotlinx/coroutines/channels/BroadcastChannel : kotlinx/coroutines/channels/SendChannel { @@ -566,6 +567,7 @@ public abstract interface class kotlinx/coroutines/channels/BroadcastChannel : k public final class kotlinx/coroutines/channels/BroadcastChannel$DefaultImpls { public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/BroadcastChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/BroadcastChannel;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V + public static fun offer (Lkotlinx/coroutines/channels/BroadcastChannel;Ljava/lang/Object;)Z } public final class kotlinx/coroutines/channels/BroadcastChannelKt { @@ -598,6 +600,8 @@ public abstract interface class kotlinx/coroutines/channels/Channel : kotlinx/co public final class kotlinx/coroutines/channels/Channel$DefaultImpls { public static synthetic fun cancel (Lkotlinx/coroutines/channels/Channel;)V + public static fun offer (Lkotlinx/coroutines/channels/Channel;Ljava/lang/Object;)Z + public static fun poll (Lkotlinx/coroutines/channels/Channel;)Ljava/lang/Object; } public final class kotlinx/coroutines/channels/Channel$Factory { @@ -628,7 +632,7 @@ public final class kotlinx/coroutines/channels/ChannelKt { public final class kotlinx/coroutines/channels/ChannelResult { public static final field Companion Lkotlinx/coroutines/channels/ChannelResult$Companion; public static final synthetic fun box-impl (Ljava/lang/Object;)Lkotlinx/coroutines/channels/ChannelResult; - public static synthetic fun constructor-impl (Ljava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)Ljava/lang/Object; + public static fun constructor-impl (Ljava/lang/Object;)Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z public static fun equals-impl (Ljava/lang/Object;Ljava/lang/Object;)Z public static final fun equals-impl0 (Ljava/lang/Object;Ljava/lang/Object;)Z @@ -645,6 +649,12 @@ public final class kotlinx/coroutines/channels/ChannelResult { public final synthetic fun unbox-impl ()Ljava/lang/Object; } +public final class kotlinx/coroutines/channels/ChannelResult$Companion { + public final fun closed-JP2dKIU (Ljava/lang/Throwable;)Ljava/lang/Object; + public final fun failure-PtdJZtk ()Ljava/lang/Object; + public final fun success-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object; +} + public final class kotlinx/coroutines/channels/ChannelsKt { public static final fun all (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -789,6 +799,7 @@ public final class kotlinx/coroutines/channels/ConflatedBroadcastChannel : kotli public fun offer (Ljava/lang/Object;)Z public fun openSubscription ()Lkotlinx/coroutines/channels/ReceiveChannel; public fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun trySend-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object; } public final class kotlinx/coroutines/channels/ProduceKt { @@ -804,6 +815,10 @@ public abstract interface class kotlinx/coroutines/channels/ProducerScope : kotl public abstract fun getChannel ()Lkotlinx/coroutines/channels/SendChannel; } +public final class kotlinx/coroutines/channels/ProducerScope$DefaultImpls { + public static fun offer (Lkotlinx/coroutines/channels/ProducerScope;Ljava/lang/Object;)Z +} + public abstract interface class kotlinx/coroutines/channels/ReceiveChannel { public abstract synthetic fun cancel ()V public abstract synthetic fun cancel (Ljava/lang/Throwable;)Z @@ -818,12 +833,14 @@ public abstract interface class kotlinx/coroutines/channels/ReceiveChannel { public abstract fun receive (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun receiveCatching-JP2dKIU (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun receiveOrNull (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun tryReceive-PtdJZtk ()Ljava/lang/Object; } public final class kotlinx/coroutines/channels/ReceiveChannel$DefaultImpls { public static synthetic fun cancel (Lkotlinx/coroutines/channels/ReceiveChannel;)V public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V + public static fun poll (Lkotlinx/coroutines/channels/ReceiveChannel;)Ljava/lang/Object; } public abstract interface class kotlinx/coroutines/channels/SendChannel { @@ -834,10 +851,12 @@ public abstract interface class kotlinx/coroutines/channels/SendChannel { public abstract fun isFull ()Z public abstract fun offer (Ljava/lang/Object;)Z public abstract fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun trySend-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object; } public final class kotlinx/coroutines/channels/SendChannel$DefaultImpls { public static synthetic fun close$default (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z + public static fun offer (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Object;)Z } public final class kotlinx/coroutines/channels/TickerChannelsKt { diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt index 180598c94f..82143b03a1 100644 --- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt @@ -137,23 +137,42 @@ internal abstract class AbstractSendChannel( return sendSuspend(element) } - public final override fun offer(element: E): Boolean { + override fun offer(element: E): Boolean { + try { + return super.offer(element) + } catch (e: Throwable) { + onUndeliveredElement?.callUndeliveredElementCatchingException(element)?.let { + // If it crashes, add send exception as suppressed for better diagnostics + it.addSuppressed(e) + throw it + } + throw e + } + } + + public final override fun trySend(element: E): ChannelResult { val result = offerInternal(element) return when { - result === OFFER_SUCCESS -> true + result === OFFER_SUCCESS -> ChannelResult.success(Unit) result === OFFER_FAILED -> { - // We should check for closed token on offer as well, otherwise offer won't be linearizable + // We should check for closed token on trySend as well, otherwise trySend won't be linearizable // in the face of concurrent close() // See https://github.com/Kotlin/kotlinx.coroutines/issues/359 - throw recoverStackTrace(helpCloseAndGetSendException(element, closedForSend ?: return false)) + val closedForSend = closedForSend ?: return ChannelResult.failure() + ChannelResult.closed(helpCloseAndGetSendException(closedForSend)) } result is Closed<*> -> { - throw recoverStackTrace(helpCloseAndGetSendException(element, result)) + ChannelResult.closed(helpCloseAndGetSendException(result)) } - else -> error("offerInternal returned $result") + else -> error("trySend returned $result") } } + private fun helpCloseAndGetSendException(closed: Closed<*>): Throwable { + helpClose(closed) + return closed.sendException + } + private fun helpCloseAndGetSendException(element: E, closed: Closed<*>): Throwable { // To ensure linearizablity we must ALWAYS help close the channel when we observe that it was closed // See https://github.com/Kotlin/kotlinx.coroutines/issues/1419 @@ -632,9 +651,11 @@ internal abstract class AbstractChannel( } @Suppress("UNCHECKED_CAST") - public final override fun poll(): E? { + public final override fun tryReceive(): ChannelResult { val result = pollInternal() - return if (result === POLL_FAILED) null else receiveOrNullResult(result) + if (result === POLL_FAILED) return ChannelResult.failure() + if (result is Closed<*>) return ChannelResult.closed(result.closeCause) + return ChannelResult.success(result as E) } @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") @@ -905,7 +926,7 @@ internal abstract class AbstractChannel( @JvmField val receiveMode: Int ) : Receive() { fun resumeValue(value: E): Any? = when (receiveMode) { - RECEIVE_RESULT -> ChannelResult.value(value) + RECEIVE_RESULT -> ChannelResult.success(value) else -> value } @@ -990,7 +1011,7 @@ internal abstract class AbstractChannel( @Suppress("UNCHECKED_CAST") override fun completeResumeReceive(value: E) { block.startCoroutineCancellable( - if (receiveMode == RECEIVE_RESULT) ChannelResult.value(value) else value, + if (receiveMode == RECEIVE_RESULT) ChannelResult.success(value) else value, select.completion, resumeOnCancellationFun(value) ) @@ -1144,7 +1165,7 @@ internal abstract class Receive : LockFreeLinkedListNode(), ReceiveOrClose @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") private inline fun Any?.toResult(): ChannelResult = - if (this is Closed<*>) ChannelResult.closed(closeCause) else ChannelResult.value(this as E) + if (this is Closed<*>) ChannelResult.closed(closeCause) else ChannelResult.success(this as E) @Suppress("NOTHING_TO_INLINE") private inline fun Closed<*>.toResult(): ChannelResult = ChannelResult.closed(closeCause) diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 824a57bfb1..61d06d83d2 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -85,7 +85,22 @@ public interface SendChannel { * then it calls `onUndeliveredElement` before throwing an exception. * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. */ - public fun offer(element: E): Boolean + public fun offer(element: E): Boolean { + val result = trySend(element) + if (result.isSuccess) return true + throw recoverStackTrace(result.exceptionOrNull() ?: return false) + } + + /** + * Immediately adds the specified [element] to this channel, if this doesn't violate its capacity restrictions, + * and returns the successful result. Otherwise, returns failed or closed result. + * This is synchronous variant of [send], which backs off in situations when `send` suspends or throws. + * + * When `trySend` call returns a non-successful result, it guarantees that the element was not delivered to the consumer, and + * it does not call `onUndeliveredElement` that was installed for this channel. + * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. + */ + public fun trySend(element: E): ChannelResult /** * Closes this channel. @@ -218,7 +233,7 @@ public interface ReceiveChannel { @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @LowPriorityInOverloadResolution @Deprecated( - message = "Deprecated in favor of receiveOrClosed and receiveOrNull extension", + message = "Deprecated in favor of receiveCatching and receiveOrNull extension", level = DeprecationLevel.WARNING, replaceWith = ReplaceWith("receiveOrNull", "kotlinx.coroutines.channels.receiveOrNull") ) @@ -230,13 +245,13 @@ public interface ReceiveChannel { * [closed for `receive`][isClosedForReceive] without a cause. The [select] invocation fails with * the original [close][SendChannel.close] cause exception if the channel has _failed_. * - * @suppress **Deprecated**: in favor of onReceiveOrClosed and onReceiveOrNull extension. + * @suppress **Deprecated**: in favor of receiveCatching and onReceiveOrNull extension. */ @ObsoleteCoroutinesApi @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @LowPriorityInOverloadResolution @Deprecated( - message = "Deprecated in favor of onReceiveOrClosed and onReceiveOrNull extension", + message = "Deprecated in favor of receiveCatching and onReceiveOrNull extension", level = DeprecationLevel.WARNING, replaceWith = ReplaceWith("onReceiveOrNull", "kotlinx.coroutines.channels.onReceiveOrNull") ) @@ -251,7 +266,7 @@ public interface ReceiveChannel { * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this * function is suspended, this function immediately resumes with a [CancellationException]. * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. The `receiveOrClosed` call can retrieve the element from the channel, + * suspended, it will not resume successfully. The `receiveCatching` call can retrieve the element from the channel, * but then throw [CancellationException], thus failing to deliver the element. * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. * @@ -271,11 +286,22 @@ public interface ReceiveChannel { public val onReceiveCatching: SelectClause1> /** - * Retrieves and removes an element from this channel if its not empty, or returns `null` if the channel is empty + * Retrieves and removes an element from this channel if it's not empty or returns `null` if the channel is empty * or is [is closed for `receive`][isClosedForReceive] without a cause. * It throws the original [close][SendChannel.close] cause exception if the channel has _failed_. */ - public fun poll(): E? + public fun poll(): E? { + val result = tryReceive() + if (result.isSuccess) return result.getOrThrow() + throw recoverStackTrace(result.exceptionOrNull() ?: return null) + } + + /** + * Retrieves and removes an element from this channel if it's not empty, returning a [successful][ChannelResult.success] + * result, returns [failed][ChannelResult.failed] result if the channel is empty, and [closed][ChannelResult.closed] + * result if the channel is closed. + */ + public fun tryReceive(): ChannelResult /** * Returns a new iterator to receive elements from this channel using a `for` loop. @@ -315,35 +341,35 @@ public interface ReceiveChannel { /** * A discriminated union of channel operation result. - * It encapsulates successful or failed result of a channel operation, or a failed operation to a closed channel with + * It encapsulates the successful or failed result of a channel operation or a failed operation to a closed channel with * an optional cause. * - * Successful result represents a successful operation with value of type [T], for example, result of [Channel.receiveCatching] - * operation or a successfully sent element as a result of [Channel.trySend]. + * The successful result represents a successful operation with a value of type [T], for example, + * the result of [Channel.receiveCatching] operation or a successfully sent element as a result of [Channel.trySend]. * - * Failed result represents a failed operation attempt to a channel, but it doesn't necessary indicate that the channel is failed. + * The failed result represents a failed operation attempt to a channel, but it doesn't necessary indicate that the channel is failed. * E.g. when the channel is full, [Channel.trySend] returns failed result, but the channel itself is not in the failed state. * - * Closed result represents an operation attempt to a closed channel and also implies that the operation was failed. + * The closed result represents an operation attempt to a closed channel and also implies that the operation has failed. */ @Suppress("UNCHECKED_CAST") public inline class ChannelResult -internal constructor(private val holder: Any?) { +@PublishedApi internal constructor(private val holder: Any?) { /** * Returns `true` if this instance represents a successful * operation outcome. * - * In this case [isFailure] and [isClosed] return false. + * In this case [isFailure] and [isClosed] return `false`. */ - public val isSuccess: Boolean get() = holder !is Closed + public val isSuccess: Boolean get() = holder !is Failed /** - * Returns true if this instance represents unsuccessful operation. + * Returns `true` if this instance represents unsuccessful operation. * * In this case [isSuccess] returns false, but it does not imply * that the channel is failed or closed. * - * Example of failed operation without an exception and channel being closed + * Example of a failed operation without an exception and channel being closed * is [Channel.trySend] attempt to a channel that is full. */ public val isFailure: Boolean get() = holder is Failed @@ -352,7 +378,7 @@ internal constructor(private val holder: Any?) { * Returns `true` if this instance represents unsuccessful operation * to a closed or cancelled channel. * - * In this case [isSuccess] returns false, [isFailure] returns `true`, but it does not imply + * In this case [isSuccess] returns `false`, [isFailure] returns `true`, but it does not imply * that [exceptionOrNull] returns non-null value. * * It can happen if the channel was [closed][Channel.close] normally without an exception. @@ -374,7 +400,7 @@ internal constructor(private val holder: Any?) { } /** - * Returns the encapsulated exception if this instance represents failure or null if it is success + * Returns the encapsulated exception if this instance represents failure or `null` if it is success * or unsuccessful operation to closed channel. */ public fun exceptionOrNull(): Throwable? = (holder as? Closed)?.cause @@ -389,13 +415,21 @@ internal constructor(private val holder: Any?) { override fun toString(): String = "Closed($cause)" } - internal companion object { - @Suppress("NOTHING_TO_INLINE") - internal inline fun value(value: E): ChannelResult = + @Suppress("NOTHING_TO_INLINE") + @InternalCoroutinesApi + public companion object { + private val failed = Failed() + + @InternalCoroutinesApi + public fun success(value: E): ChannelResult = ChannelResult(value) - @Suppress("NOTHING_TO_INLINE") - internal inline fun closed(cause: Throwable?): ChannelResult = + @InternalCoroutinesApi + public fun failure(): ChannelResult = + ChannelResult(failed) + + @InternalCoroutinesApi + public fun closed(cause: Throwable?): ChannelResult = ChannelResult(Closed(cause)) } diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt index f1d092e34b..75524f214f 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt @@ -229,12 +229,12 @@ public class ConflatedBroadcastChannel() : BroadcastChannel { /** * Sends the value to all subscribed receives and stores this value as the most recent state for - * future subscribers. This implementation always returns `true`. - * It throws exception if the channel [isClosedForSend] (see [close] for details). + * future subscribers. This implementation always returns either successful result + * or closed with an exception. */ - public override fun offer(element: E): Boolean { - offerInternal(element)?.let { throw it.sendException } - return true + public override fun trySend(element: E): ChannelResult { + offerInternal(element)?.let { return ChannelResult.closed(it.sendException) } + return ChannelResult.success(Unit) } @Suppress("UNCHECKED_CAST") diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt b/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt index 6381c467a4..0f6ee3aca8 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt @@ -237,7 +237,7 @@ private fun Flow.debounceInternal(timeoutMillisSelector: (T) -> Long) : F lastValue = null // Consume the value } } - // Should be receiveOrClosed when boxing issues are fixed + // Should be receiveCatching when boxing issues are fixed values.onReceiveOrNull { value -> if (value == null) { if (lastValue != null) downstream.emit(NULL.unbox(lastValue)) diff --git a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt index e178f124a7..f1658cfa84 100644 --- a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt @@ -142,7 +142,7 @@ class BasicOperationsTest : TestBase() { val result = channel.receiveCatching() assertEquals(1, result.getOrThrow()) assertEquals(1, result.getOrNull()) - assertTrue(ChannelResult.value(1) == result) + assertTrue(ChannelResult.success(1) == result) expect(3) launch { diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt index 4344b1055d..2341c62ebe 100644 --- a/kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ChannelReceiveCatchingTest.kt @@ -45,7 +45,7 @@ class ChannelReceiveCatchingTest : TestBase() { assertEquals(1, element.getOrThrow()) assertEquals(1, element.getOrNull()) assertEquals("Value(1)", element.toString()) - assertTrue(ChannelResult.value(1) == element) // Don't box + assertTrue(ChannelResult.success(1) == element) // Don't box assertFalse(element.isFailure) assertFalse(element.isClosed) @@ -54,7 +54,7 @@ class ChannelReceiveCatchingTest : TestBase() { assertNull(nullElement.getOrThrow()) assertNull(nullElement.getOrNull()) assertEquals("Value(null)", nullElement.toString()) - assertTrue(ChannelResult.value(null) == nullElement) // Don't box + assertTrue(ChannelResult.success(null) == nullElement) // Don't box assertFalse(element.isFailure) assertFalse(element.isClosed) @@ -113,7 +113,7 @@ class ChannelReceiveCatchingTest : TestBase() { fun testReceiveResultChannel() = runTest { val channel = Channel>() launch { - channel.send(ChannelResult.value(1u)) + channel.send(ChannelResult.success(1u)) channel.send(ChannelResult.closed(TestException1())) channel.close(TestException2()) } diff --git a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt index d28d912d17..3e70007a6e 100644 --- a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt +++ b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt @@ -44,9 +44,9 @@ private class ChannelViaBroadcast( override suspend fun receive(): E = sub.receive() override suspend fun receiveOrNull(): E? = sub.receiveOrNull() override suspend fun receiveCatching(): ChannelResult = sub.receiveCatching() - override fun poll(): E? = sub.poll() override fun iterator(): ChannelIterator = sub.iterator() - + override fun tryReceive(): ChannelResult = sub.tryReceive() + override fun cancel(cause: CancellationException?) = sub.cancel(cause) // implementing hidden method anyway, so can cast to an internal class diff --git a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt index 3a208c0f70..5153cb47f1 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt @@ -168,6 +168,11 @@ private class LazyActorCoroutine( return super.offer(element) } + override fun trySend(element: E): ChannelResult { + start() + return super.trySend(element) + } + override fun close(cause: Throwable?): Boolean { // close the channel _first_ val closed = super.close(cause) diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt index 76713aa173..9f7ce497fa 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -99,4 +99,4 @@ class ChannelCancelUndeliveredElementStressTest : TestBase() { dReceivedCnt++ lastReceived = received } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api b/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api index 961fdbe238..6798625179 100644 --- a/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api +++ b/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api @@ -63,6 +63,7 @@ public final class kotlinx/coroutines/reactive/PublisherCoroutine : kotlinx/coro public fun registerSelectClause2 (Lkotlinx/coroutines/selects/SelectInstance;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V public fun request (J)V public fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun trySend-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object; } public final class kotlinx/coroutines/reactive/ReactiveFlowKt { diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 156205624e..6bb02ef192 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.selects.* import kotlinx.coroutines.sync.* import org.reactivestreams.* import kotlin.coroutines.* -import kotlin.internal.LowPriorityInOverloadResolution +import kotlin.internal.* /** * Creates cold reactive [Publisher] that runs a given [block] in a coroutine. @@ -96,10 +96,10 @@ public class PublisherCoroutine( override fun invokeOnClose(handler: (Throwable?) -> Unit): Nothing = throw UnsupportedOperationException("PublisherCoroutine doesn't support invokeOnClose") - override fun offer(element: T): Boolean { - if (!mutex.tryLock()) return false + override fun trySend(element: T): ChannelResult { + if (!mutex.tryLock()) return ChannelResult.failure() doLockedNext(element) - return true + return ChannelResult.success(Unit) } public override suspend fun send(element: T) { diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index 4f4706d8ad..7813bc7548 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -91,6 +91,12 @@ private class RxObservableCoroutine( return true } + override fun trySend(element: T): ChannelResult { + if (!mutex.tryLock()) return ChannelResult.failure() + doLockedNext(element) + return ChannelResult.success(Unit) + } + public override suspend fun send(element: T) { // fast-path -- try send without suspension if (offer(element)) return diff --git a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt index bd9239e7db..fed89c328e 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt @@ -71,10 +71,10 @@ private class RxObservableCoroutine( override fun invokeOnClose(handler: (Throwable?) -> Unit) = throw UnsupportedOperationException("RxObservableCoroutine doesn't support invokeOnClose") - override fun offer(element: T): Boolean { - if (!mutex.tryLock()) return false + override fun trySend(element: T): ChannelResult { + if (!mutex.tryLock()) return ChannelResult.failure() doLockedNext(element) - return true + return ChannelResult.success(Unit) } public override suspend fun send(element: T) { From 2021b5aee1ad93f48bd6e54915ad4e09f11300c7 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 25 Mar 2021 16:43:25 +0300 Subject: [PATCH 16/55] Update contribution guide (#2608) --- CONTRIBUTING.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7737062fa3..4628eade47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,6 +52,10 @@ so do familiarize yourself with the following guidelines. * Follow the style of writing tests that is used in this project: name test functions as `testXxx`. Don't use backticks in test names. * If you introduce any new public APIs: + * Comment on the existing issue if you want to work on it or create one beforehand. + Ensure that the issue not only describes a problem, but also describes a solution that had received a positive feedback. Propose a solution if there isn't any. + PRs with new API, but without a corresponding issue with a positive feedback about the proposed implementation are unlikely to + be approved or reviewed. * All new APIs must come with documentation and tests. * All new APIs are initially released with `@ExperimentalCoroutineApi` annotation and are graduated later. * [Update the public API dumps](#updating-the-public-api-dump) and commit the resulting changes as well. @@ -59,8 +63,6 @@ so do familiarize yourself with the following guidelines. * If you plan large API additions, then please start by submitting an issue with the proposed API design to gather community feedback. * [Contact the maintainers](#contacting-maintainers) to coordinate any big piece of work in advance. -* Comment on the existing issue if you want to work on it. Ensure that the issue not only describes a problem, - but also describes a solution that had received a positive feedback. Propose a solution if there isn't any. * Steps for contributing new integration modules are explained [here](integration/README.md#Contributing). ## Building From ea039ea004ae6f533889fdef9af23151d76e916e Mon Sep 17 00:00:00 2001 From: koshachy Date: Tue, 6 Apr 2021 12:06:40 +0300 Subject: [PATCH 17/55] update: delete -Xcoroutines flag since it will be removed in Kotlin 1.5.0 (#2620) --- docs/topics/coroutines-basic-jvm.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/topics/coroutines-basic-jvm.md b/docs/topics/coroutines-basic-jvm.md index 3b067ea86b..a6ca3ba5f3 100644 --- a/docs/topics/coroutines-basic-jvm.md +++ b/docs/topics/coroutines-basic-jvm.md @@ -56,11 +56,6 @@ Make sure it's configured for Kotlin 1.3 or higher. org.jetbrains.kotlin kotlin-maven-plugin ... - - - -Xcoroutines=enable - - ``` From 7ae273c6860b81914108ea890064f29f1802139f Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Tue, 6 Apr 2021 13:23:06 +0300 Subject: [PATCH 18/55] Fix wrong docs on Job.join (#2616) * Fix wrong docs on Job.join and Job.cancelAndJoin Fixes #2615 --- kotlinx-coroutines-core/common/src/Job.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/Job.kt b/kotlinx-coroutines-core/common/src/Job.kt index 1a98135a24..be58213288 100644 --- a/kotlinx-coroutines-core/common/src/Job.kt +++ b/kotlinx-coroutines-core/common/src/Job.kt @@ -252,9 +252,9 @@ public interface Job : CoroutineContext.Element { * suspending function is invoked or while it is suspended, this function * throws [CancellationException]. * - * In particular, it means that a parent coroutine invoking `join` on a child coroutine that was started using - * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child - * had crashed, unless a non-standard [CoroutineExceptionHandler] is installed in the context. + * In particular, it means that a parent coroutine invoking `join` on a child coroutine throws + * [CancellationException] if the child had failed, since a failure of a child coroutine cancels parent by default, + * unless the child was launched from within [supervisorScope]. * * This function can be used in [select] invocation with [onJoin] clause. * Use [isCompleted] to check for a completion of this job without waiting. @@ -498,9 +498,9 @@ internal fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle * suspending function is invoked or while it is suspended, this function * throws [CancellationException]. * - * In particular, it means that a parent coroutine invoking `cancelAndJoin` on a child coroutine that was started using - * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child - * had crashed, unless a non-standard [CoroutineExceptionHandler] is installed in the context. + * In particular, it means that a parent coroutine invoking `cancelAndJoin` on a child coroutine throws + * [CancellationException] if the child had failed, since a failure of a child coroutine cancels parent by default, + * unless the child was launched from within [supervisorScope]. * * This is a shortcut for the invocation of [cancel][Job.cancel] followed by [join][Job.join]. */ From 98532c9f1e81feac232357c94a24db795d6576e5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 6 Apr 2021 17:25:50 +0300 Subject: [PATCH 19/55] Deprecation and migration of receiveOrNull and onReceiveOrNull. (#2612) * Deprecation and migration of receiveOrNull and onReceiveOrNull. * Raise deprecation level for members, introduce deprecation for extensions * Explain rationale behind deprecation * Provide default implementation for deprecated members in Channel interface * Get rid of the internal implementation, leverage receiveCatching * Introduce new extensions for ChannelResult and use them as a replacement in our own operators Fixes #1676 --- docs/topics/select-expression.md | 69 ++++++----- .../test/time/FlowSampleTest.kt | 5 +- kotlinx-coroutines-core/README.md | 14 +-- .../api/kotlinx-coroutines-core.api | 9 ++ kotlinx-coroutines-core/common/README.md | 6 +- .../common/src/channels/AbstractChannel.kt | 43 +------ .../common/src/channels/Channel.kt | 112 ++++++++++++------ .../common/src/channels/Channels.common.kt | 42 +++---- .../common/src/flow/internal/Combine.kt | 6 +- .../common/src/flow/operators/Delay.kt | 38 +++--- .../common/src/selects/Select.kt | 20 ++-- .../channels/ArrayBroadcastChannelTest.kt | 16 +-- .../common/test/channels/ArrayChannelTest.kt | 14 +-- .../test/channels/BasicOperationsTest.kt | 37 +----- .../ChannelUndeliveredElementFailureTest.kt | 28 +---- .../channels/ConflatedBroadcastChannelTest.kt | 4 +- .../test/channels/ConflatedChannelTest.kt | 10 +- .../test/channels/LinkedListChannelTest.kt | 10 +- .../common/test/channels/ProduceTest.kt | 6 +- .../test/channels/RendezvousChannelTest.kt | 16 +-- .../common/test/channels/TestChannelKind.kt | 3 - .../common/test/selects/SelectLoopTest.kt | 6 +- .../testReceiveOrNullFromClosedChannel.txt | 8 -- .../jvm/test/channels/ActorTest.kt | 14 +-- .../BroadcastChannelMultiReceiveStressTest.kt | 16 +-- ...annelCancelUndeliveredElementStressTest.kt | 2 +- .../channels/ChannelSendReceiveStressTest.kt | 16 +-- .../ChannelUndeliveredElementStressTest.kt | 4 +- .../ConflatedChannelCloseStressTest.kt | 8 +- .../StackTraceRecoveryChannelsTest.kt | 13 +- .../jvm/test/guide/example-select-02.kt | 20 ++-- .../jvm/test/guide/example-select-05.kt | 8 +- .../src/ReactiveFlow.kt | 10 +- .../test/CancelledParentAttachTest.kt | 3 +- .../test/PublisherAsFlowTest.kt | 12 +- .../test/PublisherSubscriptionSelectTest.kt | 21 ++-- .../test/ObservableSubscriptionSelectTest.kt | 22 ++-- .../test/ObservableSubscriptionSelectTest.kt | 20 ++-- 38 files changed, 339 insertions(+), 372 deletions(-) delete mode 100644 kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveOrNullFromClosedChannel.txt diff --git a/docs/topics/select-expression.md b/docs/topics/select-expression.md index 082a50d65b..3d20ff39d4 100644 --- a/docs/topics/select-expression.md +++ b/docs/topics/select-expression.md @@ -120,31 +120,32 @@ buzz -> 'Buzz!' ## Selecting on close The [onReceive][ReceiveChannel.onReceive] clause in `select` fails when the channel is closed causing the corresponding -`select` to throw an exception. We can use [onReceiveOrNull][onReceiveOrNull] clause to perform a +`select` to throw an exception. We can use [onReceiveCatching][ReceiveChannel.onReceiveCatching] clause to perform a specific action when the channel is closed. The following example also shows that `select` is an expression that returns the result of its selected clause: ```kotlin suspend fun selectAorB(a: ReceiveChannel, b: ReceiveChannel): String = select { - a.onReceiveOrNull { value -> - if (value == null) - "Channel 'a' is closed" - else + a.onReceiveCatching { it -> + val value = it.getOrNull() + if (value != null) { "a -> '$value'" + } else { + "Channel 'a' is closed" + } } - b.onReceiveOrNull { value -> - if (value == null) - "Channel 'b' is closed" - else + b.onReceiveCatching { it -> + val value = it.getOrNull() + if (value != null) { "b -> '$value'" + } else { + "Channel 'b' is closed" + } } } ``` -Note that [onReceiveOrNull][onReceiveOrNull] is an extension function defined only -for channels with non-nullable elements so that there is no accidental confusion between a closed channel -and a null value. Let's use it with channel `a` that produces "Hello" string four times and channel `b` that produces "World" four times: @@ -158,17 +159,21 @@ import kotlinx.coroutines.selects.* suspend fun selectAorB(a: ReceiveChannel, b: ReceiveChannel): String = select { - a.onReceiveOrNull { value -> - if (value == null) - "Channel 'a' is closed" - else + a.onReceiveCatching { it -> + val value = it.getOrNull() + if (value != null) { "a -> '$value'" + } else { + "Channel 'a' is closed" + } } - b.onReceiveOrNull { value -> - if (value == null) - "Channel 'b' is closed" - else + b.onReceiveCatching { it -> + val value = it.getOrNull() + if (value != null) { "b -> '$value'" + } else { + "Channel 'b' is closed" + } } } @@ -215,7 +220,7 @@ the first one among them gets selected. Here, both channels are constantly produ being the first clause in select, wins. However, because we are using unbuffered channel, the `a` gets suspended from time to time on its [send][SendChannel.send] invocation and gives a chance for `b` to send, too. -The second observation, is that [onReceiveOrNull][onReceiveOrNull] gets immediately selected when the +The second observation, is that [onReceiveCatching][ReceiveChannel.onReceiveCatching] gets immediately selected when the channel is already closed. ## Selecting to send @@ -375,19 +380,19 @@ Deferred 4 produced answer 'Waited for 128 ms' Let us write a channel producer function that consumes a channel of deferred string values, waits for each received deferred value, but only until the next deferred value comes over or the channel is closed. This example puts together -[onReceiveOrNull][onReceiveOrNull] and [onAwait][Deferred.onAwait] clauses in the same `select`: +[onReceiveCatching][ReceiveChannel.onReceiveCatching] and [onAwait][Deferred.onAwait] clauses in the same `select`: ```kotlin fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel>) = produce { var current = input.receive() // start with first received deferred value while (isActive) { // loop while not cancelled/closed val next = select?> { // return next deferred value from this select or null - input.onReceiveOrNull { update -> - update // replaces next value to wait + input.onReceiveCatching { update -> + update.getOrNull() } - current.onAwait { value -> + current.onAwait { value -> send(value) // send value that current deferred has produced - input.receiveOrNull() // and use the next deferred from the input channel + input.receiveCatching().getOrNull() // and use the next deferred from the input channel } } if (next == null) { @@ -423,12 +428,12 @@ fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel>) = var current = input.receive() // start with first received deferred value while (isActive) { // loop while not cancelled/closed val next = select?> { // return next deferred value from this select or null - input.onReceiveOrNull { update -> - update // replaces next value to wait + input.onReceiveCatching { update -> + update.getOrNull() } - current.onAwait { value -> + current.onAwait { value -> send(value) // send value that current deferred has produced - input.receiveOrNull() // and use the next deferred from the input channel + input.receiveCatching().getOrNull() // and use the next deferred from the input channel } } if (next == null) { @@ -491,7 +496,7 @@ Channel was closed [ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html [ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html -[onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html +[ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html [SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/send.html [SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html @@ -499,4 +504,4 @@ Channel was closed [select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html - \ No newline at end of file + diff --git a/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt b/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt index 11ceb1a831..d35ee72de0 100644 --- a/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt +++ b/integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.time @@ -12,8 +12,7 @@ import org.junit.Test import java.time.Duration import kotlin.test.assertEquals - -class SampleTest : TestBase() { +class FlowSampleTest : TestBase() { @Test public fun testBasic() = withVirtualTime { expect(1) diff --git a/kotlinx-coroutines-core/README.md b/kotlinx-coroutines-core/README.md index bc5587623a..9fdf418233 100644 --- a/kotlinx-coroutines-core/README.md +++ b/kotlinx-coroutines-core/README.md @@ -54,9 +54,9 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio | ---------------- | --------------------------------------------- | ------------------------------------------------ | -------------------------- | [Job] | [join][Job.join] | [onJoin][Job.onJoin] | [isCompleted][Job.isCompleted] | [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted] -| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [offer][kotlinx.coroutines.channels.SendChannel.offer] -| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll] -| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveOrNull][kotlinx.coroutines.channels.receiveOrNull] | [onReceiveOrNull][kotlinx.coroutines.channels.onReceiveOrNull] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll] +| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [trySend][kotlinx.coroutines.channels.SendChannel.trySend] +| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] +| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive] | [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock] | none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none @@ -133,11 +133,11 @@ Obsolete and deprecated module to test coroutines. Replaced with `kotlinx-corout [kotlinx.coroutines.channels.ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html [kotlinx.coroutines.channels.SendChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/index.html [kotlinx.coroutines.channels.SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html -[kotlinx.coroutines.channels.SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html +[kotlinx.coroutines.channels.SendChannel.trySend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html [kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html -[kotlinx.coroutines.channels.ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/poll.html -[kotlinx.coroutines.channels.receiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/receive-or-null.html -[kotlinx.coroutines.channels.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html +[kotlinx.coroutines.channels.ReceiveChannel.tryReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/try-receive.html +[kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html +[kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 062e466e04..2f2b8d54ab 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -555,7 +555,9 @@ public abstract interface class kotlinx/coroutines/channels/ActorScope : kotlinx public final class kotlinx/coroutines/channels/ActorScope$DefaultImpls { public static synthetic fun cancel (Lkotlinx/coroutines/channels/ActorScope;)V + public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/ActorScope;)Lkotlinx/coroutines/selects/SelectClause1; public static fun poll (Lkotlinx/coroutines/channels/ActorScope;)Ljava/lang/Object; + public static fun receiveOrNull (Lkotlinx/coroutines/channels/ActorScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract interface class kotlinx/coroutines/channels/BroadcastChannel : kotlinx/coroutines/channels/SendChannel { @@ -600,8 +602,10 @@ public abstract interface class kotlinx/coroutines/channels/Channel : kotlinx/co public final class kotlinx/coroutines/channels/Channel$DefaultImpls { public static synthetic fun cancel (Lkotlinx/coroutines/channels/Channel;)V + public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/Channel;)Lkotlinx/coroutines/selects/SelectClause1; public static fun offer (Lkotlinx/coroutines/channels/Channel;Ljava/lang/Object;)Z public static fun poll (Lkotlinx/coroutines/channels/Channel;)Ljava/lang/Object; + public static fun receiveOrNull (Lkotlinx/coroutines/channels/Channel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlinx/coroutines/channels/Channel$Factory { @@ -627,6 +631,9 @@ public final class kotlinx/coroutines/channels/ChannelKt { public static final fun Channel (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/channels/Channel; public static synthetic fun Channel$default (IILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel; public static synthetic fun Channel$default (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel; + public static final fun getOrElse-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static final fun onFailure-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static final fun onSuccess-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } public final class kotlinx/coroutines/channels/ChannelResult { @@ -840,7 +847,9 @@ public final class kotlinx/coroutines/channels/ReceiveChannel$DefaultImpls { public static synthetic fun cancel (Lkotlinx/coroutines/channels/ReceiveChannel;)V public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V + public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/selects/SelectClause1; public static fun poll (Lkotlinx/coroutines/channels/ReceiveChannel;)Ljava/lang/Object; + public static fun receiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract interface class kotlinx/coroutines/channels/SendChannel { diff --git a/kotlinx-coroutines-core/common/README.md b/kotlinx-coroutines-core/common/README.md index 6712648ae8..e8503d0d16 100644 --- a/kotlinx-coroutines-core/common/README.md +++ b/kotlinx-coroutines-core/common/README.md @@ -59,7 +59,7 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio | [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted] | [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [offer][kotlinx.coroutines.channels.SendChannel.offer] | [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll] -| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveOrNull][kotlinx.coroutines.channels.receiveOrNull] | [onReceiveOrNull][kotlinx.coroutines.channels.onReceiveOrNull] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll] +| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll] | [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock] | none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none @@ -149,8 +149,8 @@ Low-level primitives for finer-grained control of coroutines. [kotlinx.coroutines.channels.SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html [kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html [kotlinx.coroutines.channels.ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/poll.html -[kotlinx.coroutines.channels.receiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/receive-or-null.html -[kotlinx.coroutines.channels.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html +[kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html +[kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt index 82143b03a1..04a9d1a64a 100644 --- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt @@ -623,24 +623,6 @@ internal abstract class AbstractChannel( if (result) onReceiveEnqueued() } - public final override suspend fun receiveOrNull(): E? { - // fast path -- try poll non-blocking - val result = pollInternal() - @Suppress("UNCHECKED_CAST") - if (result !== POLL_FAILED && result !is Closed<*>) return result as E - // slow-path does suspend - return receiveSuspend(RECEIVE_NULL_ON_CLOSE) - } - - @Suppress("UNCHECKED_CAST") - private fun receiveOrNullResult(result: Any?): E? { - if (result is Closed<*>) { - if (result.closeCause != null) throw recoverStackTrace(result.closeCause) - return null - } - return result as E - } - @Suppress("UNCHECKED_CAST") public final override suspend fun receiveCatching(): ChannelResult { // fast path -- try poll non-blocking @@ -755,14 +737,6 @@ internal abstract class AbstractChannel( } } - final override val onReceiveOrNull: SelectClause1 - get() = object : SelectClause1 { - @Suppress("UNCHECKED_CAST") - override fun registerSelectClause1(select: SelectInstance, block: suspend (E?) -> R) { - registerSelectReceiveMode(select, RECEIVE_NULL_ON_CLOSE, block as suspend (Any?) -> R) - } - } - final override val onReceiveCatching: SelectClause1> get() = object : SelectClause1> { @Suppress("UNCHECKED_CAST") @@ -799,14 +773,6 @@ internal abstract class AbstractChannel( if (!select.trySelect()) return startCoroutineUnintercepted(ChannelResult.closed(value.closeCause), select.completion) } - RECEIVE_NULL_ON_CLOSE -> { - if (value.closeCause == null) { - if (!select.trySelect()) return - startCoroutineUnintercepted(null, select.completion) - } else { - throw recoverStackTrace(value.receiveException) - } - } } } else -> { @@ -942,7 +908,6 @@ internal abstract class AbstractChannel( override fun resumeReceiveClosed(closed: Closed<*>) { when { - receiveMode == RECEIVE_NULL_ON_CLOSE && closed.closeCause == null -> cont.resume(null) receiveMode == RECEIVE_RESULT -> cont.resume(closed.toResult()) else -> cont.resumeWithException(closed.receiveException) } @@ -1022,11 +987,6 @@ internal abstract class AbstractChannel( when (receiveMode) { RECEIVE_THROWS_ON_CLOSE -> select.resumeSelectWithException(closed.receiveException) RECEIVE_RESULT -> block.startCoroutineCancellable(ChannelResult.closed(closed.closeCause), select.completion) - RECEIVE_NULL_ON_CLOSE -> if (closed.closeCause == null) { - block.startCoroutineCancellable(null, select.completion) - } else { - select.resumeSelectWithException(closed.receiveException) - } } } @@ -1044,8 +1004,7 @@ internal abstract class AbstractChannel( // receiveMode values internal const val RECEIVE_THROWS_ON_CLOSE = 0 -internal const val RECEIVE_NULL_ON_CLOSE = 1 -internal const val RECEIVE_RESULT = 2 +internal const val RECEIVE_RESULT = 1 @JvmField @SharedImmutable diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 61d06d83d2..8aad264d2c 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.channels.Channel.Factory.RENDEZVOUS import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED import kotlinx.coroutines.internal.* import kotlinx.coroutines.selects.* +import kotlin.contracts.* import kotlin.internal.* import kotlin.jvm.* @@ -210,52 +211,49 @@ public interface ReceiveChannel { public val onReceive: SelectClause1 /** - * Retrieves and removes an element from this channel if it's not empty, or suspends the caller while the channel is empty, - * or returns `null` if the channel is [closed for `receive`][isClosedForReceive] without cause, - * or throws the original [close][SendChannel.close] cause exception if the channel has _failed_. - * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with a [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. The `receiveOrNull` call can retrieve the element from the channel, - * but then throw [CancellationException], thus failing to deliver the element. - * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. - * - * Note that this function does not check for cancellation when it is not suspended. - * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. + * This function was deprecated since 1.3.0 and is no longer recommended to use + * or to implement in subclasses. * - * This function can be used in [select] invocations with the [onReceiveOrNull] clause. - * Use [poll] to try receiving from this channel without waiting. + * It had the following pitfalls: + * - Didn't allow to distinguish 'null' as "closed channel" from "null as a value" + * - Was throwing if the channel has failed even though its signature may suggest it returns 'null' + * - It didn't really belong to core channel API and can be exposed as an extension instead. * - * @suppress **Deprecated**: in favor of receiveOrClosed and receiveOrNull extension. + * @suppress doc */ - @ObsoleteCoroutinesApi @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @LowPriorityInOverloadResolution @Deprecated( - message = "Deprecated in favor of receiveCatching and receiveOrNull extension", - level = DeprecationLevel.WARNING, - replaceWith = ReplaceWith("receiveOrNull", "kotlinx.coroutines.channels.receiveOrNull") - ) - public suspend fun receiveOrNull(): E? + message = "Deprecated in favor of receiveCatching", + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("receiveCatching().getOrNull()") + ) // Warning since 1.3.0, error in 1.5.0, will be hidden in 1.6.0 + public suspend fun receiveOrNull(): E? = receiveCatching().getOrNull() /** - * Clause for the [select] expression of the [receiveOrNull] suspending function that selects with the element - * received from the channel or `null` if the channel is - * [closed for `receive`][isClosedForReceive] without a cause. The [select] invocation fails with - * the original [close][SendChannel.close] cause exception if the channel has _failed_. + * This function was deprecated since 1.3.0 and is no longer recommended to use + * or to implement in subclasses. + * See [receiveOrNull] documentation. * - * @suppress **Deprecated**: in favor of receiveCatching and onReceiveOrNull extension. + * @suppress **Deprecated**: in favor of onReceiveCatching extension. */ - @ObsoleteCoroutinesApi - @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - @LowPriorityInOverloadResolution @Deprecated( - message = "Deprecated in favor of receiveCatching and onReceiveOrNull extension", - level = DeprecationLevel.WARNING, - replaceWith = ReplaceWith("onReceiveOrNull", "kotlinx.coroutines.channels.onReceiveOrNull") - ) + message = "Deprecated in favor of onReceiveCatching extension", + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("onReceiveCatching") + ) // Warning since 1.3.0, error in 1.5.0, will be hidden or removed in 1.6.0 public val onReceiveOrNull: SelectClause1 + get() { + return object : SelectClause1 { + @InternalCoroutinesApi + override fun registerSelectClause1(select: SelectInstance, block: suspend (E?) -> R) { + onReceiveCatching.registerSelectClause1(select) { + it.exceptionOrNull()?.let { throw it } + block(it.getOrNull()) + } + } + } + } /** * Retrieves and removes an element from this channel if it's not empty, or suspends the caller while this channel is empty. @@ -354,7 +352,7 @@ public interface ReceiveChannel { */ @Suppress("UNCHECKED_CAST") public inline class ChannelResult -@PublishedApi internal constructor(private val holder: Any?) { +@PublishedApi internal constructor(@PublishedApi internal val holder: Any?) { /** * Returns `true` if this instance represents a successful * operation outcome. @@ -440,6 +438,50 @@ public inline class ChannelResult } } +/** + * Returns the encapsulated value if this instance represents [success][ChannelResult.isSuccess] or the + * result of [onFailure] function for the encapsulated [Throwable] exception if it is failed or closed + * result. + */ +@OptIn(ExperimentalContracts::class) +public inline fun ChannelResult.getOrElse(onFailure: (exception: Throwable?) -> T): T { + contract { + callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE) + } + @Suppress("UNCHECKED_CAST") + return if (holder is ChannelResult.Failed) onFailure(exceptionOrNull()) else holder as T +} + +/** + * Performs the given [action] on the encapsulated value if this instance represents [success][ChannelResult.isSuccess]. + * Returns the original `ChannelResult` unchanged. + */ +@OptIn(ExperimentalContracts::class) +public inline fun ChannelResult.onSuccess(action: (value: T) -> Unit): ChannelResult { + contract { + callsInPlace(action, InvocationKind.AT_MOST_ONCE) + } + @Suppress("UNCHECKED_CAST") + if (holder !is ChannelResult.Failed) action(holder as T) + return this +} + +/** + * Performs the given [action] on the encapsulated [Throwable] exception if this instance represents [failure][ChannelResult.isFailure]. + * The result of [ChannelResult.exceptionOrNull] is passed to the [action] parameter. + * + * Returns the original `ChannelResult` unchanged. + */ +@OptIn(ExperimentalContracts::class) +public inline fun ChannelResult.onFailure(action: (exception: Throwable?) -> Unit): ChannelResult { + contract { + callsInPlace(action, InvocationKind.AT_MOST_ONCE) + } + @Suppress("UNCHECKED_CAST") + if (holder is ChannelResult.Failed) action(exceptionOrNull()) + return this +} + /** * Iterator for [ReceiveChannel]. Instances of this interface are *not thread-safe* and shall not be used * from concurrent coroutines. diff --git a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt index e3567e3107..6bf6f88123 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt @@ -37,40 +37,34 @@ public inline fun BroadcastChannel.consume(block: ReceiveChannel.() } /** - * Retrieves and removes the element from this channel suspending the caller while this channel [isEmpty] - * or returns `null` if the channel is [closed][Channel.isClosedForReceive]. + * This function is deprecated in the favour of [ReceiveChannel.receiveCatching]. * - * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this - * function is suspended, this function immediately resumes with [CancellationException]. - * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was - * suspended, it will not resume successfully. If the `receiveOrNull` call threw [CancellationException] there is no way - * to tell if some element was already received from the channel or not. See [Channel] documentation for details. + * This function is considered error-prone for the following reasons; + * * Is throwing if the channel has failed even though its signature may suggest it returns 'null' + * * It is easy to forget that exception handling still have to be explicit + * * During code reviews and code reading, intentions of the code are frequently unclear: + * are potential exceptions ignored deliberately or not? * - * Note, that this function does not check for cancellation when it is not suspended. - * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. - * - * This extension is defined only for channels on non-null types, so that generic functions defined using - * these extensions do not accidentally confuse `null` value and a normally closed channel, leading to hard - * to find bugs. + * @suppress doc */ +@Deprecated( + "Deprecated in the favour of 'receiveCatching'", + ReplaceWith("receiveCatching().getOrNull()"), + DeprecationLevel.WARNING +) // Warning since 1.5.0, ERROR in 1.6.0, HIDDEN in 1.7.0 @Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@ExperimentalCoroutinesApi // since 1.3.0, tentatively stable in 1.4.x public suspend fun ReceiveChannel.receiveOrNull(): E? { @Suppress("DEPRECATION", "UNCHECKED_CAST") return (this as ReceiveChannel).receiveOrNull() } /** - * Clause for [select] expression of [receiveOrNull] suspending function that selects with the element that - * is received from the channel or selects with `null` if the channel - * [isClosedForReceive][ReceiveChannel.isClosedForReceive] without cause. The [select] invocation fails with - * the original [close][SendChannel.close] cause exception if the channel has _failed_. - * - * This extension is defined only for channels on non-null types, so that generic functions defined using - * these extensions do not accidentally confuse `null` value and a normally closed channel, leading to hard - * to find bugs. - **/ -@ExperimentalCoroutinesApi // since 1.3.0, tentatively stable in 1.4.x + * This function is deprecated in the favour of [ReceiveChannel.onReceiveCatching] + */ +@Deprecated( + "Deprecated in the favour of 'onReceiveCatching'", + level = DeprecationLevel.WARNING +) // Warning since 1.5.0, ERROR in 1.6.0, HIDDEN in 1.7.0 public fun ReceiveChannel.onReceiveOrNull(): SelectClause1 { @Suppress("DEPRECATION", "UNCHECKED_CAST") return (this as ReceiveChannel).onReceiveOrNull diff --git a/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt b/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt index 7fde06362a..6e5f3f11aa 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt @@ -54,7 +54,7 @@ internal suspend fun FlowCollector.combineInternal( ++currentEpoch // Start batch // The very first receive in epoch should be suspending - var element = resultChannel.receiveOrNull() ?: break // Channel is closed, nothing to do here + var element = resultChannel.receiveCatching().getOrNull() ?: break // Channel is closed, nothing to do here while (true) { val index = element.index // Update values @@ -129,7 +129,9 @@ internal fun zipImpl(flow: Flow, flow2: Flow, transform: sus withContextUndispatched(coroutineContext + collectJob, Unit) { flow.collect { value -> withContextUndispatched(scopeContext, Unit, cnt) { - val otherValue = second.receiveOrNull() ?: throw AbortFlowException(this@unsafeFlow) + val otherValue = second.receiveCatching().getOrElse { + throw it ?:AbortFlowException(this@unsafeFlow) + } emit(transform(value, NULL.unbox(otherValue))) } } diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt b/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt index 0f6ee3aca8..fed5962bd5 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Delay.kt @@ -209,8 +209,7 @@ public fun Flow.debounce(timeout: (T) -> Duration): Flow = private fun Flow.debounceInternal(timeoutMillisSelector: (T) -> Long) : Flow = scopedFlow { downstream -> // Produce the values using the default (rendezvous) channel - // Note: the actual type is Any, KT-30796 - val values = produce { + val values = produce { collect { value -> send(value ?: NULL) } } // Now consume the values @@ -237,14 +236,15 @@ private fun Flow.debounceInternal(timeoutMillisSelector: (T) -> Long) : F lastValue = null // Consume the value } } - // Should be receiveCatching when boxing issues are fixed - values.onReceiveOrNull { value -> - if (value == null) { - if (lastValue != null) downstream.emit(NULL.unbox(lastValue)) - lastValue = DONE - } else { - lastValue = value - } + values.onReceiveCatching { value -> + value + .onSuccess { lastValue = it } + .onFailure { + it?.let { throw it } + // If closed normally, emit the latest value + if (lastValue != null) downstream.emit(NULL.unbox(lastValue)) + lastValue = DONE + } } } } @@ -278,21 +278,21 @@ private fun Flow.debounceInternal(timeoutMillisSelector: (T) -> Long) : F public fun Flow.sample(periodMillis: Long): Flow { require(periodMillis > 0) { "Sample period should be positive" } return scopedFlow { downstream -> - val values = produce(capacity = Channel.CONFLATED) { - // Actually Any, KT-30796 + val values = produce(capacity = Channel.CONFLATED) { collect { value -> send(value ?: NULL) } } var lastValue: Any? = null val ticker = fixedPeriodTicker(periodMillis) while (lastValue !== DONE) { select { - values.onReceiveOrNull { - if (it == null) { - ticker.cancel(ChildCancelledException()) - lastValue = DONE - } else { - lastValue = it - } + values.onReceiveCatching { result -> + result + .onSuccess { lastValue = it } + .onFailure { + it?.let { throw it } + ticker.cancel(ChildCancelledException()) + lastValue = DONE + } } // todo: shall be start sampling only when an element arrives or sample aways as here? diff --git a/kotlinx-coroutines-core/common/src/selects/Select.kt b/kotlinx-coroutines-core/common/src/selects/Select.kt index 0d97400717..a7172707e2 100644 --- a/kotlinx-coroutines-core/common/src/selects/Select.kt +++ b/kotlinx-coroutines-core/common/src/selects/Select.kt @@ -177,15 +177,17 @@ public interface SelectInstance { * corresponding non-suspending version that can be used with a regular `when` expression to select one * of the alternatives or to perform the default (`else`) action if none of them can be immediately selected. * - * | **Receiver** | **Suspending function** | **Select clause** | **Non-suspending version** - * | ---------------- | --------------------------------------------- | ------------------------------------------------ | -------------------------- - * | [Job] | [join][Job.join] | [onJoin][Job.onJoin] | [isCompleted][Job.isCompleted] - * | [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted] - * | [SendChannel] | [send][SendChannel.send] | [onSend][SendChannel.onSend] | [offer][SendChannel.offer] - * | [ReceiveChannel] | [receive][ReceiveChannel.receive] | [onReceive][ReceiveChannel.onReceive] | [poll][ReceiveChannel.poll] - * | [ReceiveChannel] | [receiveOrNull][ReceiveChannel.receiveOrNull] | [onReceiveOrNull][ReceiveChannel.onReceiveOrNull]| [poll][ReceiveChannel.poll] - * | [Mutex] | [lock][Mutex.lock] | [onLock][Mutex.onLock] | [tryLock][Mutex.tryLock] - * | none | [delay] | [onTimeout][SelectBuilder.onTimeout] | none + * ### List of supported select methods + * + * | **Receiver** | **Suspending function** | **Select clause** + * | ---------------- | --------------------------------------------- | ----------------------------------------------------- + * | [Job] | [join][Job.join] | [onJoin][Job.onJoin] + * | [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] + * | [SendChannel] | [send][SendChannel.send] | [onSend][SendChannel.onSend] + * | [ReceiveChannel] | [receive][ReceiveChannel.receive] | [onReceive][ReceiveChannel.onReceive] + * | [ReceiveChannel] | [receiveCatching][ReceiveChannel.receiveCatching] | [onReceiveCatching][ReceiveChannel.onReceiveCatching] + * | [Mutex] | [lock][Mutex.lock] | [onLock][Mutex.onLock] + * | none | [delay] | [onTimeout][SelectBuilder.onTimeout] * * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this * function is suspended, this function immediately resumes with [CancellationException]. diff --git a/kotlinx-coroutines-core/common/test/channels/ArrayBroadcastChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ArrayBroadcastChannelTest.kt index a7084296bb..2d71cc94ed 100644 --- a/kotlinx-coroutines-core/common/test/channels/ArrayBroadcastChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ArrayBroadcastChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -46,7 +46,7 @@ class ArrayBroadcastChannelTest : TestBase() { assertEquals(2, first.receive()) // suspends assertFalse(first.isClosedForReceive) expect(10) - assertNull(first.receiveOrNull()) // suspends + assertTrue(first.receiveCatching().isClosed) // suspends assertTrue(first.isClosedForReceive) expect(14) } @@ -62,7 +62,7 @@ class ArrayBroadcastChannelTest : TestBase() { assertEquals(2, second.receive()) // suspends assertFalse(second.isClosedForReceive) expect(11) - assertNull(second.receiveOrNull()) // suspends + assertNull(second.receiveCatching().getOrNull()) // suspends assertTrue(second.isClosedForReceive) expect(15) } @@ -116,9 +116,9 @@ class ArrayBroadcastChannelTest : TestBase() { expect(6) assertFalse(sub.isClosedForReceive) for (x in 1..3) - assertEquals(x, sub.receiveOrNull()) + assertEquals(x, sub.receiveCatching().getOrNull()) // and receive close signal - assertNull(sub.receiveOrNull()) + assertNull(sub.receiveCatching().getOrNull()) assertTrue(sub.isClosedForReceive) finish(7) } @@ -153,7 +153,7 @@ class ArrayBroadcastChannelTest : TestBase() { // make sure all of them are consumed check(!sub.isClosedForReceive) for (x in 1..5) check(sub.receive() == x) - check(sub.receiveOrNull() == null) + check(sub.receiveCatching().getOrNull() == null) check(sub.isClosedForReceive) } @@ -196,7 +196,7 @@ class ArrayBroadcastChannelTest : TestBase() { val channel = BroadcastChannel(1) val subscription = channel.openSubscription() subscription.cancel(TestCancellationException()) - subscription.receiveOrNull() + subscription.receive() } @Test @@ -208,6 +208,6 @@ class ArrayBroadcastChannelTest : TestBase() { channel.cancel() assertTrue(channel.isClosedForSend) assertTrue(sub.isClosedForReceive) - check(sub.receiveOrNull() == null) + check(sub.receiveCatching().getOrNull() == null) } } diff --git a/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt index a57b519f61..3900c2db90 100644 --- a/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -38,17 +38,17 @@ class ArrayChannelTest : TestBase() { } @Test - fun testClosedBufferedReceiveOrNull() = runTest { + fun testClosedBufferedReceiveCatching() = runTest { val q = Channel(1) check(q.isEmpty && !q.isClosedForSend && !q.isClosedForReceive) expect(1) launch { expect(5) check(!q.isEmpty && q.isClosedForSend && !q.isClosedForReceive) - assertEquals(42, q.receiveOrNull()) + assertEquals(42, q.receiveCatching().getOrNull()) expect(6) check(!q.isEmpty && q.isClosedForSend && q.isClosedForReceive) - assertNull(q.receiveOrNull()) + assertNull(q.receiveCatching().getOrNull()) expect(7) } expect(2) @@ -134,7 +134,7 @@ class ArrayChannelTest : TestBase() { q.cancel() check(q.isClosedForSend) check(q.isClosedForReceive) - assertFailsWith { q.receiveOrNull() } + assertFailsWith { q.receiveCatching().getOrThrow() } finish(12) } @@ -142,7 +142,7 @@ class ArrayChannelTest : TestBase() { fun testCancelWithCause() = runTest({ it is TestCancellationException }) { val channel = Channel(5) channel.cancel(TestCancellationException()) - channel.receiveOrNull() + channel.receive() } @Test @@ -160,7 +160,7 @@ class ArrayChannelTest : TestBase() { channel.offer(-1) } repeat(4) { - channel.receiveOrNull() + channel.receiveCatching().getOrNull() } checkBufferChannel(channel, capacity) } diff --git a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt index f1658cfa84..a64284aec1 100644 --- a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt @@ -24,19 +24,9 @@ class BasicOperationsTest : TestBase() { TestChannelKind.values().forEach { kind -> testSendAfterClose(kind) } } - @Test - fun testReceiveOrNullAfterClose() = runTest { - TestChannelKind.values().forEach { kind -> testReceiveOrNull(kind) } - } - - @Test - fun testReceiveOrNullAfterCloseWithException() = runTest { - TestChannelKind.values().forEach { kind -> testReceiveOrNullException(kind) } - } - @Test fun testReceiveCatching() = runTest { - TestChannelKind.values().forEach { kind -> testReceiveOrClosed(kind) } + TestChannelKind.values().forEach { kind -> testReceiveCatching(kind) } } @Test @@ -90,24 +80,7 @@ class BasicOperationsTest : TestBase() { } } - private suspend fun testReceiveOrNull(kind: TestChannelKind) = coroutineScope { - val channel = kind.create() - val d = async(NonCancellable) { - channel.receive() - } - - yield() - channel.close() - assertTrue(channel.isClosedForReceive) - - assertNull(channel.receiveOrNull()) - assertNull(channel.poll()) - - d.join() - assertTrue(d.getCancellationException().cause is ClosedReceiveChannelException) - } - - private suspend fun testReceiveOrNullException(kind: TestChannelKind) = coroutineScope { + private suspend fun testReceiveCatchingException(kind: TestChannelKind) = coroutineScope { val channel = kind.create() val d = async(NonCancellable) { channel.receive() @@ -119,8 +92,8 @@ class BasicOperationsTest : TestBase() { assertFailsWith { channel.poll() } try { - channel.receiveOrNull() - fail() + channel.receiveCatching().getOrThrow() + expectUnreached() } catch (e: TestException) { // Expected } @@ -130,7 +103,7 @@ class BasicOperationsTest : TestBase() { } @Suppress("ReplaceAssertBooleanWithAssertEquality") - private suspend fun testReceiveOrClosed(kind: TestChannelKind) = coroutineScope { + private suspend fun testReceiveCatching(kind: TestChannelKind) = coroutineScope { reset() val channel = kind.create() launch { diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt index 37db7e4526..ae05fb8d74 100644 --- a/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ChannelUndeliveredElementFailureTest.kt @@ -70,33 +70,7 @@ class ChannelUndeliveredElementFailureTest : TestBase() { } @Test - fun testReceiveOrNullCancelledFail() = runTest(unhandled = shouldBeUnhandled) { - val channel = Channel(onUndeliveredElement = onCancelFail) - val job = launch(start = CoroutineStart.UNDISPATCHED) { - channel.receiveOrNull() - expectUnreached() // will be cancelled before it dispatches - } - channel.send(item) - job.cancel() - } - - @Test - fun testReceiveOrNullSelectCancelledFail() = runTest(unhandled = shouldBeUnhandled) { - val channel = Channel(onUndeliveredElement = onCancelFail) - val job = launch(start = CoroutineStart.UNDISPATCHED) { - select { - channel.onReceiveOrNull { - expectUnreached() - } - } - expectUnreached() // will be cancelled before it dispatches - } - channel.send(item) - job.cancel() - } - - @Test - fun testReceiveOrClosedCancelledFail() = runTest(unhandled = shouldBeUnhandled) { + fun testReceiveCatchingCancelledFail() = runTest(unhandled = shouldBeUnhandled) { val channel = Channel(onUndeliveredElement = onCancelFail) val job = launch(start = CoroutineStart.UNDISPATCHED) { channel.receiveCatching() diff --git a/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt index 7dd232f2d7..856a66fbd2 100644 --- a/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -68,7 +68,7 @@ class ConflatedBroadcastChannelTest : TestBase() { expect(14) assertEquals("three", sub.receive()) // suspends expect(17) - assertNull(sub.receiveOrNull()) // suspends until closed + assertNull(sub.receiveCatching().getOrNull()) // suspends until closed expect(20) sub.cancel() expect(21) diff --git a/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt index 18f2843868..87194f72f9 100644 --- a/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -27,7 +27,7 @@ open class ConflatedChannelTest : TestBase() { val q = createConflatedChannel() q.send(1) q.send(2) // shall conflated previously sent - assertEquals(2, q.receiveOrNull()) + assertEquals(2, q.receiveCatching().getOrNull()) } @Test @@ -41,7 +41,7 @@ open class ConflatedChannelTest : TestBase() { // not it is closed for receive, too assertTrue(q.isClosedForSend) assertTrue(q.isClosedForReceive) - assertNull(q.receiveOrNull()) + assertNull(q.receiveCatching().getOrNull()) } @Test @@ -82,7 +82,7 @@ open class ConflatedChannelTest : TestBase() { q.cancel() check(q.isClosedForSend) check(q.isClosedForReceive) - assertFailsWith { q.receiveOrNull() } + assertFailsWith { q.receiveCatching().getOrThrow() } finish(2) } @@ -90,6 +90,6 @@ open class ConflatedChannelTest : TestBase() { fun testCancelWithCause() = runTest({ it is TestCancellationException }) { val channel = createConflatedChannel() channel.cancel(TestCancellationException()) - channel.receiveOrNull() + channel.receive() } } diff --git a/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt index 4233a35084..cdec8a776e 100644 --- a/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -18,8 +18,8 @@ class LinkedListChannelTest : TestBase() { check(!c.close()) assertEquals(1, c.receive()) assertEquals(2, c.poll()) - assertEquals(3, c.receiveOrNull()) - assertNull(c.receiveOrNull()) + assertEquals(3, c.receiveCatching().getOrNull()) + assertNull(c.receiveCatching().getOrNull()) } @Test @@ -31,13 +31,13 @@ class LinkedListChannelTest : TestBase() { q.cancel() check(q.isClosedForSend) check(q.isClosedForReceive) - assertFailsWith { q.receiveOrNull() } + assertFailsWith { q.receive() } } @Test fun testCancelWithCause() = runTest({ it is TestCancellationException }) { val channel = Channel(Channel.UNLIMITED) channel.cancel(TestCancellationException()) - channel.receiveOrNull() + channel.receive() } } diff --git a/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt b/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt index 194504e713..61ef072622 100644 --- a/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ProduceTest.kt @@ -24,7 +24,7 @@ class ProduceTest : TestBase() { expect(4) check(c.receive() == 2) expect(5) - check(c.receiveOrNull() == null) + assertNull(c.receiveCatching().getOrNull()) finish(7) } @@ -49,7 +49,7 @@ class ProduceTest : TestBase() { expect(4) c.cancel() expect(5) - assertFailsWith { c.receiveOrNull() } + assertFailsWith { c.receiveCatching().getOrThrow() } expect(6) yield() // to produce finish(8) @@ -76,7 +76,7 @@ class ProduceTest : TestBase() { expect(4) c.cancel(TestCancellationException()) try { - assertNull(c.receiveOrNull()) + c.receive() expectUnreached() } catch (e: TestCancellationException) { expect(5) diff --git a/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt index 4d20d71596..ab0292a831 100644 --- a/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -36,15 +36,15 @@ class RendezvousChannelTest : TestBase() { } @Test - fun testClosedReceiveOrNull() = runTest { + fun testClosedReceiveCatching() = runTest { val q = Channel(Channel.RENDEZVOUS) check(q.isEmpty && !q.isClosedForSend && !q.isClosedForReceive) expect(1) launch { expect(3) - assertEquals(42, q.receiveOrNull()) + assertEquals(42, q.receiveCatching().getOrNull()) expect(4) - assertNull(q.receiveOrNull()) + assertNull(q.receiveCatching().getOrNull()) expect(6) } expect(2) @@ -233,9 +233,9 @@ class RendezvousChannelTest : TestBase() { expect(7) yield() // try to resume sender (it will not resume despite the close!) expect(8) - assertEquals(42, q.receiveOrNull()) + assertEquals(42, q.receiveCatching().getOrNull()) expect(9) - assertNull(q.receiveOrNull()) + assertNull(q.receiveCatching().getOrNull()) expect(10) yield() // to sender, it was resumed! finish(12) @@ -266,7 +266,7 @@ class RendezvousChannelTest : TestBase() { q.cancel() check(q.isClosedForSend) check(q.isClosedForReceive) - assertFailsWith { q.receiveOrNull() } + assertFailsWith { q.receiveCatching().getOrThrow() } finish(12) } @@ -274,6 +274,6 @@ class RendezvousChannelTest : TestBase() { fun testCancelWithCause() = runTest({ it is TestCancellationException }) { val channel = Channel(Channel.RENDEZVOUS) channel.cancel(TestCancellationException()) - channel.receiveOrNull() + channel.receiveCatching().getOrThrow() } } diff --git a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt index 3e70007a6e..f234e141fe 100644 --- a/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt +++ b/kotlinx-coroutines-core/common/test/channels/TestChannelKind.kt @@ -42,7 +42,6 @@ private class ChannelViaBroadcast( override val isEmpty: Boolean get() = sub.isEmpty override suspend fun receive(): E = sub.receive() - override suspend fun receiveOrNull(): E? = sub.receiveOrNull() override suspend fun receiveCatching(): ChannelResult = sub.receiveCatching() override fun iterator(): ChannelIterator = sub.iterator() override fun tryReceive(): ChannelResult = sub.tryReceive() @@ -55,8 +54,6 @@ private class ChannelViaBroadcast( override val onReceive: SelectClause1 get() = sub.onReceive - override val onReceiveOrNull: SelectClause1 - get() = sub.onReceiveOrNull override val onReceiveCatching: SelectClause1> get() = sub.onReceiveCatching } diff --git a/kotlinx-coroutines-core/common/test/selects/SelectLoopTest.kt b/kotlinx-coroutines-core/common/test/selects/SelectLoopTest.kt index e31ccfc16d..ba8f56ad4c 100644 --- a/kotlinx-coroutines-core/common/test/selects/SelectLoopTest.kt +++ b/kotlinx-coroutines-core/common/test/selects/SelectLoopTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913 @@ -27,7 +27,7 @@ class SelectLoopTest : TestBase() { try { while (true) { select { - channel.onReceiveOrNull { + channel.onReceiveCatching { expectUnreached() } job.onJoin { @@ -40,4 +40,4 @@ class SelectLoopTest : TestBase() { finish(4) } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveOrNullFromClosedChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveOrNullFromClosedChannel.txt deleted file mode 100644 index ac8f5f4ee6..0000000000 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveOrNullFromClosedChannel.txt +++ /dev/null @@ -1,8 +0,0 @@ -kotlinx.coroutines.RecoverableTestException - at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveOrNullFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43) - (Coroutine boundary) - at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceiveOrNull(StackTraceRecoveryChannelsTest.kt:70) - at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveOrNullFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:44) -Caused by: kotlinx.coroutines.RecoverableTestException - at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveOrNullFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file diff --git a/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt index 1d7613eded..5a2778d507 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ActorTest.kt @@ -69,11 +69,11 @@ class ActorTest(private val capacity: Int) : TestBase() { @Test fun testCloseWithoutCause() = runTest { val actor = actor(capacity = capacity) { - val element = channel.receiveOrNull() + val element = channel.receive() expect(2) assertEquals(42, element) - val next = channel.receiveOrNull() - assertNull(next) + val next = channel.receiveCatching() + assertNull(next.exceptionOrNull()) expect(3) } @@ -88,11 +88,11 @@ class ActorTest(private val capacity: Int) : TestBase() { @Test fun testCloseWithCause() = runTest { val actor = actor(capacity = capacity) { - val element = channel.receiveOrNull() + val element = channel.receive() expect(2) - require(element!! == 42) + require(element == 42) try { - channel.receiveOrNull() + channel.receive() } catch (e: IOException) { expect(3) } @@ -111,7 +111,7 @@ class ActorTest(private val capacity: Int) : TestBase() { val job = async { actor(capacity = capacity) { expect(1) - channel.receiveOrNull() + channel.receive() expectUnreached() } } diff --git a/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt index 2e73b2432a..8c9777b4af 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/BroadcastChannelMultiReceiveStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -67,10 +67,10 @@ class BroadcastChannelMultiReceiveStressTest( val channel = broadcast.openSubscription() when (receiverIndex % 5) { 0 -> doReceive(channel, receiverIndex) - 1 -> doReceiveOrNull(channel, receiverIndex) + 1 -> doReceiveCatching(channel, receiverIndex) 2 -> doIterator(channel, receiverIndex) 3 -> doReceiveSelect(channel, receiverIndex) - 4 -> doReceiveSelectOrNull(channel, receiverIndex) + 4 -> doReceiveCatchingSelect(channel, receiverIndex) } channel.cancel() } @@ -124,9 +124,9 @@ class BroadcastChannelMultiReceiveStressTest( } } - private suspend fun doReceiveOrNull(channel: ReceiveChannel, receiverIndex: Int) { + private suspend fun doReceiveCatching(channel: ReceiveChannel, receiverIndex: Int) { while (true) { - val stop = doReceived(receiverIndex, channel.receiveOrNull() ?: break) + val stop = doReceived(receiverIndex, channel.receiveCatching().getOrNull() ?: break) if (stop) break } } @@ -148,11 +148,11 @@ class BroadcastChannelMultiReceiveStressTest( } } - private suspend fun doReceiveSelectOrNull(channel: ReceiveChannel, receiverIndex: Int) { + private suspend fun doReceiveCatchingSelect(channel: ReceiveChannel, receiverIndex: Int) { while (true) { - val event = select { channel.onReceiveOrNull { it } } ?: break + val event = select { channel.onReceiveCatching { it.getOrNull() } } ?: break val stop = doReceived(receiverIndex, event) if (stop) break } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt index 9f7ce497fa..a6a5340389 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt @@ -89,7 +89,7 @@ class ChannelCancelUndeliveredElementStressTest : TestBase() { private suspend fun receiveOne(channel: Channel) { val received = when (Random.nextInt(3)) { 0 -> channel.receive() - 1 -> channel.receiveOrNull() ?: error("Cannot be closed yet") + 1 -> channel.receiveCatching().getOrElse { error("Cannot be closed yet") } 2 -> select { channel.onReceive { it } } diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt index f414c33338..a6345cc55b 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelSendReceiveStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -60,10 +60,10 @@ class ChannelSendReceiveStressTest( launch(pool + CoroutineName("receiver$receiverIndex")) { when (receiverIndex % 5) { 0 -> doReceive(receiverIndex) - 1 -> doReceiveOrNull(receiverIndex) + 1 -> doReceiveCatching(receiverIndex) 2 -> doIterator(receiverIndex) 3 -> doReceiveSelect(receiverIndex) - 4 -> doReceiveSelectOrNull(receiverIndex) + 4 -> doReceiveCatchingSelect(receiverIndex) } receiversCompleted.incrementAndGet() } @@ -152,9 +152,9 @@ class ChannelSendReceiveStressTest( } } - private suspend fun doReceiveOrNull(receiverIndex: Int) { + private suspend fun doReceiveCatching(receiverIndex: Int) { while (true) { - doReceived(receiverIndex, channel.receiveOrNull() ?: break) + doReceived(receiverIndex, channel.receiveCatching().getOrNull() ?: break) } } @@ -173,10 +173,10 @@ class ChannelSendReceiveStressTest( } } - private suspend fun doReceiveSelectOrNull(receiverIndex: Int) { + private suspend fun doReceiveCatchingSelect(receiverIndex: Int) { while (true) { - val event = select { channel.onReceiveOrNull { it } } ?: break + val event = select { channel.onReceiveCatching { it.getOrNull() } } ?: break doReceived(receiverIndex, event) } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt index 3f502ba9fb..8f5224db79 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt @@ -188,8 +188,8 @@ class ChannelUndeliveredElementStressTest(private val kind: TestChannelKind) : T val receivedData = when (receiveMode) { 1 -> channel.receive() 2 -> select { channel.onReceive { it } } - 3 -> channel.receiveOrNull() ?: error("Should not be closed") - 4 -> select { channel.onReceiveOrNull { it ?: error("Should not be closed") } } + 3 -> channel.receiveCatching().getOrElse { error("Should not be closed") } + 4 -> select { channel.onReceiveCatching { it.getOrElse { error("Should not be closed") } } } 5 -> channel.receiveCatching().getOrThrow() 6 -> { val iterator = channel.iterator() diff --git a/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt index 316b378508..fd26144faf 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.channels @@ -64,7 +64,9 @@ class ConflatedChannelCloseStressTest : TestBase() { } val receiver = async(pool + NonCancellable) { while (isActive) { - curChannel.get().receiveOrNull() + curChannel.get().receiveCatching().getOrElse { + it?.let { throw it } + } received.incrementAndGet() } } @@ -110,4 +112,4 @@ class ConflatedChannelCloseStressTest : TestBase() { } class StopException : Exception() -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt index f52f8b5bcf..555d99fc94 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.exceptions @@ -37,13 +37,6 @@ class StackTraceRecoveryChannelsTest : TestBase() { channelReceive(channel) } - @Test - fun testReceiveOrNullFromClosedChannel() = runTest { - val channel = Channel() - channel.close(RecoverableTestException()) - channelReceiveOrNull(channel) - } - @Test fun testSendToClosedChannel() = runTest { val channel = Channel() @@ -67,7 +60,6 @@ class StackTraceRecoveryChannelsTest : TestBase() { } private suspend fun channelReceive(channel: Channel) = channelOp { channel.receive() } - private suspend fun channelReceiveOrNull(channel: Channel) = channelOp { channel.receiveOrNull() } private suspend inline fun channelOp(block: () -> Unit) { try { @@ -75,6 +67,7 @@ class StackTraceRecoveryChannelsTest : TestBase() { block() expectUnreached() } catch (e: RecoverableTestException) { + e.printStackTrace() verifyStackTrace("channels/${name.methodName}", e) } } @@ -177,4 +170,4 @@ class StackTraceRecoveryChannelsTest : TestBase() { private suspend fun Channel.sendFromScope() = coroutineScope { sendWithContext(wrapperDispatcher(coroutineContext)) } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt index 57fe638297..22380d3af5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt @@ -11,17 +11,21 @@ import kotlinx.coroutines.selects.* suspend fun selectAorB(a: ReceiveChannel, b: ReceiveChannel): String = select { - a.onReceiveOrNull { value -> - if (value == null) - "Channel 'a' is closed" - else + a.onReceiveCatching { it -> + val value = it.getOrNull() + if (value != null) { "a -> '$value'" + } else { + "Channel 'a' is closed" + } } - b.onReceiveOrNull { value -> - if (value == null) - "Channel 'b' is closed" - else + b.onReceiveCatching { it -> + val value = it.getOrNull() + if (value != null) { "b -> '$value'" + } else { + "Channel 'b' is closed" + } } } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt index 464e9b20f3..68b4456454 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt @@ -13,12 +13,12 @@ fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel>) = var current = input.receive() // start with first received deferred value while (isActive) { // loop while not cancelled/closed val next = select?> { // return next deferred value from this select or null - input.onReceiveOrNull { update -> - update // replaces next value to wait + input.onReceiveCatching { update -> + update.getOrNull() } - current.onAwait { value -> + current.onAwait { value -> send(value) // send value that current deferred has produced - input.receiveOrNull() // and use the next deferred from the input channel + input.receiveCatching().getOrNull() // and use the next deferred from the input channel } } if (next == null) { diff --git a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt index 1f04ea538c..cb8de7a636 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt @@ -112,7 +112,7 @@ private class PublisherAsFlow( collectImpl(scope.coroutineContext, SendingCollector(scope.channel)) } -@Suppress("SubscriberImplementation") +@Suppress("ReactiveStreamsSubscriberImplementation") private class ReactiveSubscriber( capacity: Int, onBufferOverflow: BufferOverflow, @@ -124,7 +124,11 @@ private class ReactiveSubscriber( // be reliable with rendezvous channel, so a rendezvous channel is replaced with buffer=1 channel private val channel = Channel(if (capacity == Channel.RENDEZVOUS) 1 else capacity, onBufferOverflow) - suspend fun takeNextOrNull(): T? = channel.receiveOrNull() + suspend fun takeNextOrNull(): T? { + val result = channel.receiveCatching() + result.exceptionOrNull()?.let { throw it } + return result.getOrElse { null } // Closed channel + } override fun onNext(value: T) { // Controlled by requestSize @@ -247,7 +251,7 @@ public class FlowSubscription( if (old <= 0L) { assert(old == 0L) // Emitter is not started yet or has suspended -- spin on race with suspendCancellableCoroutine - while(true) { + while (true) { val producer = producer.getAndSet(null) ?: continue // spin if not set yet producer.resume(Unit) break diff --git a/reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt b/reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt index f846882ab2..1db10b278d 100644 --- a/reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/CancelledParentAttachTest.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.* import org.junit.* -class CancelledParentAttachTest : TestBase() { +class CancelledParentAttachTest : TestBase() {; @Test fun testFlow() = runTest { @@ -17,4 +17,5 @@ class CancelledParentAttachTest : TestBase() { val j = Job().also { it.cancel() } f.asPublisher(j).asFlow().collect() } + } diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt index 04833e9814..e2c86c97ff 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactive @@ -263,4 +263,14 @@ class PublisherAsFlowTest : TestBase() { } assertEquals(expected, list) } + + @Test + fun testException() = runTest { + expect(1) + val p = publish { throw TestException() }.asFlow() + p.catch { + assertTrue { it is TestException } + finish(2) + }.collect() + } } diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt index 110718ac55..790cf7ec7c 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt @@ -1,10 +1,11 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.reactive import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* import kotlinx.coroutines.selects.* import org.junit.Test import org.junit.runner.* @@ -31,23 +32,23 @@ class PublisherSubscriptionSelectTest(private val request: Int) : TestBase() { val channelB = source.openSubscription(request) loop@ while (true) { val done: Int = select { - channelA.onReceiveOrNull { - if (it != null) assertEquals(a++, it) - if (it == null) 0 else 1 + channelA.onReceiveCatching { result -> + result.onSuccess { assertEquals(a++, it) } + if (result.isSuccess) 1 else 0 } - channelB.onReceiveOrNull { - if (it != null) assertEquals(b++, it) - if (it == null) 0 else 2 + channelB.onReceiveCatching { result -> + result.onSuccess { assertEquals(b++, it) } + if (result.isSuccess) 2 else 0 } } when (done) { 0 -> break@loop 1 -> { - val r = channelB.receiveOrNull() + val r = channelB.receiveCatching().getOrNull() if (r != null) assertEquals(b++, r) } 2 -> { - val r = channelA.receiveOrNull() + val r = channelA.receiveCatching().getOrNull() if (r != null) assertEquals(a++, r) } } @@ -58,4 +59,4 @@ class PublisherSubscriptionSelectTest(private val request: Int) : TestBase() { // should receive one of them fully assertTrue(a == n || b == n) } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt index 3cd3bbffff..396d19d159 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt @@ -1,12 +1,14 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.rx2 import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* import kotlinx.coroutines.selects.* import org.junit.Test +import kotlin.onSuccess import kotlin.test.* class ObservableSubscriptionSelectTest : TestBase() { @@ -22,23 +24,23 @@ class ObservableSubscriptionSelectTest : TestBase() { val channelB = source.openSubscription() loop@ while (true) { val done: Int = select { - channelA.onReceiveOrNull { - if (it != null) assertEquals(a++, it) - if (it == null) 0 else 1 + channelA.onReceiveCatching { result -> + result.onSuccess { assertEquals(a++, it) } + if (result.isSuccess) 1 else 0 } - channelB.onReceiveOrNull { - if (it != null) assertEquals(b++, it) - if (it == null) 0 else 2 + channelB.onReceiveCatching { result -> + result.onSuccess { assertEquals(b++, it) } + if (result.isSuccess) 2 else 0 } } when (done) { 0 -> break@loop 1 -> { - val r = channelB.receiveOrNull() + val r = channelB.receiveCatching().getOrNull() if (r != null) assertEquals(b++, r) } 2 -> { - val r = channelA.receiveOrNull() + val r = channelA.receiveCatching().getOrNull() if (r != null) assertEquals(a++, r) } } @@ -48,4 +50,4 @@ class ObservableSubscriptionSelectTest : TestBase() { // should receive one of them fully assertTrue(a == n || b == n) } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-rx3/test/ObservableSubscriptionSelectTest.kt b/reactive/kotlinx-coroutines-rx3/test/ObservableSubscriptionSelectTest.kt index 2f04316159..58a54616f6 100644 --- a/reactive/kotlinx-coroutines-rx3/test/ObservableSubscriptionSelectTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/ObservableSubscriptionSelectTest.kt @@ -1,12 +1,14 @@ /* - * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines.rx3 import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* import kotlinx.coroutines.selects.* import org.junit.Test +import kotlin.onSuccess import kotlin.test.* class ObservableSubscriptionSelectTest : TestBase() { @@ -22,23 +24,23 @@ class ObservableSubscriptionSelectTest : TestBase() { val channelB = source.openSubscription() loop@ while (true) { val done: Int = select { - channelA.onReceiveOrNull { - if (it != null) assertEquals(a++, it) - if (it == null) 0 else 1 + channelA.onReceiveCatching { result -> + result.onSuccess { assertEquals(a++, it) } + if (result.isSuccess) 1 else 0 } - channelB.onReceiveOrNull { - if (it != null) assertEquals(b++, it) - if (it == null) 0 else 2 + channelB.onReceiveCatching { result -> + result.onSuccess { assertEquals(b++, it) } + if (result.isSuccess) 2 else 0 } } when (done) { 0 -> break@loop 1 -> { - val r = channelB.receiveOrNull() + val r = channelB.receiveCatching().getOrNull() if (r != null) assertEquals(b++, r) } 2 -> { - val r = channelA.receiveOrNull() + val r = channelA.receiveCatching().getOrNull() if (r != null) assertEquals(a++, r) } } From eb963e84dad1b358d801eb408b89f50c5307fdc3 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 6 Apr 2021 19:19:14 +0300 Subject: [PATCH 20/55] Remove deprecated SendChannel.isFull (#2613) --- .../api/kotlinx-coroutines-core.api | 2 -- .../common/src/channels/AbstractChannel.kt | 3 +-- .../common/src/channels/ArrayChannel.kt | 1 - kotlinx-coroutines-core/common/src/channels/Channel.kt | 10 ---------- .../common/src/channels/ConflatedBroadcastChannel.kt | 1 - kotlinx-coroutines-core/jvm/src/channels/Channels.kt | 2 +- .../api/kotlinx-coroutines-reactive.api | 1 - reactive/kotlinx-coroutines-reactive/src/Publish.kt | 1 - reactive/kotlinx-coroutines-rx2/src/RxObservable.kt | 1 - reactive/kotlinx-coroutines-rx3/src/RxObservable.kt | 1 - 10 files changed, 2 insertions(+), 21 deletions(-) diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 2f2b8d54ab..611ebd8650 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -802,7 +802,6 @@ public final class kotlinx/coroutines/channels/ConflatedBroadcastChannel : kotli public final fun getValueOrNull ()Ljava/lang/Object; public fun invokeOnClose (Lkotlin/jvm/functions/Function1;)V public fun isClosedForSend ()Z - public fun isFull ()Z public fun offer (Ljava/lang/Object;)Z public fun openSubscription ()Lkotlinx/coroutines/channels/ReceiveChannel; public fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -857,7 +856,6 @@ public abstract interface class kotlinx/coroutines/channels/SendChannel { public abstract fun getOnSend ()Lkotlinx/coroutines/selects/SelectClause2; public abstract fun invokeOnClose (Lkotlin/jvm/functions/Function1;)V public abstract fun isClosedForSend ()Z - public abstract fun isFull ()Z public abstract fun offer (Ljava/lang/Object;)Z public abstract fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun trySend-JP2dKIU (Ljava/lang/Object;)Ljava/lang/Object; diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt index 04a9d1a64a..52e47227ad 100644 --- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt @@ -127,8 +127,7 @@ internal abstract class AbstractSendChannel( // ------ SendChannel ------ public final override val isClosedForSend: Boolean get() = closedForSend != null - public override val isFull: Boolean get() = isFullImpl - protected val isFullImpl: Boolean get() = queue.nextNode !is ReceiveOrClosed<*> && isBufferFull + private val isFullImpl: Boolean get() = queue.nextNode !is ReceiveOrClosed<*> && isBufferFull public final override suspend fun send(element: E) { // fast path -- try offer non-blocking diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt index 4569ec72fa..7e6c0e68c5 100644 --- a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt @@ -49,7 +49,6 @@ internal open class ArrayChannel( protected final override val isBufferAlwaysFull: Boolean get() = false protected final override val isBufferFull: Boolean get() = size.value == capacity && onBufferOverflow == BufferOverflow.SUSPEND - override val isFull: Boolean get() = lock.withLock { isFullImpl } override val isEmpty: Boolean get() = lock.withLock { isEmptyImpl } override val isClosedForReceive: Boolean get() = lock.withLock { super.isClosedForReceive } diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 8aad264d2c..7b6cc0ad9d 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -31,16 +31,6 @@ public interface SendChannel { @ExperimentalCoroutinesApi public val isClosedForSend: Boolean - /** - * Returns `true` if the channel is full (out of capacity), which means that an attempt to [send] will suspend. - * This function returns `false` if the channel [is closed for `send`][isClosedForSend]. - * - * @suppress **Will be removed in next releases, no replacement.** - */ - @ExperimentalCoroutinesApi - @Deprecated(level = DeprecationLevel.ERROR, message = "Will be removed in next releases without replacement") - public val isFull: Boolean - /** * Sends the specified [element] to this channel, suspending the caller while the buffer of this channel is full * or if it does not exist, or throws an exception if the channel [is closed for `send`][isClosedForSend] (see [close] for details). diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt index 75524f214f..c84afb2fe0 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt @@ -94,7 +94,6 @@ public class ConflatedBroadcastChannel() : BroadcastChannel { } public override val isClosedForSend: Boolean get() = _state.value is Closed - public override val isFull: Boolean get() = false @Suppress("UNCHECKED_CAST") public override fun openSubscription(): ReceiveChannel { diff --git a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt index 081a0583d1..52adfea276 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt @@ -10,7 +10,7 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* /** - * Adds [element] into to this channel, **blocking** the caller while this channel [Channel.isFull], + * Adds [element] into to this channel, **blocking** the caller while this channel is full, * or throws exception if the channel [Channel.isClosedForSend] (see [Channel.close] for details). * * This is a way to call [Channel.send] method inside a blocking code using [runBlocking], diff --git a/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api b/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api index 6798625179..06ef048407 100644 --- a/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api +++ b/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api @@ -57,7 +57,6 @@ public final class kotlinx/coroutines/reactive/PublisherCoroutine : kotlinx/coro public fun invokeOnClose (Lkotlin/jvm/functions/Function1;)Ljava/lang/Void; public synthetic fun invokeOnClose (Lkotlin/jvm/functions/Function1;)V public fun isClosedForSend ()Z - public fun isFull ()Z public fun offer (Ljava/lang/Object;)Z public synthetic fun onCompleted (Ljava/lang/Object;)V public fun registerSelectClause2 (Lkotlinx/coroutines/selects/SelectInstance;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 6bb02ef192..f66b1c3bae 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -91,7 +91,6 @@ public class PublisherCoroutine( private var cancelled = false // true when Subscription.cancel() is invoked override val isClosedForSend: Boolean get() = isCompleted - override val isFull: Boolean = mutex.isLocked override fun close(cause: Throwable?): Boolean = cancelCoroutine(cause) override fun invokeOnClose(handler: (Throwable?) -> Unit): Nothing = throw UnsupportedOperationException("PublisherCoroutine doesn't support invokeOnClose") diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index 7813bc7548..edbb106253 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -80,7 +80,6 @@ private class RxObservableCoroutine( private val _signal = atomic(OPEN) override val isClosedForSend: Boolean get() = isCompleted - override val isFull: Boolean = mutex.isLocked override fun close(cause: Throwable?): Boolean = cancelCoroutine(cause) override fun invokeOnClose(handler: (Throwable?) -> Unit) = throw UnsupportedOperationException("RxObservableCoroutine doesn't support invokeOnClose") diff --git a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt index fed89c328e..a17e32dd31 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt @@ -66,7 +66,6 @@ private class RxObservableCoroutine( private val _signal = atomic(OPEN) override val isClosedForSend: Boolean get() = isCompleted - override val isFull: Boolean = mutex.isLocked override fun close(cause: Throwable?): Boolean = cancelCoroutine(cause) override fun invokeOnClose(handler: (Throwable?) -> Unit) = throw UnsupportedOperationException("RxObservableCoroutine doesn't support invokeOnClose") From f2f49b09db471179a8861a77840c47706e668b9d Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Wed, 7 Apr 2021 11:44:14 +0300 Subject: [PATCH 21/55] Complete mono { } on cancellation (#2606) Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/2262 --- .../kotlinx-coroutines-reactor/src/Mono.kt | 27 +++--- .../test/MonoTest.kt | 93 +++++++++++++++++++ 2 files changed, 107 insertions(+), 13 deletions(-) diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index e146dca9f6..6786f1abab 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -7,21 +7,23 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* +import kotlinx.coroutines.reactive.* +import org.reactivestreams.* import reactor.core.* import reactor.core.publisher.* import kotlin.coroutines.* import kotlin.internal.* /** - * Creates cold [mono][Mono] that will run a given [block] in a coroutine and emits its result. + * Creates a cold [mono][Mono] that runs a given [block] in a coroutine and emits its result. * Every time the returned mono is subscribed, it starts a new coroutine. - * If [block] result is `null`, [MonoSink.success] is invoked without a value. - * Unsubscribing cancels running coroutine. + * If the result of [block] is `null`, [MonoSink.success] is invoked without a value. + * Unsubscribing cancels the running coroutine. * * Coroutine context can be specified with [context] argument. * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used. * - * Method throws [IllegalArgumentException] if provided [context] contains a [Job] instance. + * @throws IllegalArgumentException if the provided [context] contains a [Job] instance. */ public fun mono( context: CoroutineContext = EmptyCoroutineContext, @@ -67,17 +69,16 @@ private class MonoCoroutine( } override fun onCancelled(cause: Throwable, handled: Boolean) { - try { - /* - * sink.error handles exceptions on its own and, by default, handling of undeliverable exceptions is a no-op. - * Guard potentially non-empty handlers against meaningless cancellation exceptions - */ - if (getCancellationException() !== cause) { + /** Cancellation exceptions that were caused by [dispose], that is, came from downstream, are not errors. */ + if (getCancellationException() !== cause || !disposed) { + try { + /** If [sink] turns out to already be in a terminal state, this exception will be passed through the + * [Hooks.onOperatorError] hook, which is the way to signal undeliverable exceptions in Reactor. */ sink.error(cause) + } catch (e: Throwable) { + // In case of improper error implementation or fatal exceptions + handleCoroutineException(context, cause) } - } catch (e: Throwable) { - // In case of improper error implementation or fatal exceptions - handleCoroutineException(context, cause) } } diff --git a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt index 0271483fc1..97b195c517 100644 --- a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt @@ -5,6 +5,7 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* import org.junit.* @@ -21,6 +22,7 @@ class MonoTest : TestBase() { @Before fun setup() { ignoreLostThreads("timer-", "parallel-") + Hooks.onErrorDropped { expectUnreached() } } @Test @@ -285,4 +287,95 @@ class MonoTest : TestBase() { .collect { } } } + + /** Test that cancelling a [mono] due to a timeout does throw an exception. */ + @Test + fun testTimeout() { + val mono = mono { + withTimeout(1) { delay(100) } + } + try { + mono.doOnSubscribe { expect(1) } + .doOnNext { expectUnreached() } + .doOnSuccess { expectUnreached() } + .doOnError { expect(2) } + .doOnCancel { expectUnreached() } + .block() + } catch (e: CancellationException) { + expect(3) + } + finish(4) + } + + /** Test that when the reason for cancellation of a [mono] is that the downstream doesn't want its results anymore, + * this is considered normal behavior and exceptions are not propagated. */ + @Test + fun testDownstreamCancellationDoesNotThrow() = runTest { + /** Attach a hook that handles exceptions from publishers that are known to be disposed of. We don't expect it + * to be fired in this case, as the reason for the publisher in this test to accept an exception is simply + * cancellation from the downstream. */ + Hooks.onOperatorError("testDownstreamCancellationDoesNotThrow") { t, a -> + expectUnreached() + t + } + /** A Mono that doesn't emit a value and instead waits indefinitely. */ + val mono = mono { expect(3); delay(Long.MAX_VALUE) } + .doOnSubscribe { expect(2) } + .doOnNext { expectUnreached() } + .doOnSuccess { expectUnreached() } + .doOnError { expectUnreached() } + .doOnCancel { expect(4) } + expect(1) + mono.awaitCancelAndJoin() + finish(5) + Hooks.resetOnOperatorError("testDownstreamCancellationDoesNotThrow") + } + + /** Test that, when [Mono] is cancelled by the downstream and throws during handling the cancellation, the resulting + * error is propagated to [Hooks.onOperatorError]. */ + @Test + fun testRethrowingDownstreamCancellation() = runTest { + /** Attach a hook that handles exceptions from publishers that are known to be disposed of. We expect it + * to be fired in this case. */ + Hooks.onOperatorError("testDownstreamCancellationDoesNotThrow") { t, a -> + expect(5) + t + } + /** A Mono that doesn't emit a value and instead waits indefinitely, and, when cancelled, throws. */ + val mono = mono { + expect(3); + try { + delay(Long.MAX_VALUE) + } catch (e: CancellationException) { + throw TestException() + } + } + .doOnSubscribe { expect(2) } + .doOnNext { expectUnreached() } + .doOnSuccess { expectUnreached() } + .doOnError { expectUnreached() } + .doOnCancel { expect(4) } + expect(1) + mono.awaitCancelAndJoin() + finish(6) /** if this line fails, see the comment for [awaitCancelAndJoin] */ + Hooks.resetOnOperatorError("testDownstreamCancellationDoesNotThrow") + } + + /** Run the given [Mono], cancel it, wait for the cancellation handler to finish, and *return only then*. + * + * There are no guarantees about the execution context in which the cancellation handler will run, but we have + * to wait for it to finish to check its behavior. The contraption below seems to ensure that everything works out. + * If it stops giving that guarantee, then [testRethrowingDownstreamCancellation] should fail more or less + * consistently because the hook won't have enough time to fire before a call to [finish]. + */ + private suspend fun Mono.awaitCancelAndJoin() = coroutineScope { + val job = async(start = CoroutineStart.UNDISPATCHED) { + awaitFirstOrNull() + } + newSingleThreadContext("monoCancellationCleanup").use { pool -> + launch(pool) { + job.cancelAndJoin() + } + }.join() + } } From 86a0658af37d149c4e3a866f9aebf1c537a48238 Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Wed, 7 Apr 2021 13:34:27 +0300 Subject: [PATCH 22/55] Make the subscriber in awaitOne less permissive (#2586) The implementation of Reactive Streams' Subscriber used for `await*` operations was assuming that the publisher is correct. Now, the implementation detects some instances of problematic behavior for publishers and reports them. Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/2079 --- .../kotlinx-coroutines-reactive/src/Await.kt | 92 ++++++++++-- .../test/IntegrationTest.kt | 132 ++++++++++++++++++ 2 files changed, 211 insertions(+), 13 deletions(-) diff --git a/reactive/kotlinx-coroutines-reactive/src/Await.kt b/reactive/kotlinx-coroutines-reactive/src/Await.kt index e9f6955085..9af134cb94 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Await.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Await.kt @@ -4,12 +4,11 @@ package kotlinx.coroutines.reactive -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.Job -import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.* import org.reactivestreams.Publisher import org.reactivestreams.Subscriber import org.reactivestreams.Subscription +import java.lang.IllegalStateException import java.util.* import kotlin.coroutines.* @@ -134,31 +133,61 @@ private suspend fun Publisher.awaitOne( mode: Mode, default: T? = null ): T = suspendCancellableCoroutine { cont -> + /* This implementation must obey + https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#2-subscriber-code + The numbers of rules are taken from there. */ injectCoroutineContext(cont.context).subscribe(object : Subscriber { - private lateinit var subscription: Subscription + // It is unclear whether 2.13 implies (T: Any), but if so, it seems that we don't break anything by not adhering + private var subscription: Subscription? = null private var value: T? = null private var seenValue = false + private var inTerminalState = false override fun onSubscribe(sub: Subscription) { + /** cancelling the new subscription due to rule 2.5, though the publisher would either have to + * subscribe more than once, which would break 2.12, or leak this [Subscriber]. */ + if (subscription != null) { + sub.cancel() + return + } subscription = sub cont.invokeOnCancellation { sub.cancel() } - sub.request(if (mode == Mode.FIRST) 1 else Long.MAX_VALUE) + sub.request(if (mode == Mode.FIRST || mode == Mode.FIRST_OR_DEFAULT) 1 else Long.MAX_VALUE) } override fun onNext(t: T) { + val sub = subscription.let { + if (it == null) { + /** Enforce rule 1.9: expect [Subscriber.onSubscribe] before any other signals. */ + handleCoroutineException(cont.context, + IllegalStateException("'onNext' was called before 'onSubscribe'")) + return + } else { + it + } + } + if (inTerminalState) { + gotSignalInTerminalStateException(cont.context, "onNext") + return + } when (mode) { Mode.FIRST, Mode.FIRST_OR_DEFAULT -> { - if (!seenValue) { - seenValue = true - subscription.cancel() - cont.resume(t) + if (seenValue) { + moreThanOneValueProvidedException(cont.context, mode) + return } + seenValue = true + sub.cancel() + cont.resume(t) } Mode.LAST, Mode.SINGLE, Mode.SINGLE_OR_DEFAULT -> { if ((mode == Mode.SINGLE || mode == Mode.SINGLE_OR_DEFAULT) && seenValue) { - subscription.cancel() - if (cont.isActive) + sub.cancel() + /* the check for `cont.isActive` is needed in case `sub.cancel() above calls `onComplete` or + `onError` on its own. */ + if (cont.isActive) { cont.resumeWithException(IllegalArgumentException("More than one onNext value for $mode")) + } } else { value = t seenValue = true @@ -169,8 +198,16 @@ private suspend fun Publisher.awaitOne( @Suppress("UNCHECKED_CAST") override fun onComplete() { + if (!tryEnterTerminalState("onComplete")) { + return + } if (seenValue) { - if (cont.isActive) cont.resume(value as T) + /* the check for `cont.isActive` is needed because, otherwise, if the publisher doesn't acknowledge the + call to `cancel` for modes `SINGLE*` when more than one value was seen, it may call `onComplete`, and + here `cont.resume` would fail. */ + if (mode != Mode.FIRST_OR_DEFAULT && mode != Mode.FIRST && cont.isActive) { + cont.resume(value as T) + } return } when { @@ -178,14 +215,43 @@ private suspend fun Publisher.awaitOne( cont.resume(default as T) } cont.isActive -> { + // the check for `cont.isActive` is just a slight optimization and doesn't affect correctness cont.resumeWithException(NoSuchElementException("No value received via onNext for $mode")) } } } override fun onError(e: Throwable) { - cont.resumeWithException(e) + if (tryEnterTerminalState("onError")) { + cont.resumeWithException(e) + } + } + + /** + * Enforce rule 2.4: assume that the [Publisher] is in a terminal state after [onError] or [onComplete]. + */ + private fun tryEnterTerminalState(signalName: String): Boolean { + if (inTerminalState) { + gotSignalInTerminalStateException(cont.context, signalName) + return false + } + inTerminalState = true + return true } }) } +/** + * Enforce rule 2.4 (detect publishers that don't respect rule 1.7): don't process anything after a terminal + * state was reached. + */ +private fun gotSignalInTerminalStateException(context: CoroutineContext, signalName: String) = + handleCoroutineException(context, + IllegalStateException("'$signalName' was called after the publisher already signalled being in a terminal state")) + +/** + * Enforce rule 1.1: it is invalid for a publisher to provide more values than requested. + */ +private fun moreThanOneValueProvidedException(context: CoroutineContext, mode: Mode) = + handleCoroutineException(context, + IllegalStateException("Only a single value was requested in '$mode', but the publisher provided more")) \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt index 18cd012d16..a1467080ac 100644 --- a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt @@ -9,6 +9,8 @@ import org.junit.Test import org.junit.runner.* import org.junit.runners.* import org.reactivestreams.* +import java.lang.IllegalStateException +import java.lang.RuntimeException import kotlin.coroutines.* import kotlin.test.* @@ -130,6 +132,136 @@ class IntegrationTest( finish(3) } + /** + * Test that the continuation is not being resumed after it has already failed due to there having been too many + * values passed. + */ + @Test + fun testNotCompletingFailedAwait() = runTest { + try { + expect(1) + Publisher { sub -> + sub.onSubscribe(object: Subscription { + override fun request(n: Long) { + expect(2) + sub.onNext(1) + sub.onNext(2) + expect(4) + sub.onComplete() + } + + override fun cancel() { + expect(3) + } + }) + }.awaitSingle() + } catch (e: java.lang.IllegalArgumentException) { + expect(5) + } + finish(6) + } + + /** + * Test the behavior of [awaitOne] on unconforming publishers. + */ + @Test + fun testAwaitOnNonconformingPublishers() = runTest { + fun publisher(block: Subscriber.(n: Long) -> Unit) = + Publisher { subscriber -> + subscriber.onSubscribe(object: Subscription { + override fun request(n: Long) { + subscriber.block(n) + } + + override fun cancel() { + } + }) + } + val dummyMessage = "dummy" + val dummyThrowable = RuntimeException(dummyMessage) + suspend fun assertDetectsBadPublisher( + operation: suspend Publisher.() -> T, + message: String, + block: Subscriber.(n: Long) -> Unit, + ) { + assertCallsExceptionHandlerWith { + try { + publisher(block).operation() + } catch (e: Throwable) { + if (e.message != dummyMessage) + throw e + } + }.let { + assertTrue("Expected the message to contain '$message', got '${it.message}'") { + it.message?.contains(message) ?: false + } + } + } + + // Rule 1.1 broken: the publisher produces more values than requested. + assertDetectsBadPublisher({ awaitFirst() }, "provided more") { + onNext(1) + onNext(2) + onComplete() + } + + // Rule 1.7 broken: the publisher calls a method on a subscriber after reaching the terminal state. + assertDetectsBadPublisher({ awaitSingle() }, "terminal state") { + onNext(1) + onError(dummyThrowable) + onComplete() + } + assertDetectsBadPublisher({ awaitSingleOrDefault(2) }, "terminal state") { + onComplete() + onError(dummyThrowable) + } + assertDetectsBadPublisher({ awaitFirst() }, "terminal state") { + onNext(0) + onComplete() + onComplete() + } + assertDetectsBadPublisher({ awaitFirstOrDefault(1) }, "terminal state") { + onComplete() + onNext(3) + } + assertDetectsBadPublisher({ awaitSingle() }, "terminal state") { + onError(dummyThrowable) + onNext(3) + } + + // Rule 1.9 broken (the first signal to the subscriber was not 'onSubscribe') + assertCallsExceptionHandlerWith { + try { + Publisher { subscriber -> + subscriber.onNext(3) + subscriber.onComplete() + }.awaitFirst() + } catch (e: NoSuchElementException) { + // intentionally blank + } + }.let { assertTrue(it.message?.contains("onSubscribe") ?: false) } + } + + private suspend inline fun assertCallsExceptionHandlerWith( + crossinline operation: suspend () -> Unit): E + { + val caughtExceptions = mutableListOf() + val exceptionHandler = object: AbstractCoroutineContextElement(CoroutineExceptionHandler), + CoroutineExceptionHandler + { + override fun handleException(context: CoroutineContext, exception: Throwable) { + caughtExceptions += exception + } + } + return withContext(exceptionHandler) { + operation() + caughtExceptions.single().let { + assertTrue(it is E) + it + } + } + } + private suspend fun checkNumbers(n: Int, pub: Publisher) { var last = 0 pub.collect { From d23dd3d7e8cf120bf51271043c496888accc92b2 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 7 Apr 2021 16:03:44 +0300 Subject: [PATCH 23/55] Remove internal 'checkCompletion' function and replace it with the identical 'ensureActive' extension to reduce code duplication (#2467) --- kotlinx-coroutines-core/common/src/Builders.common.kt | 2 +- kotlinx-coroutines-core/common/src/JobSupport.kt | 2 +- kotlinx-coroutines-core/common/src/Yield.kt | 7 +------ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/Builders.common.kt b/kotlinx-coroutines-core/common/src/Builders.common.kt index 09ae9b685d..a11ffe9eb4 100644 --- a/kotlinx-coroutines-core/common/src/Builders.common.kt +++ b/kotlinx-coroutines-core/common/src/Builders.common.kt @@ -150,7 +150,7 @@ public suspend fun withContext( val oldContext = uCont.context val newContext = oldContext + context // always check for cancellation of new context - newContext.checkCompletion() + newContext.ensureActive() // FAST PATH #1 -- new context is the same as the old one if (newContext === oldContext) { val coroutine = ScopeCoroutine(newContext, uCont) diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt index 438e7f5722..0a3dd23472 100644 --- a/kotlinx-coroutines-core/common/src/JobSupport.kt +++ b/kotlinx-coroutines-core/common/src/JobSupport.kt @@ -541,7 +541,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren public final override suspend fun join() { if (!joinInternal()) { // fast-path no wait - coroutineContext.checkCompletion() + coroutineContext.ensureActive() return // do not suspend } return joinSuspend() // slow-path wait diff --git a/kotlinx-coroutines-core/common/src/Yield.kt b/kotlinx-coroutines-core/common/src/Yield.kt index 975a48935c..98e210412b 100644 --- a/kotlinx-coroutines-core/common/src/Yield.kt +++ b/kotlinx-coroutines-core/common/src/Yield.kt @@ -30,7 +30,7 @@ import kotlin.coroutines.intrinsics.* */ public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { uCont -> val context = uCont.context - context.checkCompletion() + context.ensureActive() val cont = uCont.intercepted() as? DispatchedContinuation ?: return@sc Unit if (cont.dispatcher.isDispatchNeeded(context)) { // this is a regular dispatcher -- do simple dispatchYield @@ -50,8 +50,3 @@ public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { u } COROUTINE_SUSPENDED } - -internal fun CoroutineContext.checkCompletion() { - val job = get(Job) - if (job != null && !job.isActive) throw job.getCancellationException() -} From b61a66c7a565d8ca635a38e178564adbbbfaabd2 Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Wed, 7 Apr 2021 17:11:19 +0300 Subject: [PATCH 24/55] Display artificial stack frames as calls to functions instead of text (#2461) Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/2291 --- docs/images/after.png | Bin 297058 -> 160118 bytes docs/images/before.png | Bin 161429 -> 98194 bytes .../jvm/src/debug/CoroutineDebugging.kt | 65 ++++++++++++++++++ .../jvm/src/debug/internal/DebugProbesImpl.kt | 34 +++++---- .../jvm/src/internal/StackTraceRecovery.kt | 17 +++-- .../channels/testCancelledOffer.txt | 2 +- .../channels/testOfferFromScope.txt | 4 +- .../channels/testOfferWithContextWrapped.txt | 2 +- .../channels/testOfferWithCurrentContext.txt | 2 +- .../channels/testReceiveFromChannel.txt | 4 +- .../channels/testReceiveFromClosedChannel.txt | 4 +- .../channels/testSendFromScope.txt | 4 +- .../channels/testSendToChannel.txt | 4 +- .../channels/testSendToClosedChannel.txt | 4 +- .../resume-mode/testEventLoopDispatcher.txt | 4 +- .../testEventLoopDispatcherSuspending.txt | 4 +- .../testNestedEventLoopChangedContext.txt | 2 +- ...estedEventLoopChangedContextSuspending.txt | 4 +- .../testNestedEventLoopDispatcher.txt | 2 +- ...estNestedEventLoopDispatcherSuspending.txt | 4 +- .../resume-mode/testNestedUnconfined.txt | 4 +- .../testNestedUnconfinedChangedContext.txt | 2 +- ...stedUnconfinedChangedContextSuspending.txt | 4 +- .../testNestedUnconfinedSuspending.txt | 4 +- .../resume-mode/testUnconfined.txt | 4 +- .../resume-mode/testUnconfinedSuspending.txt | 4 +- .../select/testSelectCompletedAwait.txt | 2 +- .../stacktraces/select/testSelectJoin.txt | 4 +- ...edFromLexicalBlockWhenTriggeredByChild.txt | 4 +- ...acktraceIsRecoveredFromSuspensionPoint.txt | 4 +- ...sRecoveredFromSuspensionPointWithChild.txt | 4 +- .../StackTraceRecoveryNestedScopesTest.kt | 4 +- .../test/exceptions/StackTraceRecoveryTest.kt | 12 ++-- .../jvm/test/exceptions/Stacktraces.kt | 17 +---- kotlinx-coroutines-debug/README.md | 2 +- kotlinx-coroutines-debug/src/DebugProbes.kt | 2 +- .../test/CoroutinesDumpTest.kt | 6 +- .../test/DebugProbesTest.kt | 10 +-- .../test/RunningThreadStackMergeTest.kt | 8 +-- .../test/SanitizedProbesTest.kt | 12 ++-- .../test/ScopedBuildersTest.kt | 4 +- .../test/StacktraceUtils.kt | 39 ++++------- .../CoroutinesTimeoutDisabledTracesTest.kt | 2 +- 43 files changed, 179 insertions(+), 145 deletions(-) create mode 100644 kotlinx-coroutines-core/jvm/src/debug/CoroutineDebugging.kt diff --git a/docs/images/after.png b/docs/images/after.png index 4ce15e8b4e635e033bca77ae40e66ae1f840c1ef..b1e138c68279eee31d03a286d6faa7cb6025773c 100644 GIT binary patch literal 160118 zcmb^3Wl$Z@x+r`cg1fuBOK^90hu{vugS)%CI|~V}3wO7LOM*MW9p3!+-lxuc?>Qgu zr=FgwnV#wD?q}wiUrm&Xk~9)LK0Fv07?P}vgc=waR1_E(_&glM-#sQ={_KAXNLMjg z4LCTsjo-?@z`#hsWF}FO-0G^-2*sFIf zD=%+N7grP&jxg|VLvUaa|7XD@7r0!!9^@hdh6)A+1};)K4p*p5Fl^eOKn~o@;~k9fS)gcTvd=vc+e&@ zWrhil2GdLqY6*d?BELQ(z|@lO=+9*}ymc`;V&g|ACMV0(YyiZWWRfgO_WU2+mAaiZ z)R^&@z+Rtw4~3o@(RoqvKo|%lX+XSWh=Cnem|o-6AnY7p?Lb?Gz(Ma3TE{G@^dyGL zR1Vod2&Ct9FNJCwtYuqoCWj+uQ<+2Rq0uIG%Fd{(7K@>x@{YMGY#j^ySovMqeb>(P z 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 l@NAi`fft@| zG`_V*Kt@S;JVNPY{-{TXi8)-76)UVW!9AFFp=(Z=<*88o#xvG0GdC~X8lDJtXhAeQh zov{j-6}#1@H{SLD9Y$`P%(WTe)L#bISnQ^(RF5@4W4=E$+M9787OK6xFA<4(H3G}M zc*YcmKe{Y{YzcfX1?E(T^=EjdWlmoBv{?g_RL42!OnSQcq}Q8uKYEnAGKfZm3C74$ zY!HrSP75Qc?AVWFk^A$A^J#(e^%W$zq2OULZtG*K+!W%g^6A9q&(Nz-&GoE@8cU?r zpJLzCm>dA7b3fp;Q*CK~PSR zl{|lDyCh#oaGxjdhsF0tyXJc?T4z^_Ze$_}jj6$U5ymcJqC8MPS*g*zi1sQ)h}slw z5$P`@>B9o0j8ur?EfYot1rUbvIw*2cFl#t9t|PF*A5^8?ia4lJRMbjP&|7D{}pHP?Lwm9JfVVSd*;p59W~=(O(F{*#E#U8pZy^FN%4Ye{!rY z?S#vH=nmc8)~SjMOt%6U^AC zw~c~m^0xTo8R}-wld>#oSE3O%oYY(xqklRNy1#}G+24X#f2d4yt0k0OOV`}zR)=&s z&A-#J>l{Vp8y~KxBv__T02#AVvjjWN(A^>`cLW7Li@bIaPh zhI}wBMYMsu#6~mhEz3J9tJ89(FY)8_btU~%QuIdZN~N`W@ktt5X`g2BG(RSEqzyBq5XVtzEz^O%}$X(ONVwG)rs%ZcOsB^7rD=3#XK%K)`82hGXuNP=&MJP+D;pO zARXN}J(lTN0K|a<0!p$Jj+fLk-9%AOE%{_I5{heyQw5Z7tkfYVLZ)bn4qMGvfkT3o7^A(w0mdwG%3r9)$r0^9`KrpLxdRFJCkKT<~T@ z6%@yBG-rmWTHubY`}b;^wUJ2?JtRwAvW+N00%wkwFg?jIH5@i6zYZZHhrL%zKPF)O zTFlhA&a2R0=q@I%>mX$k(KC@>F3aS<<#RRR@;ZW->^6F&-Kd>Xfd z;KQj;DajhsRFOmu7Dd~ax0OsW{E*rj1MO5?wdF~)nIj)pum)T7Qgv|1)6H{kXn3eGS+Pvaf4duy{ z#`uY4pV9+k*@FzIB$?NAV!l#^?CD(qV1Xwv0I2rHM$!XHE~6_*7hg)BaR&jdDOxwM z3o_2U^wH1c+$U8I2gnjdh5%Z^ zoC{zieerPS#1eSe{Fdj76OOJ4tG)J;QdAH{F0s6??Wf)k1gp)lPiA>|js2I|t#TGj z+Ul+!ZbM7V1hzX0R4@kxXcysS^46%KZ6jrSF(tI&qAUv6jC?qzjjlGseI;Y^-*Nw; z0BjN=J(iuA64Mp|Kbb5}?JiZ=M&=(kIl@1P5mdPexk^Y^5IG%9*ZMTcni2>9X2xUw zt^5oJb}qmSpKQ?XsPt)71Z?&L^K!josb$~o>NGoPtvUmA60~KZwbIthq=9m|h$o}Y z-R?ntqgCpCH;}H^rDJ}Cl6$Yp-g33O{&F>^zW^Th;|JDJ+Z5S=qN{6!D-j#jm+-JU10!Wa8vg@#_yBoX2iiBvr2g*p1Y!JVpV=3V4>f z!qS&S%a5OYJDvXT-JfRA^F5B>7>UBXZ(C^7CUM@Z(3FOR*HDvNCB^s=QOfO7tqvvW z+51zjY=vdz85;}5l&sz(SE=_@Tt6g|?t3p7ara}vUr(oMTRnS_(jXi7sC#rdPOPZ_ z`e(#fcY@*q+Q&E8f4~pN2m+F`F96U(JGm9OqkM`{F(DPp1*(zNSj^dMS$D~-M27_g6XQjcor4RyEDhI{GlaLm5d ztAqBtp76wbVl(YX-qZbD{Fw$&78YBCVmJ}oY+|FsyV)zT7>)(@!h68 z+8jD}A#?4sw{(*u0;@cZI!RDw^EeG^iw95OUC*Gu(Qpy@YB4KLl#p!T+5SA~>)}?b zO?b0YTxQ3`y8Kmy(DSLmzeIazz}q8r>(`kr5TpY%+Gj_3ylLAI&=}5MTZ7Y=iwV>y zb^JB&VEs)4NIWU>=JgqUW-Q84+6~MhWFeU*6~tm^F+m52_QOP$w)TZmV%GT{%+m^i z%c>m7b5y%v!U)DLRRu`VIs#f{B5^#-!I2jXXd=r#VcfOmwFOwiEz~CcX>z`xr39}htsYY-v&>Hw^@@(#x6-d4Oo9ROm?VW-zcgLvO&?CNl_ z;pvgFNdv?i9cy*E#IHf>ycic_AnDA>q~;@W+usK59c+O2 zWJBWN<>BZ3hI9vYQ1ulIqD#-#QwI`JQE=28_s({vqo-LGutZ2Avn zjXU#tFJ`^B&5>2Qs$1RknCDEu?oVg4qfXWKQBU4MYTq#z|IP`_>%*^w{0Ry~oC}7S z)oK&OEO|s@5H$~bWjiaWlIa&cy&Kdc0v^_q+jOK|Yhc;GJ%;gdau@S`%WES`9H7li zj*~OK)n$K=_GU$<v#RZL>9R@tb#A!Z^=?;ZVUZ*Bx}u23br>A_M+(XLe3foeJjc-MTG|Fm~y zRAO&Azj&IiTS?+f}g_Ud5GsY(_aS9x65V3pk~hyL!AWXft}&xu}?a_hj+H5 znMv4{4=FU-R0-9{TZ7Gm61FD5Y4kXDmPJenpc%#JjB6=_jGT0 zmAV5uXRe>$+Sh!gx*j^7W^7__i(9vCId$!GeNr5Mo?+cXnrx89IFHxch3j-2R`a7} zN!1BjI{{d)l9zpOKAeCQ^hvfX=J@8T^-hA$NB*gzq+iI)fzwkXQ;VreD;e_zXquy4 z6@g2EQiM;kx%<)g^Jy|tgLZO|WJaGBrTe8-H!ZzHL1H!Zd)KvZ2Q!$bRFysSp+iV^ zhl>SuK?T7D0ZSV*>=~HsupV&siyHCGcMi)NK-8Wor}}YKt4Oex3jtuKo5HSan+KYc zts_1TA^|j21UpF-rmpaN)S;%@7e$^|Fz2%y-b$+dtlvuP+f8ik_#gfZk+ee?GfyYly!W2jcq#HBDVDAIBd7X_bM}s8> zOiK40mYx?UeEZ{O%nyz+uiEZ>M3&ht36K!Fub7;+Hkh+(JFRO(=nvVJDcSWk^Ap6f z@d+z?3xt(R8L}?(Z-b|wy=^}A%;T4I`y24quQpxjQ5RKkuzm2jG+Kzxv>}}D{F@;h zrl}7a(*(eOY1E#QK<;3DUXpRXe>jeO0eSBMAzxFKj`E_ntg+a7OY$tYLw^gxQD(q# zyKZA%nl!fLqDQt2Lk&gYm3rA+yb~XNWzsjCN27vR1o=aI<~IV$VkxYy=GgqB`1=!k z@&W=0(_M23%HbQP<=Ql%dw=2G`XSHeg4*Efna1%5oP^$hVZ@t8FxX-HJ;)9i&xaM* z*YK|uCBhemh%sNV(Ep&BPY#3dOB+!EgKqy@o&Q&x6mzgoCEf~*S^i(3!N28tKULCC z?Y|rbB^dax-YNecAsgdUZ7;)^_Lyy-?`IPDF8RhZ+R0sa1(f^?pnEn9< znDPUQ{Clu}jbLu_NfgWv^ZT*?hfz}nKDGMPudpb8>$U#ZZrKTtzRencyU0|*FC|jV zsIJcpcovcb(dY=~DnR1z0grL;!&aIq1va4#NWMG_eir=S9(N8y(ZDzZ+@A8cAs0rzJb&KCQjq2M zRcUR{tt45C6=Z{GEz^$x&pRT;fx>B*+bm#N5-EK%MSalC~>4>%%lKBzn zvQyI_lcQ8oQTJu)5WXL0fcnP$a`z01DXAh!(ycoVuHZ{i8n8{NnL;%uitkhck(ywh zd1KqmUpwg6O5CR>X%6#t&4&_l^U7Gq@%o|~s8B8>Em~Er>ZRk`3w5d%3W4gyqo6xY26 z68-4#7SOmGzWIKi;!o;Ittn&W0r@mF1jgu-@ZtT&csX~Fj<3ukS3nWYz^<;nn%aOb z!L}j4P@vaiWjNUS_&UbW7HfKl3SFV?ZgqbL{X=9}VNQfz*lAavdcGL*wU1M0bI8_< z-qk{W`9=YF8aEQd`@q0t#ftPd;pc6B{1Eh=-N!*q&|bw%kv?Q@jUuQX!wR#)cl+Jq z8>WXZKNmFSXlU=qN{6@~e)HxL0}3f-UuZ zDiV^q29HIZvJBHmH!M$06uY^#QU#QOQNECWYp70=Q{fA{W3Dhc)K+33 z?m&*YIyk7>Tm!sr3XV|lgv+G)3#jW}?Ns05xW!Z8)=d(_y2PEa`@k2?m{4>d>l8u% z2o&Cvfi`t>dE>&SJjd<$4)8ct)hY#p+(h~FiN#C%16T2~2FjAPU ziwFPPk|y%Vjhm-hkF?A(V^J?PW*4C0Cs*GH79C@FqU(T zW2j>_gcUZ#j4;V{rBXP`YcPz(b-|ZAH^teljjXJ~NlkT^omma%FN(O;PnAehcp9m3 zLt1vA3jmuS2#(ZYr42rMTew--Sc!bc^Zu3%E*lN{`I_TJ_EW{EY3OxfxOk6*_R?;c zBJZ*u=qv;)GR`w#S@g?4eGT&uEWk+T)u8aMG3ArM%oWv7?^G_z;+1gTtqAiVxe1qf5YX2Zxs`7TTn{Ktb*vS{GZ(eRj?XI-=h7vgW5T53Fc{Rig%9)`=sAbN!Jp zigCW05iJx`5|@pORz`WSCdx)~ZN@SSqX0?a&tPR;UFTdJOD2zvXgegOE1{UpA$?S+o;{FiY%WiF%91G&m||p|?Zgg!Mjr{YYh-=J=RGV*pp^~kb1I{S9EyYuYGvfeiojZd?ABD?1 zOFWQ9Rr@i^+|F*~)*~3_ezOyQczJJDR!-Ffo*;pxQ+k%ccp%@X+J@Fcmv2+SqAPn| z=~zFcd76-H|4-Neor3+?bCywrH{GzpH^LHf%zGQ_iIuxp6$6&X@L;<@Qu%peJlcc| z(e(`dp zlWlgc+RBkiRBD&_XQTeDB8>pn0j!G8Qc^yudu zeTj6v1l`b4RHZmFMUJxvFTLX(vgy~SK_MR__I0V0K()r; zbo#K=p!c-a#P-IIe>ChL7e=c*a;`x?V=~e~JKFj%pSeST5bL~cdTI3RzX5j7me?F= zl)wz!*=>G3ny+*Q1;;B1m-@!rOY;#G^ieps@j+G5H}C_l1FiA!UAHK=lNS7)@Taz5wGHp4WT*V`1rbF>4}xEj`bH4YhxD1kG*niGAhvJCf+egi;!k zC0R(NT3FohOcns_>h`$eEfgyxq{9f;M;)zwg5YD5M5J}vt-wLIVe2mzV6)CF!{mxX zD%K_F@h!R!?)L5B^yjAiW&PgAIJaU2XYQ0?Z3UG7cWcp+P~6uawtFNY7lk27Jw+KX zE~puu`<(&4WlE&IU;NZDy ze%{^t)}Za;hB(H`7Buobzox-Y5j~snrSNh*CwK}@A*r^ESb~T4KsD6l34yABc9({8 z6Oq{=o9dcW2PehBYpq~N9Z0p@Sw1xSiTn=`!V4^mUG!fNLL-&Efx+*bRWj)tqOjEp zuqs$2M$y1{iUX8ff1r;54p)j%8YUfC8T$I6@vQIc7b%9SWW#i~A53h^lTKpMH&;{A zf{OQtJAD{69ckvw9mvg>GEYn*3j-hW_N|Gm704>AZoLasdnDWsxweM-FSmFrXIoJB zcrR5%YLuzsXbNuvsi!41h5`7Vem~X7J^*Nh*H}>hGlbBt_5VAB zP*qO&2ZUe+kHU~0Lt_q-f-hmQ#5XjlkC!O4b1znvttB}3B=U*#n&J5WS!>z?!fPi@ z^bh}uKRhQAz8)`-bFq6*dz}iz=*0`cc>|Z1m34%P(xL&9AKlzc|dE3;jYD9kc=T zIK;@VY?QU3JNzZ*vwoI}{nbHC4$vggwWItXM_V@|*dEOIAW)bM?+)8#fD#NV_HoXl zu9i=l=O$RbcL#D1K`|}PO@Ap4gw!>HuKX%OEDk$=dF~{NMt2ce`2WfvOl{}Zk~xsS zzvrxQ(kl!ZHM6?C45UkhuISGwv38;DZ$wqpVwf3;v0UuQtf40ObvOie80NBn7vDaf z+cgMq{G6lqpL5g;Fd2Ju|UAbtn51^|6^~sbJI|H-F0 zBe|tY$@h~Z>*FzkE7i^~e7~~JoXRs%%)FM-c8a>2fSLX83lUwWQp4#CYwStmO(Q z0};YR9Y|o?kfshx8GM9L?%M>X6|oE0S4-&zuB%sFROJk*`rMBZaqxApV83&d`J1A# zrbg;~|1UU$9LxU)IKw#Pf592TfhB%uzQ&KTFKHOXQ^)ZCBsP4*m17m+ApZx%PU|

)Oe348*>)O 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° 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 literal 297058 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\%9r~F@xR0pxIlbm#|}R&!7K# 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 z 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 k?8iUU9@mwxaPGn z{`+=45uS;;x%|1`V&v4MnXVEh5JvV>92-DAA@Kz8+%MgQHHcaA4l%`c+HTE0dAfHw zGg~0Hd{p8!Wi%p_)667w1qkgQEM7|O(S7x~hQE?wQ4U0KUq`Snb(@xIMN03;83B{RO`>Z^w!d%RrRJ{z8v-V6jUS1}K; ziBOcoilFxrC>6|#k2Tk&5}sf5Up`BX%l}cb;%m}5j3PC7%d#>bho$UTGNd%&evZK~ zMJ~ObdOlf6c(~uYVk(Ao3d>aGNth$z5}kSoe6C!#Lu30NETRGx(~fSBwMQC*7ha59 zB;+Delm{g}{P(V(Z<^MA9)PM%N2R8g27#RC_qzMENZH_&)csPu4y4cH!he`-JJH`W zJs%)Qeh<>Tfm$372k)1Z9XL{3lRnhvuA#oNAC!%WcjKdG!k4(;3%%X1DOL|t%Y)2z z2k7fMJ;aLxh*1IDP z`&asEpk-B^UaT1BNYK(&mtp)I<>R}3OLKW^xN*Clk)EbdVsALK@Y!KSb(%eiH)ZY* z7>zrkFi%Lyq4K@Jb3as@A4Rh9pofk4b$Pw6+DWpk)}*#6*L0O+43$Z|HRen~sRLIaJ$6xz8KRw^cp_HEHx0g_^z z;$GpEnfxl0qSV4?#`q1&qQ$%qH0OJYaVm)cEVd~)d4n*0?m`#q*>UyLO_$`+!i<++ zs&{j|m`yuGXOEIaN~*j|Di3q;HacM%RbrJZc|KuEhHQyyk!s-y_oy<50lZZ0uGwP= zr?bLnXhCd8UW-&~@c0{X`dz{->ciYDfRI>WMFLg&9IlWC*@`IhQl();JWK|< zaG2g8gmdxh()BGx?4&nN8f{E#KQH%s8oaiY#+goj>K6y^A zUklKx&lhvu2{khFei+Gq2k8@xkQ@B3Y-ZSF5otl$VOniJY~H>a;rousP{J=s){yZluDf9yalQdofm`xz`{6IhU$DC zb$)(ro+;-VdkdL8ng&fV*Q#&V*udxdoFkb>bH+?@5QpJUXE96)+XT>c(hXQ zaO2Pf{jXKt+#LSQ&lR1iPS?fPw4ke8_vma6tmqFDp%1E^Ka{nR1woOZZTL0(AC|AP zz2@hJ!Lt?Y?~N^uk0P(72%g#H$acCSwu9+U9m|7r69|T0Afsi>0(3q*ZRcQ<9+-e3OXB zG3;0@&)@iPvE0QKysR}}xLo91EIv;CrR-8cfAfjPQZ~g4 z72}Vvcwgeq_dzw{v;YGABa*OO9WUV(}R^G`&0gq-)+X zVB>4Jb_YvYF&i7kduA$m;04*48R;Soh=gyui5#qop2t+{k;@IJWy|Ju4aD9@lWy7V z#X~oe6g6*BlBM?q;UmmnyL_E))ntqr0{wt>lW~8)jL;vaSO_DE|gME9meel(qz8n17 zJZ~^L$V|eOkr}@2j$f~Gt3l2s`R0AvEeY)vbmV(52~T|ymA)|$_l|z4AZv~vm6h5x z{b}Vg^c*)I4Vg*Y-j@mG_6WdF`*=5-wNFr zn+W}>=yfKPm!I$;sJpc!fIT^aU_~RLyd7&?dwL5O|5-Ovsp=fA{#Z zd=g8XSkjjwfGTyEfL2C{JAih>=y+G=X)VQBTu-lq}+OU2p*l2{7(&` z>OQLPJ3T+g=R_hD@^C)^Id%D|+ws8sC}P2k!9FzlEg=N(zWCb+{+4R7kR+Gji8$Oo`<)FCQg2`pnFr^?bFrbSLVtS96jAPwF-V;^0`hH&h?H!o+N$8 zm#LXkl;ZPbWv0hibqKHuY##+le-m~h+8CtRPmCMR;9%X6!V&1Ue?kc=gzzSO^FBfy zKJ}>vJyUWyfje^FY9^$CNFoSp;eBjxk{=MIB1Gnomk8=K#U{&TAX1>9-ZH^(vSyvo!5957#ZUXUpJ5@SXbud-8&42B za4SSFq*UPH5+v(4TFWIiH-FO@6}jp=1B$sreHH1qd=Fo?y`I@@_aCx7emE^wal6zBPWOuj;IyTunyRw@Sr*u)B9*lOw0uHm78Ss8y&z{Y$e36tRSPd{)a@jfL z#$4cC(SN~~!@(DtkOcq--iT%GT|52M^iA~Hz%MNo=vlCxuLEfv6_8X2N9X!r>=(0) zs6Uzhdl$w0!Xq~tivNUv%3+I@iQtER*A+dfSRV`sw})OfD0|N>En`b80xmGR^#e-P zDP*|d5hU0@r~f~6ol|sZO|)iX+d8pr z+qP|;*tYHD#I|kQwr!i8|8|f2*01}q_NW?b)v9k!1dH#6C1_qYt!m#kYnZC@yiq|;V? z_XuIZFhWLeei_o13ZHj@@oIYiT(cN2kGsa;Z{RuV#_}}kxa9)A8CFXCJZuBfJ?{Ez z;e#beXne9S%#EN_@MkT0+W{}RV8miot;ARdW`}S20zsKld!MjtT^SdN{06`KcfpbR z@N$!Sn=W=yUwNVv;^F`#Abhr9*=+lgnQn!2+}yBa^#DNO#DQ&P`-aSB4VgDRU|eei zKZ@<%>ABs3iIw}CZFs$*vU0MI&#K?K^e|aSH?i7+GE!3zPZM4bB^>nCH6VNTB`FDs zGxHNNtpw}|MN2FS_vbr1=QlU&Klg1vY2o98O3AsNnzZ^8$<7226UHM!EW>k|xgxdt zE8cC=XDQx(J134eNmN}53Ld6WlDjfpMRN`NVB#R={(Y~mUz^?SV9B+J1&)EJ!+Bzf z1s)P?YDK(eKi$L>Dpb(s{)?-iqQ zvVAoI)6m3N<`cWDML{M;oD>%6sHodU%*Ucbd&skEs_4Q(0NEbb))w_PdSvCnK{21KzU+$nBO@I&=sDrxs7&`ifHV9dk~z{ zf1fZEO=gSPbd?IXa2&(wl524h68udN{N@FqNI*_CHBnNTgp&1oX_4R>R{Oky@;{;b2Lvhg?Br)$|OqREomQOzE7H@Zb0&iAnim0)nDMkjYy38Se~oHm?+H z?4G?aYJ_-JX<;rgnbM?85rerTV2v;zuA>7-u!*Klu(h&b_BM+b#hP({p(@Y1?{j2u ztEg_mu8u)bVN^ME&|)p+_yLL|3X*_soL+yTnMF2ay=DNUY(kB+h0R6***+BXwwUGU z`U;NJ0~2rCg>~BgcNu$&dQtKC`J&?Q^rj?D&GCC>OvHp^jXMT`AS+2rIbDx9RZ?=l zpmgj+rIf#vUmm(BEfx7RBbV>}+1o%iPqH9T(%ebfc6d_Gwg8V?27XL&4`dNS#5;C8 zf&(kLlbVQm=t^Q->!qF$SAsB*q^Ywqtno!|o{x77COWC?mJ(V;N|HPk<^CV>m)$hH zZ^rw##Cd3{2nR{~I4h)f-*0&RydX&!l@`t6kv3&)aY>buYV`IoxAJk%NWmt)e7xTY zY(qM>jN_}p9kR^iy^Jwq26d&DtqjRts=h%4h{CUWcWX?|h;*cbB;EsY4jO< z=#0=3<>YV-HJz@zIo{r}q0PiAp|CvRH`wmwRo)_$-V!TcS^1kxJ2^VzA(@ku|Iz-Ito_>inc`^~7 zhvy(*A>k6EobM-zkcWzjo^psnp4@)_M^Zr|rpGaTBvJW=&7?>WsPBe<{LO^qpGGDU zt*$*%f&&!gv3d@Fo;(~_QvZBqse0n_K}c2`yMEv0G?OW;cFV_!7<`Acl|;Cx=9fAx z6?K)SLcvm~gH4P)epU89ap!L;-y7lA-8C*Xl_E*?c!5GFYZK!~c6d@$(%MGaX-`MF zyRs}LF>dE;oAwM0npEfjf!U+Z*D_I(%iA}w7&2TUCR{}sxR*3F|gk?eL3S`zfOCU1e?fY$iLPaRT`;P8;& zz~>Dc_V7fo+Moi_BR54zm)2R60#CeDmCG5<>ptX*%b?Foy&X*!&+mO10TCJqv{d+( z70U{%Z<~nT7i&@j2pv8k5bJY5p!gT%UFST5_U8}u=YZsI$@uciOG37kUHo_Sn^X2| zGOpX0C#i-$>B(f_I&2G#$=yase7SbeYMy*qR1p+I;qG}bJ*!vyPxt8<_W)awgIFf+ z*m1(>Sv(Q&W+R2xlgaZj^~?i0#0?;Sckk_D?a^khCrS%XE}xxR@sRm;&9*JY61Hep zFjH-p&9&luXV^mGAp!kp5}4yXdrmUOzL(+aoXs=iJZHG#{_ty9bk5>BL3Qpw4;#4y z0eC5x=-_bFHl4Bm$M>_8Kr-!k(ps&a7@rpSKjD0Azt#wJ_pieV4JI9QT8}34xe1N` zQCvzT?ObZ*ehYvUX?GuLdJ zKUKBgj?m^Q67h1Jb=nuL@AlOm&_z8&;#FLdlZbaZ>1;NiLL=EYWwDmM-a>M4H}Sj; z!;N%Yj?_qgfeLKr6X!b5E*snZhW5$o)h!zv9FCbMNnAE{6PX-zXms$<6Z`-X^Wr(y z&^xy7)G6^Qcr5eTdd~kE$Vn(+6EhDmmUu-I~n5C)OG|XLM4>?&xYUW423yy7yR<*_>xcceJA_dq zu5r-W{`zaHRt@Rl_Wo7=k2VsmomTzUaBVnf)i9cw$?c{y_2IaKR-=-w+vk#fChPZF zR)7$HkVFA{xks~^`Er>_I1Y0~wu?1NY-S-f=suz|Z}#Tx;z;UOK$?&DhxbK$Q4=Kj z)R@BnJw-uPQ(QxWZtUX`wJeHKJS;L`xn>C-EtR7|2c6En$*%h$N~4C37mez!Tt!L5 zmRDd4ZR>X*I4CC{y^dAD#s4b+M$bfLmu5Jh3E+{wQN?dNRDU1bdkCA6D6pDV>K>n9c^QQcbd zO}@N)PUK`5!2YfF9;)!lqmf>*SBqYoV}+GHK*^q1cHITD5r0jF_bE_)~}1$nw{; z_64VrbmALT33n&o^X0jROxhcrZ5xeVa*{GYlY;4BD_thu?Pnm|3JRE>N0;*zoZX&*#vZei16L&l- z;*-QB&~|}{5@yQ6Ga@TwCI_FFX3I?aJN%KR$>JRxXEK5 ze43J^Jk~jxdTGB?&Z|xem`I$#b*90Kg+@>2ns#5A(;lxXB|f~q5Sb9X5mSo>i>@Vq z-Cb_M_BoJ`4+lA`;j_v9a(L$c3UkcjQ`?zRf6)i>=J55SAGtaxUZZ2XmXM?s36U#r z`ogT^6)I0uSNhE+wZgQ?(@xxA{Rr?fr^vex|hK18)Q z8778%vCrY+o1{tyfijrqigo@yC3P_gcQp7ib-e5b7!-nHJGx7L$?WH#baOo>>~z|h z2sp6_{B6WLYQOBW+!)0K-KsAZrUC>^UXQ{|zCBK}YN@tlG$o>G_mjO$LI!z!d#~P- zw}(u08lMKk;BL*EhL$7<%X~_TiHBs3CtKB$;3qXYN+agpT21WvzKT$KSHR3?AZz!P zdxHngv>sGJ*t&oBkSTP2ppp7xNa~#!rc?e|;JM-e9{bk=;~ez1ywutdOlAX}YqjPC zlmDQxn}PHo(~(*mVU8#BnnfZF_JGnDplx~pw#@F{mhgoTkO3mmdNyq`|9S^q09;-(UtU!@3|UQu}Ip{KcUVyKg5X)th0KzoG2kZa9_Uc?{4QGF9WPJeUAG9&DpYYlmi$k(WX;3O)l0WWlR<@ z=?A%cJS&xFO&-oIYYSCX(%+T3+&5d$v3f7}^U7WDS()$8Dj)t?P&U~w>)uM#Bvl#B zSKQ4dlOL+LcS?k1lJVEbt#52T>D6EGL^xId3U!fkjOMy`dJAHG=B3s>zC z>QEr67#S7Q%FR2BJzLy?gBWxrL(zn8GFuEuOu)I-&cy?iD4}3!?q8U$7z_g{{^qzF zz2#_k!u_E|E!&IjW07c~b-!V+NzsDsa<+|_NCb11QH1|G_TBqxp$#fppsdLtQjly{ z&6~%DEm#QKCzKE`Ej~BmB3!(?+fQQ7T+3y90juMP81dQ8aVg0al;{Hb3_)>5RA_*2qw z{(`+^G*oZewH_V-WCFyXENGcTi979LuGVA!7cFB*zux+S^_o7fCQwMEvDO-^`ODoU z_UK`15y?lnQB})C~9-peG z`R4O4(Pyujd1t;`mnllQWV;8=?UqF#=Ho74BCO%0DlLDMhm2@vI1ZvbZ*FP+F%Eg+ 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

q%G$yP|- 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�XAwN(TSRPot&c73QtDj#|u0(x^eD& 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
uH$Bd{GLxDUX@S)$zc>BU{6 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 diff --git a/kotlinx-coroutines-core/jvm/src/debug/CoroutineDebugging.kt b/kotlinx-coroutines-core/jvm/src/debug/CoroutineDebugging.kt new file mode 100644 index 0000000000..49a794e046 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/src/debug/CoroutineDebugging.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +/* This package name is like this so that +1) the artificial stack frames look pretty, and +2) the IDE reliably navigates to this file. */ +package _COROUTINE + +/** + * A collection of artificial stack trace elements to be included in stack traces by the coroutines machinery. + * + * There are typically two ways in which one can encounter an artificial stack frame: + * 1. By using the debug mode, via the stacktrace recovery mechanism; see + * [stacktrace recovery](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/topics/debugging.md#stacktrace-recovery) + * documentation. The usual way to enable the debug mode is with the [kotlinx.coroutines.DEBUG_PROPERTY_NAME] system + * property. + * 2. By looking at the output of DebugProbes; see the + * [kotlinx-coroutines-debug](https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-debug) module. + */ +internal class ArtificialStackFrames { + /** + * Returns an artificial stack trace element denoting the boundary between coroutine creation and its execution. + * + * Appearance of this function in stack traces does not mean that it was called. Instead, it is used as a marker + * that separates the part of the stack trace with the code executed in a coroutine from the stack trace of the code + * that launched the coroutine. + * + * In earlier versions of kotlinx-coroutines, this was displayed as "(Coroutine creation stacktrace)", which caused + * problems for tooling that processes stack traces: https://github.com/Kotlin/kotlinx.coroutines/issues/2291 + * + * Note that presence of this marker in a stack trace implies that coroutine creation stack traces were enabled. + */ + fun coroutineCreation(): StackTraceElement = Exception().artificialFrame(_CREATION::class.java.simpleName) + + /** + * Returns an artificial stack trace element denoting a coroutine boundary. + * + * Appearance of this function in stack traces does not mean that it was called. Instead, when one coroutine invokes + * another, this is used as a marker in the stack trace to denote where the execution of one coroutine ends and that + * of another begins. + * + * In earlier versions of kotlinx-coroutines, this was displayed as "(Coroutine boundary)", which caused + * problems for tooling that processes stack traces: https://github.com/Kotlin/kotlinx.coroutines/issues/2291 + */ + fun coroutineBoundary(): StackTraceElement = Exception().artificialFrame(_BOUNDARY::class.java.simpleName) +} + +// These are needed for the IDE navigation to detect that this file does contain the definition. +private class _CREATION +private class _BOUNDARY + +internal val ARTIFICIAL_FRAME_PACKAGE_NAME = "_COROUTINE" + +/** + * Forms an artificial stack frame with the given class name. + * + * It consists of the following parts: + * 1. The package name, it seems, is needed for the IDE to detect stack trace elements reliably. It is `_COROUTINE` since + * this is a valid identifier. + * 2. Class names represents what type of artificial frame this is. + * 3. The method name is `_`. The methods not being present in class definitions does not seem to affect navigation. + */ +private fun Throwable.artificialFrame(name: String): StackTraceElement = + with(stackTrace[0]) { StackTraceElement(ARTIFICIAL_FRAME_PACKAGE_NAME + "." + name, "_", fileName, lineNumber) } diff --git a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt index 4c88cc976a..49294d73e1 100644 --- a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt +++ b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt @@ -7,7 +7,6 @@ package kotlinx.coroutines.debug.internal import kotlinx.atomicfu.* import kotlinx.coroutines.* import kotlinx.coroutines.debug.* -import kotlinx.coroutines.internal.* import kotlinx.coroutines.internal.ScopeCoroutine import java.io.* import java.lang.StackTraceElement @@ -18,10 +17,10 @@ import kotlin.concurrent.* import kotlin.coroutines.* import kotlin.coroutines.jvm.internal.CoroutineStackFrame import kotlin.synchronized -import kotlinx.coroutines.internal.artificialFrame as createArtificialFrame // IDEA bug workaround +import _COROUTINE.ArtificialStackFrames internal object DebugProbesImpl { - private const val ARTIFICIAL_FRAME_MESSAGE = "Coroutine creation stacktrace" + private val ARTIFICIAL_FRAME = ArtificialStackFrames().coroutineCreation() private val dateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss") private var weakRefCleanerThread: Thread? = null @@ -219,7 +218,7 @@ internal object DebugProbesImpl { info.state out.print("\n\nCoroutine ${owner.delegate}, state: $state") if (observedStackTrace.isEmpty()) { - out.print("\n\tat ${createArtificialFrame(ARTIFICIAL_FRAME_MESSAGE)}") + out.print("\n\tat $ARTIFICIAL_FRAME") printStackTrace(out, info.creationStackTrace) } else { printStackTrace(out, enhancedStackTrace) @@ -416,15 +415,17 @@ internal object DebugProbesImpl { return createOwner(completion, frame) } - private fun List.toStackTraceFrame(): StackTraceFrame? = - foldRight(null) { frame, acc -> - StackTraceFrame(acc, frame) - } + private fun List.toStackTraceFrame(): StackTraceFrame = + StackTraceFrame( + foldRight(null) { frame, acc -> + StackTraceFrame(acc, frame) + }, ARTIFICIAL_FRAME + ) private fun createOwner(completion: Continuation, frame: StackTraceFrame?): Continuation { if (!isInstalled) return completion val info = DebugCoroutineInfoImpl(completion.context, frame, sequenceNumber.incrementAndGet()) - val owner = CoroutineOwner(completion, info, frame) + val owner = CoroutineOwner(completion, info) capturedCoroutinesMap[owner] = true if (!isInstalled) capturedCoroutinesMap.clear() return owner @@ -447,9 +448,9 @@ internal object DebugProbesImpl { */ private class CoroutineOwner( @JvmField val delegate: Continuation, - @JvmField val info: DebugCoroutineInfoImpl, - private val frame: CoroutineStackFrame? + @JvmField val info: DebugCoroutineInfoImpl ) : Continuation by delegate, CoroutineStackFrame { + private val frame get() = info.creationStackBottom override val callerFrame: CoroutineStackFrame? get() = frame?.callerFrame @@ -467,12 +468,10 @@ internal object DebugProbesImpl { private fun sanitizeStackTrace(throwable: T): List { val stackTrace = throwable.stackTrace val size = stackTrace.size - val probeIndex = stackTrace.indexOfLast { it.className == "kotlin.coroutines.jvm.internal.DebugProbesKt" } + val traceStart = 1 + stackTrace.indexOfLast { it.className == "kotlin.coroutines.jvm.internal.DebugProbesKt" } if (!sanitizeStackTraces) { - return List(size - probeIndex) { - if (it == 0) createArtificialFrame(ARTIFICIAL_FRAME_MESSAGE) else stackTrace[it + probeIndex] - } + return List(size - traceStart) { stackTrace[it + traceStart] } } /* @@ -483,9 +482,8 @@ internal object DebugProbesImpl { * If an interval of internal methods ends in a synthetic method, the outermost non-synthetic method in that * interval will also be included. */ - val result = ArrayList(size - probeIndex + 1) - result += createArtificialFrame(ARTIFICIAL_FRAME_MESSAGE) - var i = probeIndex + 1 + val result = ArrayList(size - traceStart + 1) + var i = traceStart while (i < size) { if (stackTrace[i].isInternalMethod) { result += stackTrace[i] // we include the boundary of the span in any case diff --git a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt index 48e8790cd1..cf6506ef06 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt @@ -7,6 +7,8 @@ package kotlinx.coroutines.internal import kotlinx.coroutines.* +import _COROUTINE.ARTIFICIAL_FRAME_PACKAGE_NAME +import _COROUTINE.ArtificialStackFrames import java.util.* import kotlin.coroutines.* import kotlin.coroutines.intrinsics.* @@ -18,6 +20,8 @@ import kotlin.coroutines.intrinsics.* private const val baseContinuationImplClass = "kotlin.coroutines.jvm.internal.BaseContinuationImpl" private const val stackTraceRecoveryClass = "kotlinx.coroutines.internal.StackTraceRecoveryKt" +private val ARTIFICIAL_FRAME = ArtificialStackFrames().coroutineBoundary() + private val baseContinuationImplClassName = runCatching { Class.forName(baseContinuationImplClass).canonicalName }.getOrElse { baseContinuationImplClass } @@ -42,7 +46,7 @@ private fun E.sanitizeStackTrace(): E { val adjustment = if (endIndex == -1) 0 else size - endIndex val trace = Array(size - lastIntrinsic - adjustment) { if (it == 0) { - artificialFrame("Coroutine boundary") + ARTIFICIAL_FRAME } else { stackTrace[startIndex + it - 1] } @@ -91,13 +95,13 @@ private fun recoverFromStackFrame(exception: E, continuation: Co * IllegalStateException * at foo * at kotlin.coroutines.resumeWith - * (Coroutine boundary) + * at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) * at bar * ...real stackTrace... * caused by "IllegalStateException" (original one) */ private fun createFinalException(cause: E, result: E, resultStackTrace: ArrayDeque): E { - resultStackTrace.addFirst(artificialFrame("Coroutine boundary")) + resultStackTrace.addFirst(ARTIFICIAL_FRAME) val causeTrace = cause.stackTrace val size = causeTrace.frameIndex(baseContinuationImplClassName) if (size == -1) { @@ -187,12 +191,7 @@ private fun createStackTrace(continuation: CoroutineStackFrame): ArrayDeque.frameIndex(methodName: String) = indexOfFirst { methodName == it.className } private fun StackTraceElement.elementWiseEquals(e: StackTraceElement): Boolean { diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt index cfed5af47c..010dab389d 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt @@ -1,5 +1,5 @@ kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@2a06d350 - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:170) at kotlinx.coroutines.channels.ChannelCoroutine.offer(ChannelCoroutine.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testCancelledOffer$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:153) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferFromScope.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferFromScope.txt index bf3fd3a3ca..aa5a6a17e1 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferFromScope.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferFromScope.txt @@ -1,10 +1,10 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:109) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:167) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:162) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendFromScope$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:172) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:112) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:109) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt index 612d00de06..4908d3be38 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt @@ -1,6 +1,6 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithContextWrapped$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:98) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:199) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:194) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithContextWrapped$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:100) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt index 833afbf8aa..1eb464c71b 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt @@ -1,6 +1,6 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithCurrentContext$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:86) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:210) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:205) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithCurrentContext$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:89) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt index 66bb5e5e2e..af8c1fd389 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt @@ -1,8 +1,8 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:97) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceive(StackTraceRecoveryChannelsTest.kt:116) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:101) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:97) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromClosedChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromClosedChannel.txt index 76c0b1a8fa..3f392cd31d 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromClosedChannel.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromClosedChannel.txt @@ -1,8 +1,8 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:110) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceive(StackTraceRecoveryChannelsTest.kt:116) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:111) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:110) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendFromScope.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendFromScope.txt index 9f932032bd..49c3628bb2 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendFromScope.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendFromScope.txt @@ -1,10 +1,10 @@ kotlinx.coroutines.RecoverableTestCancellationException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:136) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:167) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:162) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendFromScope$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:172) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1$deferred$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:126) Caused by: kotlinx.coroutines.RecoverableTestCancellationException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:136) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt index dab728fa79..4a8e320e2d 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt @@ -2,11 +2,11 @@ java.util.concurrent.CancellationException: RendezvousChannel was cancelled at kotlinx.coroutines.channels.AbstractChannel.cancel(AbstractChannel.kt:630) at kotlinx.coroutines.channels.ReceiveChannel$DefaultImpls.cancel$default(Channel.kt:311) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:52) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelSend(StackTraceRecoveryChannelsTest.kt:73) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:56) Caused by: java.util.concurrent.CancellationException: RendezvousChannel was cancelled at kotlinx.coroutines.channels.AbstractChannel.cancel(AbstractChannel.kt:630) at kotlinx.coroutines.channels.ReceiveChannel$DefaultImpls.cancel$default(Channel.kt:311) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:52) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToClosedChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToClosedChannel.txt index 54fdbb3295..f2609594f3 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToClosedChannel.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToClosedChannel.txt @@ -1,8 +1,8 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelSend(StackTraceRecoveryChannelsTest.kt:74) at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:44) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcher.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcher.txt index 6b40ec8308..0e75e64511 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcher.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcher.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testEventLoopDispatcher$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62) @@ -9,4 +9,4 @@ kotlinx.coroutines.RecoverableTestException Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testEventLoopDispatcher$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcherSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcherSuspending.txt index 5afc559fe0..0792646ed4 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcherSuspending.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcherSuspending.txt @@ -1,10 +1,10 @@ otlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:99) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:116) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:110) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:101) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testEventLoopDispatcherSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:89) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:99) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContext.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContext.txt index 406b2d1c9c..f3ca1fc4e0 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContext.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContext.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:54) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContextSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContextSuspending.txt index 86ec5e4bb2..dbb574fead 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContextSuspending.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContextSuspending.txt @@ -1,6 +1,6 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:130) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:124) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:115) @@ -8,4 +8,4 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContextSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:102) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcher.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcher.txt index d9098bbaad..e17e2db685 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcher.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcher.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcher$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:47) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcherSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcherSuspending.txt index 8caed7ac0c..26e035992c 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcherSuspending.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcherSuspending.txt @@ -1,6 +1,6 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:130) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:124) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:115) @@ -8,4 +8,4 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcherSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:95) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfined.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfined.txt index a2cd009dc8..f247920ee5 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfined.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfined.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfined$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:27) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62) @@ -10,4 +10,4 @@ kotlinx.coroutines.RecoverableTestException Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfined$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:27) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContext.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContext.txt index a786682b7e..b7ae52c9d3 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContext.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContext.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:34) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContextSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContextSuspending.txt index 8c937a7c6b..241a3b2342 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContextSuspending.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContextSuspending.txt @@ -1,6 +1,6 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:148) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:140) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:130) @@ -8,4 +8,4 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContextSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:94) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedSuspending.txt index b6eef47911..4484c66432 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedSuspending.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedSuspending.txt @@ -1,6 +1,6 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:148) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:140) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:130) @@ -8,4 +8,4 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:87) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt index 9b9cba3eb4..4f0103ecc8 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfined$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62) @@ -9,4 +9,4 @@ kotlinx.coroutines.RecoverableTestException Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfined$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfinedSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfinedSuspending.txt index ca0bbe7fb8..fb742a3076 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfinedSuspending.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfinedSuspending.txt @@ -1,9 +1,9 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:140) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:130) at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfinedSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:82) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectCompletedAwait.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectCompletedAwait.txt index dbc39ccc55..2e86a7ad18 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectCompletedAwait.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectCompletedAwait.txt @@ -1,6 +1,6 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$testSelectCompletedAwait$1.invokeSuspend(StackTraceRecoverySelectTest.kt:40) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$testSelectCompletedAwait$1.invokeSuspend(StackTraceRecoverySelectTest.kt:41) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$testSelectCompletedAwait$1.invokeSuspend(StackTraceRecoverySelectTest.kt:40) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt index 2d48086150..3f404cd937 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$doSelect$$inlined$select$lambda$1.invokeSuspend(StackTraceRecoverySelectTest.kt:33) - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$testSelectJoin$1.invokeSuspend(StackTraceRecoverySelectTest.kt:20) Caused by: kotlinx.coroutines.RecoverableTestException at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$doSelect$$inlined$select$lambda$1.invokeSuspend(StackTraceRecoverySelectTest.kt:33) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) \ No newline at end of file + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromLexicalBlockWhenTriggeredByChild.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromLexicalBlockWhenTriggeredByChild.txt index ab23c9a369..ac40dc152b 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromLexicalBlockWhenTriggeredByChild.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromLexicalBlockWhenTriggeredByChild.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 200 ms - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest.outerChildWithTimeout(StackTraceRecoveryWithTimeoutTest.kt:48) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest$testStacktraceIsRecoveredFromLexicalBlockWhenTriggeredByChild$1.invokeSuspend(StackTraceRecoveryWithTimeoutTest.kt:40) Caused by: kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 200 ms at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:116) - at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:86) \ No newline at end of file + at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:86) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPoint.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPoint.txt index d3497face6..9d5ddb6621 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPoint.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPoint.txt @@ -1,5 +1,5 @@ kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 200 ms - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest.suspendForever(StackTraceRecoveryWithTimeoutTest.kt:42) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest$outerWithTimeout$2.invokeSuspend(StackTraceRecoveryWithTimeoutTest.kt:32) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest.outerWithTimeout(StackTraceRecoveryWithTimeoutTest.kt:31) @@ -7,4 +7,4 @@ kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 200 ms Caused by: kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 200 ms at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:116) at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:86) - at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run(EventLoop.common.kt:492) \ No newline at end of file + at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run(EventLoop.common.kt:492) diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPointWithChild.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPointWithChild.txt index 8ec7691e50..6f21cc6b30 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPointWithChild.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/timeout/testStacktraceIsRecoveredFromSuspensionPointWithChild.txt @@ -1,9 +1,9 @@ kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 200 ms - (Coroutine boundary) + at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest.suspendForever(StackTraceRecoveryWithTimeoutTest.kt:92) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest$outerChild$2.invokeSuspend(StackTraceRecoveryWithTimeoutTest.kt:78) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest.outerChild(StackTraceRecoveryWithTimeoutTest.kt:74) at kotlinx.coroutines.exceptions.StackTraceRecoveryWithTimeoutTest$testStacktraceIsRecoveredFromSuspensionPointWithChild$1.invokeSuspend(StackTraceRecoveryWithTimeoutTest.kt:66) Caused by: kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 200 ms at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:116) - at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:86) \ No newline at end of file + at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:86) diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt index bea18a431e..bc9d056668 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt @@ -12,7 +12,7 @@ class StackTraceRecoveryNestedScopesTest : TestBase() { "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest.failure(StackTraceRecoveryNestedScopesTest.kt:9)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest.access\$failure(StackTraceRecoveryNestedScopesTest.kt:7)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$createFailingAsync\$1.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:12)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$callWithTimeout\$2.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:23)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$callCoroutineScope\$2.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:29)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$$TEST_MACROS\$1.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:36)\n" + @@ -82,7 +82,7 @@ class StackTraceRecoveryNestedScopesTest : TestBase() { "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest.failure(StackTraceRecoveryNestedScopesTest.kt:23)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest.access\$failure(StackTraceRecoveryNestedScopesTest.kt:7)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$createFailingAsync\$1.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:26)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$callWithTimeout\$2.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:37)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$callCoroutineScope\$2.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:43)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$testAwaitNestedScopes\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:68)\n" + diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt index dbbd77c4b7..1a381547ba 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt @@ -35,7 +35,7 @@ class StackTraceRecoveryTest : TestBase() { val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testAsync\$1\$1\$1.invokeSuspend(StackTraceRecoveryTest.kt:99)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.oneMoreNestedMethod(StackTraceRecoveryTest.kt:49)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.nestedMethod(StackTraceRecoveryTest.kt:44)\n" + @@ -58,7 +58,7 @@ class StackTraceRecoveryTest : TestBase() { val stacktrace = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCompletedAsync\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:44)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.oneMoreNestedMethod(StackTraceRecoveryTest.kt:81)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.nestedMethod(StackTraceRecoveryTest.kt:75)\n" + @@ -93,7 +93,7 @@ class StackTraceRecoveryTest : TestBase() { outerMethod(deferred, "kotlinx.coroutines.RecoverableTestException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testWithContext\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:143)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.innerMethod(StackTraceRecoveryTest.kt:158)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$outerMethod\$2.invokeSuspend(StackTraceRecoveryTest.kt:151)\n" + @@ -131,7 +131,7 @@ class StackTraceRecoveryTest : TestBase() { outerScopedMethod(deferred, "kotlinx.coroutines.RecoverableTestException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCoroutineScope\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:143)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.innerMethod(StackTraceRecoveryTest.kt:158)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$outerScopedMethod\$2\$1.invokeSuspend(StackTraceRecoveryTest.kt:193)\n" + @@ -230,7 +230,7 @@ class StackTraceRecoveryTest : TestBase() { verifyStackTrace(e, "kotlinx.coroutines.RecoverableTestException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.throws(StackTraceRecoveryTest.kt:280)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$throws\$1.invokeSuspend(StackTraceRecoveryTest.kt)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.awaiter(StackTraceRecoveryTest.kt:285)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testNonDispatchedRecovery\$await\$1.invokeSuspend(StackTraceRecoveryTest.kt:291)\n" + @@ -248,7 +248,7 @@ class StackTraceRecoveryTest : TestBase() { } catch (e: Throwable) { verifyStackTrace(e, "kotlinx.coroutines.RecoverableTestException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCancellableContinuation\$1.invokeSuspend(StackTraceRecoveryTest.kt:329)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.awaitCallback(StackTraceRecoveryTest.kt:348)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCancellableContinuation\$1\$1.invokeSuspend(StackTraceRecoveryTest.kt:322)\n" + "Caused by: kotlinx.coroutines.RecoverableTestException\n" + diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt b/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt index f79ad4ba74..5d85c9c9f2 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt @@ -33,25 +33,10 @@ public fun toStackTrace(t: Throwable): String { } public fun String.normalizeStackTrace(): String = - applyBackspace() - .replace(Regex(":[0-9]+"), "") // remove line numbers + replace(Regex(":[0-9]+"), "") // remove line numbers .replace("kotlinx_coroutines_core_main", "") // yay source sets .replace("kotlinx_coroutines_core", "") .replace(Regex("@[0-9a-f]+"), "") // remove hex addresses in debug toStrings .lines().joinToString("\n") // normalize line separators -public fun String.applyBackspace(): String { - val array = toCharArray() - val stack = CharArray(array.size) - var stackSize = -1 - for (c in array) { - if (c != '\b') { - stack[++stackSize] = c - } else { - --stackSize - } - } - return String(stack, 0, stackSize) -} - public fun String.count(substring: String): Int = split(substring).size - 1 \ No newline at end of file diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index f048040bba..04dfa8a9fb 100644 --- a/kotlinx-coroutines-debug/README.md +++ b/kotlinx-coroutines-debug/README.md @@ -123,7 +123,7 @@ Coroutine "coroutine#2":DeferredCoroutine{Active}@289d1c02, state: SUSPENDED at ExampleKt.combineResults(Example.kt:11) at ExampleKt$computeValue$2.invokeSuspend(Example.kt:7) at ExampleKt$main$1$deferred$1.invokeSuspend(Example.kt:25) - (Coroutine creation stacktrace) + at _COROUTINE._CREATION._(CoroutineDebugging.kt) at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:25) at kotlinx.coroutines.BuildersKt.async$default(Unknown Source) diff --git a/kotlinx-coroutines-debug/src/DebugProbes.kt b/kotlinx-coroutines-debug/src/DebugProbes.kt index 373864adb8..be5c4557e2 100644 --- a/kotlinx-coroutines-debug/src/DebugProbes.kt +++ b/kotlinx-coroutines-debug/src/DebugProbes.kt @@ -134,7 +134,7 @@ public object DebugProbes { * * Coroutine "coroutine#42":StandaloneCoroutine{Active}@58fdd99, state: SUSPENDED * at MyClass$awaitData.invokeSuspend(MyClass.kt:37) - * (Coroutine creation stacktrace) + * at _COROUTINE._CREATION._(CoroutineDebugging.kt) * at MyClass.createIoRequest(MyClass.kt:142) * at MyClass.fetchData(MyClass.kt:154) * at MyClass.showData(MyClass.kt:31) diff --git a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt index fd0279123f..4fecb83e47 100644 --- a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt +++ b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt @@ -25,7 +25,7 @@ class CoroutinesDumpTest : DebugTestBase() { "Coroutine \"coroutine#1\":DeferredCoroutine{Active}@1e4a7dd4, state: SUSPENDED\n" + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingNestedMethod(CoroutinesDumpTest.kt:95)\n" + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.sleepingOuterMethod(CoroutinesDumpTest.kt:88)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n", @@ -51,7 +51,7 @@ class CoroutinesDumpTest : DebugTestBase() { "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.nestedActiveMethod(CoroutinesDumpTest.kt:141)\n" + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.activeMethod(CoroutinesDumpTest.kt:133)\n" + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest\$testRunningCoroutine\$1$deferred\$1.invokeSuspend(CoroutinesDumpTest.kt:41)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n" + @@ -81,7 +81,7 @@ class CoroutinesDumpTest : DebugTestBase() { "\tat java.lang.Thread.sleep(Native Method)\n" + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.nestedActiveMethod(CoroutinesDumpTest.kt:111)\n" + "\tat kotlinx.coroutines.debug.CoroutinesDumpTest.activeMethod(CoroutinesDumpTest.kt:106)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:99)\n" + diff --git a/kotlinx-coroutines-debug/test/DebugProbesTest.kt b/kotlinx-coroutines-debug/test/DebugProbesTest.kt index 01b2da0006..1acaee2b62 100644 --- a/kotlinx-coroutines-debug/test/DebugProbesTest.kt +++ b/kotlinx-coroutines-debug/test/DebugProbesTest.kt @@ -20,7 +20,7 @@ class DebugProbesTest : DebugTestBase() { val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:14)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:49)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:44)\n" + @@ -41,12 +41,12 @@ class DebugProbesTest : DebugTestBase() { val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:62)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\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" + @@ -73,12 +73,12 @@ class DebugProbesTest : DebugTestBase() { val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:16)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:71)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:66)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithSanitizedProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:87)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.testAsyncWithSanitizedProbes(DebugProbesTest.kt:38)", diff --git a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt index e7fdeede79..8b5724219e 100644 --- a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt +++ b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt @@ -32,7 +32,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$suspendingFunction\$2.invokeSuspend(RunningThreadStackMergeTest.kt:77)\n" + "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.suspendingFunction(RunningThreadStackMergeTest.kt:75)\n" + "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$launchCoroutine\$1.invokeSuspend(RunningThreadStackMergeTest.kt:68)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", ignoredCoroutine = ":BlockingCoroutine" ) { @@ -87,7 +87,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$suspendingFunctionWithContext\$2.invokeSuspend(RunningThreadStackMergeTest.kt:124)\n" + "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.suspendingFunctionWithContext(RunningThreadStackMergeTest.kt:122)\n" + "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$launchEscapingCoroutine\$1.invokeSuspend(RunningThreadStackMergeTest.kt:116)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", ignoredCoroutine = ":BlockingCoroutine" ) { @@ -126,7 +126,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.nonSuspendingFun(RunningThreadStackMergeTest.kt:83)\n" + "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest.suspendingFunctionWithoutContext(RunningThreadStackMergeTest.kt:160)\n" + "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$launchEscapingCoroutineWithoutContext\$1.invokeSuspend(RunningThreadStackMergeTest.kt:153)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", ignoredCoroutine = ":BlockingCoroutine" ) { @@ -158,7 +158,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { "\tat kotlinx.coroutines.debug.StacktraceUtilsKt.verifyDump(StacktraceUtils.kt)\n" + "\tat kotlinx.coroutines.debug.StacktraceUtilsKt.verifyDump\$default(StacktraceUtils.kt)\n" + "\tat kotlinx.coroutines.debug.RunningThreadStackMergeTest\$testRunBlocking\$1.invokeSuspend(RunningThreadStackMergeTest.kt)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt)\n") } diff --git a/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt b/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt index 67a283d00a..a329be7bcd 100644 --- a/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt +++ b/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt @@ -27,12 +27,12 @@ class SanitizedProbesTest : DebugTestBase() { val traces = listOf( "java.util.concurrent.ExecutionException\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest\$createDeferredNested\$1.invokeSuspend(SanitizedProbesTest.kt:97)\n" + - "\t(Coroutine boundary)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.oneMoreNestedMethod(SanitizedProbesTest.kt:67)\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.nestedMethod(SanitizedProbesTest.kt:61)\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest\$testRecoveredStackTrace\$1.invokeSuspend(SanitizedProbesTest.kt:50)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.TestBase.runTest\$default(TestBase.kt:141)\n" + @@ -52,7 +52,7 @@ class SanitizedProbesTest : DebugTestBase() { verifyDump( "Coroutine \"coroutine#3\":BlockingCoroutine{Active}@7d68ef40, state: RUNNING\n" + "\tat java.lang.Thread.getStackTrace(Thread.java)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.TestBase.runTest\$default(TestBase.kt:141)\n" + @@ -60,7 +60,7 @@ class SanitizedProbesTest : DebugTestBase() { "Coroutine \"coroutine#4\":DeferredCoroutine{Active}@75c072cb, state: SUSPENDED\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest\$createActiveDeferred\$1.invokeSuspend(SanitizedProbesTest.kt:63)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:23)\n" + "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.async\$default(Builders.common.kt)\n" + @@ -84,12 +84,12 @@ class SanitizedProbesTest : DebugTestBase() { expect(3) verifyDump("Coroutine \"coroutine#1\":BlockingCoroutine{Active}@35fc6dc4, state: RUNNING\n" + "\tat java.lang.Thread.getStackTrace(Thread.java:1552)\n" + // Skip the rest - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)", "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@1b68b9a4, state: SUSPENDED\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest\$launchSelector\$1\$1\$1.invokeSuspend(SanitizedProbesTest.kt)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n" + "\tat kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:25)\n" + "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.launch\$default(Builders.common.kt)\n" + diff --git a/kotlinx-coroutines-debug/test/ScopedBuildersTest.kt b/kotlinx-coroutines-debug/test/ScopedBuildersTest.kt index c762725569..801b74b1aa 100644 --- a/kotlinx-coroutines-debug/test/ScopedBuildersTest.kt +++ b/kotlinx-coroutines-debug/test/ScopedBuildersTest.kt @@ -17,7 +17,7 @@ class ScopedBuildersTest : DebugTestBase() { yield() verifyDump( "Coroutine \"coroutine#1\":BlockingCoroutine{Active}@16612a51, state: RUNNING\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)\n", "Coroutine \"coroutine#2\":StandaloneCoroutine{Active}@6b53e23f, state: SUSPENDED\n" + @@ -25,7 +25,7 @@ class ScopedBuildersTest : DebugTestBase() { "\tat kotlinx.coroutines.debug.ScopedBuildersTest.doWithContext(ScopedBuildersTest.kt:47)\n" + "\tat kotlinx.coroutines.debug.ScopedBuildersTest\$doInScope\$2.invokeSuspend(ScopedBuildersTest.kt:41)\n" + "\tat kotlinx.coroutines.debug.ScopedBuildersTest\$testNestedScopes\$1\$job\$1.invokeSuspend(ScopedBuildersTest.kt:30)\n" + - "\t(Coroutine creation stacktrace)\n" + + "\tat _COROUTINE._CREATION._(CoroutineDebugging.kt)\n" + "\tat kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)") job.cancelAndJoin() finish(4) diff --git a/kotlinx-coroutines-debug/test/StacktraceUtils.kt b/kotlinx-coroutines-debug/test/StacktraceUtils.kt index 8c591ebd44..64af7e2681 100644 --- a/kotlinx-coroutines-debug/test/StacktraceUtils.kt +++ b/kotlinx-coroutines-debug/test/StacktraceUtils.kt @@ -7,6 +7,9 @@ package kotlinx.coroutines.debug import java.io.* import kotlin.test.* +private val coroutineCreationFrameRegex = + Regex("\n\tat _COROUTINE._CREATION._[^\n]*\n") + public fun String.trimStackTrace(): String = trimIndent() .replace(Regex(":[0-9]+"), "") @@ -14,22 +17,6 @@ public fun String.trimStackTrace(): String = .replace(Regex("(?<=\tat )[^\n]*/"), "") .replace(Regex("\t"), "") .replace("sun.misc.Unsafe.", "jdk.internal.misc.Unsafe.") // JDK8->JDK11 - .applyBackspace() - -public fun String.applyBackspace(): String { - val array = toCharArray() - val stack = CharArray(array.size) - var stackSize = -1 - for (c in array) { - if (c != '\b') { - stack[++stackSize] = c - } else { - --stackSize - } - } - - return String(stack, 0, stackSize + 1) -} public fun verifyStackTrace(e: Throwable, traces: List) { val stacktrace = toStackTrace(e) @@ -74,7 +61,7 @@ public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null, f * `$$BlockHound$$_` prepended at the last component. */ private fun cleanBlockHoundTraces(frames: List): List { - var result = mutableListOf() + val result = mutableListOf() val blockHoundSubstr = "\$\$BlockHound\$\$_" var i = 0 while (i < frames.size) { @@ -97,21 +84,21 @@ public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null) { assertTrue(filtered[0].startsWith("Coroutines dump")) return } - // Drop "Coroutine dump" line - trace.withIndex().drop(1).forEach { (index, value) -> + // The first line, "Coroutine dump", is dropped. This is not `zip` because not having enough dumps is an error. + trace.drop(1).withIndex().forEach { (index, value) -> if (ignoredCoroutine != null && value.contains(ignoredCoroutine)) { return@forEach } - val expected = traces[index - 1].applyBackspace().split("\n\t(Coroutine creation stacktrace)\n", limit = 2) - val actual = value.applyBackspace().split("\n\t(Coroutine creation stacktrace)\n", limit = 2) + val expected = traces[index].split(coroutineCreationFrameRegex, limit = 2) + val actual = value.split(coroutineCreationFrameRegex, limit = 2) assertEquals(expected.size, actual.size, "Creation stacktrace should be part of the expected input") - expected.withIndex().forEach { (index, trace) -> - val actualTrace = actual[index].trimStackTrace().sanitizeAddresses() - val expectedTrace = trace.trimStackTrace().sanitizeAddresses() - val actualLines = cleanBlockHoundTraces(actualTrace.split("\n")) - val expectedLines = expectedTrace.split("\n") + actual.zip(expected).forEach { (actualTrace, expectedTrace) -> + val sanitizedActualTrace = actualTrace.trimStackTrace().sanitizeAddresses() + val sanitizedExpectedTrace = expectedTrace.trimStackTrace().sanitizeAddresses() + val actualLines = cleanBlockHoundTraces(sanitizedActualTrace.split("\n")) + val expectedLines = sanitizedExpectedTrace.split("\n") for (i in expectedLines.indices) { assertEquals(expectedLines[i], actualLines[i]) } diff --git a/kotlinx-coroutines-debug/test/junit4/CoroutinesTimeoutDisabledTracesTest.kt b/kotlinx-coroutines-debug/test/junit4/CoroutinesTimeoutDisabledTracesTest.kt index 886007c3e8..d6a42be528 100644 --- a/kotlinx-coroutines-debug/test/junit4/CoroutinesTimeoutDisabledTracesTest.kt +++ b/kotlinx-coroutines-debug/test/junit4/CoroutinesTimeoutDisabledTracesTest.kt @@ -22,7 +22,7 @@ class CoroutinesTimeoutDisabledTracesTest : TestBase() { "at kotlinx.coroutines.debug.junit4.CoroutinesTimeoutDisabledTracesTest.hangForever", "at kotlinx.coroutines.debug.junit4.CoroutinesTimeoutDisabledTracesTest.waitForHangJob" ), - notExpectedOutParts = listOf("Coroutine creation stacktrace"), + notExpectedOutParts = listOf("coroutineCreation"), error = TestTimedOutException::class.java ) ) From 4c90fd4a31dcb84d2c49a8d450c98d1bdc724e18 Mon Sep 17 00:00:00 2001 From: LepilkinaElena Date: Thu, 8 Apr 2021 12:11:20 +0300 Subject: [PATCH 25/55] Fix minBy/maxBy usages (#2636) --- kotlinx-coroutines-core/common/test/flow/VirtualTime.kt | 2 +- kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt b/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt index ddb1d88ae2..b2d957be46 100644 --- a/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt +++ b/kotlinx-coroutines-core/common/test/flow/VirtualTime.kt @@ -26,7 +26,7 @@ internal class VirtualTimeDispatcher(enclosingScope: CoroutineScope) : Coroutine ?: 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") - val nextTask = heap.minBy { it.deadline } ?: return@launch + val nextTask = heap.minByOrNull { it.deadline } ?: return@launch heap.remove(nextTask) currentTime = nextTask.deadline nextTask.run() diff --git a/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt b/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt index af1eee42c4..9953756f70 100644 --- a/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt +++ b/kotlinx-coroutines-test/src/internal/MainTestDispatcher.kt @@ -64,7 +64,7 @@ internal class TestMainDispatcherFactory : MainDispatcherFactory { override fun createDispatcher(allFactories: List): MainCoroutineDispatcher { val originalFactory = allFactories.asSequence() .filter { it !== this } - .maxBy { it.loadPriority } ?: MissingMainCoroutineDispatcherFactory + .maxByOrNull { it.loadPriority } ?: MissingMainCoroutineDispatcherFactory return TestMainDispatcher(originalFactory) } From 43df03282fed38f3dcdbb3644032059000afe691 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 9 Dec 2020 18:02:52 +0300 Subject: [PATCH 26/55] Hide obsolete Channels API in a backwards-compatible manner * Remove all deprecated inline methods * Move the rest to the Deprecated.kt * Apply Deprecated.HIDDEN where possible, otherwise use internal + published API --- .../api/kotlinx-coroutines-core.api | 120 +- .../common/src/channels/Channels.common.kt | 2080 +---------------- .../common/src/channels/Deprecated.kt | 406 ++++ 3 files changed, 452 insertions(+), 2154 deletions(-) create mode 100644 kotlinx-coroutines-core/common/src/channels/Deprecated.kt diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 611ebd8650..f279d5509a 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -663,124 +663,70 @@ public final class kotlinx/coroutines/channels/ChannelResult$Companion { } public final class kotlinx/coroutines/channels/ChannelsKt { - public static final fun all (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun associate (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun associateBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun associateBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun associateByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun associateByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun associateTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun any (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun cancelConsumed (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;)V public static final fun consume (Lkotlinx/coroutines/channels/BroadcastChannel;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun consume (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun consumeEach (Lkotlinx/coroutines/channels/BroadcastChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun consumeEach (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun consumeEachIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun consumes (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlin/jvm/functions/Function1; public static final fun consumesAll ([Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlin/jvm/functions/Function1; - public static final fun count (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun count (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun distinct (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun count (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun distinct (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun distinctBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static synthetic fun distinctBy$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun drop (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun drop (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun drop$default (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun dropWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun dropWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun dropWhile$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun elementAt (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun elementAtOrElse (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun elementAtOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun elementAt (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun elementAtOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun filter (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static synthetic fun filter$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun filterIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun filterIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun filterIndexed$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun filterIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun filterIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun filterNot (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun filterNot (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun filterNot$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun filterNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun filterNotTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun filterNotTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun filterTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun filterTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun find (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun findLast (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun first (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun first (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun firstOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun firstOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun flatMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun filterNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun first (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun firstOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun flatMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun flatMap$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun fold (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun foldIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun groupBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun groupBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun groupByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun groupByTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun indexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun indexOfFirst (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun indexOfLast (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun last (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun last (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun lastIndexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun lastOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun lastOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun indexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun last (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun lastIndexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun lastOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun map (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static synthetic fun map$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun mapIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static synthetic fun mapIndexed$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun mapIndexedNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun mapIndexedNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun mapIndexedNotNull$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun mapIndexedNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun mapIndexedNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun mapIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun mapIndexedTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun mapNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun mapNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun mapNotNull$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun mapNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun mapNotNullTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun mapTo (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun mapTo (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun maxBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun maxWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun minBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun minWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun none (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun none (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun maxWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun minWith (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Comparator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun none (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun onReceiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/selects/SelectClause1; - public static final fun partition (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun receiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun reduce (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun reduceIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun requireNoNulls (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun requireNoNulls (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun sendBlocking (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Object;)V - public static final fun single (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun single (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun singleOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun singleOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun sumBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun sumByDouble (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun take (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun single (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun singleOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun take (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun take$default (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun takeWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun takeWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun takeWhile$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun toChannel (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/SendChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun toCollection (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Collection;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun toList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/Map;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun toMutableList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun toMap (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun toMutableList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun toMutableSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun toSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun withIndex (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun toSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final synthetic fun withIndex (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun withIndex$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static final fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final synthetic fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; - public static synthetic fun zip$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; } public final class kotlinx/coroutines/channels/ClosedReceiveChannelException : java/util/NoSuchElementException { diff --git a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt index 6bf6f88123..e0b4f9d2a5 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channels.common.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channels.common.kt @@ -70,89 +70,6 @@ public fun ReceiveChannel.onReceiveOrNull(): SelectClause1 { return (this as ReceiveChannel).onReceiveOrNull } -/** - * Subscribes to this [BroadcastChannel] and performs the specified action for each received element. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@ObsoleteCoroutinesApi -public suspend inline fun BroadcastChannel.consumeEach(action: (E) -> Unit): Unit = - consume { - for (element in this) action(element) - } - -// -------- Operations on ReceiveChannel -------- - -/** - * Returns a [List] containing all elements. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - */ -@OptIn(ExperimentalStdlibApi::class) -public suspend fun ReceiveChannel.toList(): List = buildList { - consumeEach { - add(it) - } -} - -/** - * Returns a [CompletionHandler] that invokes [cancel][ReceiveChannel.cancel] on the [ReceiveChannel] - * with the corresponding cause. See also [ReceiveChannel.consume]. - * - * **WARNING**: It is planned that in the future a second invocation of this method - * on an channel that is already being consumed is going to fail fast, that it - * immediately throws an [IllegalStateException]. - * See [this issue](https://github.com/Kotlin/kotlinx.coroutines/issues/167) - * for details. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel<*>.consumes(): CompletionHandler = { cause: Throwable? -> - cancelConsumed(cause) -} - -@PublishedApi -internal fun ReceiveChannel<*>.cancelConsumed(cause: Throwable?) { - cancel(cause?.let { - it as? CancellationException ?: CancellationException("Channel was consumed, consumer had failed", it) - }) -} - -/** - * Returns a [CompletionHandler] that invokes [cancel][ReceiveChannel.cancel] on all the - * specified [ReceiveChannel] instances with the corresponding cause. - * See also [ReceiveChannel.consumes()] for a version on one channel. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun consumesAll(vararg channels: ReceiveChannel<*>): CompletionHandler = - { cause: Throwable? -> - var exception: Throwable? = null - for (channel in channels) - try { - channel.cancelConsumed(cause) - } catch (e: Throwable) { - if (exception == null) { - exception = e - } else { - exception.addSuppressedThrowable(e) - } - } - exception?.let { throw it } - } - /** * Makes sure that the given [block] consumes all elements from the given channel * by always invoking [cancel][ReceiveChannel.cancel] after the execution of the block. @@ -188,2006 +105,35 @@ public suspend inline fun ReceiveChannel.consumeEach(action: (E) -> Unit) } /** - * Performs the given [action] for each received element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.consumeEachIndexed(action: (IndexedValue) -> Unit) { - var index = 0 - consumeEach { - action(IndexedValue(index++, it)) - } -} - -/** - * Returns an element at the given [index] or throws an [IndexOutOfBoundsException] if the [index] is out of bounds of this channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.elementAt(index: Int): E = - elementAtOrElse(index) { throw IndexOutOfBoundsException("ReceiveChannel doesn't contain element at index $index.") } - -/** - * Returns an element at the given [index] or the result of calling the [defaultValue] function if the [index] is out of bounds of this channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.elementAtOrElse(index: Int, defaultValue: (Int) -> E): E = - consume { - if (index < 0) - return defaultValue(index) - var count = 0 - for (element in this) { - if (index == count++) - return element - } - return defaultValue(index) - } - -/** - * Returns an element at the given [index] or `null` if the [index] is out of bounds of this channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.elementAtOrNull(index: Int): E? = - consume { - if (index < 0) - return null - var count = 0 - for (element in this) { - if (index == count++) - return element - } - return null - } - -/** - * Returns the first element matching the given [predicate], or `null` if no such element was found. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.find(predicate: (E) -> Boolean): E? = - firstOrNull(predicate) - -/** - * Returns the last element matching the given [predicate], or `null` if no such element was found. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.findLast(predicate: (E) -> Boolean): E? = - lastOrNull(predicate) - -/** - * Returns first element. - * @throws [NoSuchElementException] if the channel is empty. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.first(): E = - consume { - val iterator = iterator() - if (!iterator.hasNext()) - throw NoSuchElementException("ReceiveChannel is empty.") - return iterator.next() - } - -/** - * Returns the first element matching the given [predicate]. - * @throws [NoSuchElementException] if no such element is found. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.first(predicate: (E) -> Boolean): E { - consumeEach { - if (predicate(it)) return it - } - throw NoSuchElementException("ReceiveChannel contains no element matching the predicate.") -} - -/** - * Returns the first element, or `null` if the channel is empty. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.firstOrNull(): E? = - consume { - val iterator = iterator() - if (!iterator.hasNext()) - return null - return iterator.next() - } - -/** - * Returns the first element matching the given [predicate], or `null` if element was not found. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.firstOrNull(predicate: (E) -> Boolean): E? { - consumeEach { - if (predicate(it)) return it - } - return null -} - -/** - * Returns first index of [element], or -1 if the channel does not contain element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.indexOf(element: E): Int { - var index = 0 - consumeEach { - if (element == it) - return index - index++ - } - return -1 -} - -/** - * Returns index of the first element matching the given [predicate], or -1 if the channel does not contain such element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.indexOfFirst(predicate: (E) -> Boolean): Int { - var index = 0 - consumeEach { - if (predicate(it)) - return index - index++ - } - return -1 -} - -/** - * Returns index of the last element matching the given [predicate], or -1 if the channel does not contain such element. + * Returns a [List] containing all elements. * * The operation is _terminal_. * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.indexOfLast(predicate: (E) -> Boolean): Int { - var lastIndex = -1 - var index = 0 +@OptIn(ExperimentalStdlibApi::class) +public suspend fun ReceiveChannel.toList(): List = buildList { consumeEach { - if (predicate(it)) - lastIndex = index - index++ + add(it) } - return lastIndex } /** - * Returns the last element. - * @throws [NoSuchElementException] if the channel is empty. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. + * Subscribes to this [BroadcastChannel] and performs the specified action for each received element. * * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.last(): E = +@ObsoleteCoroutinesApi +public suspend inline fun BroadcastChannel.consumeEach(action: (E) -> Unit): Unit = consume { - val iterator = iterator() - if (!iterator.hasNext()) - throw NoSuchElementException("ReceiveChannel is empty.") - var last = iterator.next() - while (iterator.hasNext()) - last = iterator.next() - return last + for (element in this) action(element) } -/** - * Returns the last element matching the given [predicate]. - * @throws [NoSuchElementException] if no such element is found. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.last(predicate: (E) -> Boolean): E { - var last: E? = null - var found = false - consumeEach { - if (predicate(it)) { - last = it - found = true - } - } - if (!found) throw NoSuchElementException("ReceiveChannel contains no element matching the predicate.") - @Suppress("UNCHECKED_CAST") - return last as E -} -/** - * Returns last index of [element], or -1 if the channel does not contain element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.lastIndexOf(element: E): Int { - var lastIndex = -1 - var index = 0 - consumeEach { - if (element == it) - lastIndex = index - index++ - } - return lastIndex +@PublishedApi +internal fun ReceiveChannel<*>.cancelConsumed(cause: Throwable?) { + cancel(cause?.let { + it as? CancellationException ?: CancellationException("Channel was consumed, consumer had failed", it) + }) } -/** - * Returns the last element, or `null` if the channel is empty. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.lastOrNull(): E? = - consume { - val iterator = iterator() - if (!iterator.hasNext()) - return null - var last = iterator.next() - while (iterator.hasNext()) - last = iterator.next() - return last - } - -/** - * Returns the last element matching the given [predicate], or `null` if no such element was found. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.lastOrNull(predicate: (E) -> Boolean): E? { - var last: E? = null - consumeEach { - if (predicate(it)) { - last = it - } - } - return last -} - -/** - * Returns the single element, or throws an exception if the channel is empty or has more than one element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.single(): E = - consume { - val iterator = iterator() - if (!iterator.hasNext()) - throw NoSuchElementException("ReceiveChannel is empty.") - val single = iterator.next() - if (iterator.hasNext()) - throw IllegalArgumentException("ReceiveChannel has more than one element.") - return single - } - -/** - * Returns the single element matching the given [predicate], or throws exception if there is no or more than one matching element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.single(predicate: (E) -> Boolean): E { - var single: E? = null - var found = false - consumeEach { - if (predicate(it)) { - if (found) throw IllegalArgumentException("ReceiveChannel contains more than one matching element.") - single = it - found = true - } - } - if (!found) throw NoSuchElementException("ReceiveChannel contains no element matching the predicate.") - @Suppress("UNCHECKED_CAST") - return single as E -} - -/** - * Returns single element, or `null` if the channel is empty or has more than one element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.singleOrNull(): E? = - consume { - val iterator = iterator() - if (!iterator.hasNext()) - return null - val single = iterator.next() - if (iterator.hasNext()) - return null - return single - } - -/** - * Returns the single element matching the given [predicate], or `null` if element was not found or more than one element was found. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.singleOrNull(predicate: (E) -> Boolean): E? { - var single: E? = null - var found = false - consumeEach { - if (predicate(it)) { - if (found) return null - single = it - found = true - } - } - if (!found) return null - return single -} - -/** - * Returns a channel containing all elements except first [n] elements. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.drop(n: Int, context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - require(n >= 0) { "Requested element count $n is less than zero." } - var remaining: Int = n - if (remaining > 0) - for (e in this@drop) { - remaining-- - if (remaining == 0) - break - } - for (e in this@drop) { - send(e) - } - } - -/** - * Returns a channel containing all elements except first elements that satisfy the given [predicate]. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.dropWhile(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - for (e in this@dropWhile) { - if (!predicate(e)) { - send(e) - break - } - } - for (e in this@dropWhile) { - send(e) - } - } - -/** - * Returns a channel containing only elements matching the given [predicate]. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.filter(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - for (e in this@filter) { - if (predicate(e)) send(e) - } - } - -/** - * Returns a channel containing only elements matching the given [predicate]. - * @param [predicate] function that takes the index of an element and the element itself - * and returns the result of predicate evaluation on the element. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.filterIndexed(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (index: Int, E) -> Boolean): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - var index = 0 - for (e in this@filterIndexed) { - if (predicate(index++, e)) send(e) - } - } - -/** - * Appends all elements matching the given [predicate] to the given [destination]. - * @param [predicate] function that takes the index of an element and the element itself - * and returns the result of predicate evaluation on the element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.filterIndexedTo(destination: C, predicate: (index: Int, E) -> Boolean): C { - consumeEachIndexed { (index, element) -> - if (predicate(index, element)) destination.add(element) - } - return destination -} - -/** - * Appends all elements matching the given [predicate] to the given [destination]. - * @param [predicate] function that takes the index of an element and the element itself - * and returns the result of predicate evaluation on the element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.filterIndexedTo(destination: C, predicate: (index: Int, E) -> Boolean): C { - consumeEachIndexed { (index, element) -> - if (predicate(index, element)) destination.send(element) - } - return destination -} - -/** - * Returns a channel containing all elements not matching the given [predicate]. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.filterNot(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = - filter(context) { !predicate(it) } - -/** - * Returns a channel containing all elements that are not `null`. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -@Suppress("UNCHECKED_CAST") -public fun ReceiveChannel.filterNotNull(): ReceiveChannel = - filter { it != null } as ReceiveChannel - -/** - * Appends all elements that are not `null` to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun > ReceiveChannel.filterNotNullTo(destination: C): C { - consumeEach { - if (it != null) destination.add(it) - } - return destination -} - -/** - * Appends all elements that are not `null` to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun > ReceiveChannel.filterNotNullTo(destination: C): C { - consumeEach { - if (it != null) destination.send(it) - } - return destination -} - -/** - * Appends all elements not matching the given [predicate] to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.filterNotTo(destination: C, predicate: (E) -> Boolean): C { - consumeEach { - if (!predicate(it)) destination.add(it) - } - return destination -} - -/** - * Appends all elements not matching the given [predicate] to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.filterNotTo(destination: C, predicate: (E) -> Boolean): C { - consumeEach { - if (!predicate(it)) destination.send(it) - } - return destination -} - -/** - * Appends all elements matching the given [predicate] to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.filterTo(destination: C, predicate: (E) -> Boolean): C { - consumeEach { - if (predicate(it)) destination.add(it) - } - return destination -} - -/** - * Appends all elements matching the given [predicate] to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.filterTo(destination: C, predicate: (E) -> Boolean): C { - consumeEach { - if (predicate(it)) destination.send(it) - } - return destination -} - -/** - * Returns a channel containing first [n] elements. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.take(n: Int, context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - if (n == 0) return@produce - require(n >= 0) { "Requested element count $n is less than zero." } - var remaining: Int = n - for (e in this@take) { - send(e) - remaining-- - if (remaining == 0) - return@produce - } - } - -/** - * Returns a channel containing first elements satisfying the given [predicate]. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.takeWhile(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - for (e in this@takeWhile) { - if (!predicate(e)) return@produce - send(e) - } - } - -/** - * Returns a [Map] containing key-value pairs provided by [transform] function - * applied to elements of the given channel. - * - * If any of two pairs would have the same key the last one gets added to the map. - * - * The returned map preserves the entry iteration order of the original channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.associate(transform: (E) -> Pair): Map = - associateTo(LinkedHashMap(), transform) - -/** - * Returns a [Map] containing the elements from the given channel indexed by the key - * returned from [keySelector] function applied to each element. - * - * If any two elements would have the same key returned by [keySelector] the last one gets added to the map. - * - * The returned map preserves the entry iteration order of the original channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.associateBy(keySelector: (E) -> K): Map = - associateByTo(LinkedHashMap(), keySelector) - -/** - * Returns a [Map] containing the values provided by [valueTransform] and indexed by [keySelector] functions applied to elements of the given channel. - * - * If any two elements would have the same key returned by [keySelector] the last one gets added to the map. - * - * The returned map preserves the entry iteration order of the original channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.associateBy(keySelector: (E) -> K, valueTransform: (E) -> V): Map = - associateByTo(LinkedHashMap(), keySelector, valueTransform) - -/** - * Populates and returns the [destination] mutable map with key-value pairs, - * where key is provided by the [keySelector] function applied to each element of the given channel - * and value is the element itself. - * - * If any two elements would have the same key returned by [keySelector] the last one gets added to the map. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.associateByTo(destination: M, keySelector: (E) -> K): M { - consumeEach { - destination.put(keySelector(it), it) - } - return destination -} - -/** - * Populates and returns the [destination] mutable map with key-value pairs, - * where key is provided by the [keySelector] function and - * and value is provided by the [valueTransform] function applied to elements of the given channel. - * - * If any two elements would have the same key returned by [keySelector] the last one gets added to the map. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.associateByTo(destination: M, keySelector: (E) -> K, valueTransform: (E) -> V): M { - consumeEach { - destination.put(keySelector(it), valueTransform(it)) - } - return destination -} - -/** - * Populates and returns the [destination] mutable map with key-value pairs - * provided by [transform] function applied to each element of the given channel. - * - * If any of two pairs would have the same key the last one gets added to the map. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.associateTo(destination: M, transform: (E) -> Pair): M { - consumeEach { - destination += transform(it) - } - return destination -} - -/** - * Send each element of the original channel - * and appends the results to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun > ReceiveChannel.toChannel(destination: C): C { - consumeEach { - destination.send(it) - } - return destination -} - -/** - * Appends all elements to the given [destination] collection. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun > ReceiveChannel.toCollection(destination: C): C { - consumeEach { - destination.add(it) - } - return destination -} - -/** - * Returns a [Map] filled with all elements of this channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel>.toMap(): Map = - toMap(LinkedHashMap()) - -/** - * Returns a [MutableMap] filled with all elements of this channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun > ReceiveChannel>.toMap(destination: M): M { - consumeEach { - destination += it - } - return destination -} - -/** - * Returns a [MutableList] filled with all elements of this channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.toMutableList(): MutableList = - toCollection(ArrayList()) - -/** - * Returns a [Set] of all elements. - * - * The returned set preserves the element iteration order of the original channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.toSet(): Set = - this.toMutableSet() - -/** - * Returns a single channel of all elements from results of [transform] function being invoked on each element of original channel. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.flatMap(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> ReceiveChannel): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - for (e in this@flatMap) { - transform(e).toChannel(this) - } - } - -/** - * Groups elements of the original channel by the key returned by the given [keySelector] function - * applied to each element and returns a map where each group key is associated with a list of corresponding elements. - * - * The returned map preserves the entry iteration order of the keys produced from the original channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.groupBy(keySelector: (E) -> K): Map> = - groupByTo(LinkedHashMap(), keySelector) - -/** - * Groups values returned by the [valueTransform] function applied to each element of the original channel - * by the key returned by the given [keySelector] function applied to the element - * and returns a map where each group key is associated with a list of corresponding values. - * - * The returned map preserves the entry iteration order of the keys produced from the original channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.groupBy(keySelector: (E) -> K, valueTransform: (E) -> V): Map> = - groupByTo(LinkedHashMap(), keySelector, valueTransform) - -/** - * Groups elements of the original channel by the key returned by the given [keySelector] function - * applied to each element and puts to the [destination] map each group key associated with a list of corresponding elements. - * - * @return The [destination] map. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun >> ReceiveChannel.groupByTo(destination: M, keySelector: (E) -> K): M { - consumeEach { - val key = keySelector(it) - val list = destination.getOrPut(key) { ArrayList() } - list.add(it) - } - return destination -} - -/** - * Groups values returned by the [valueTransform] function applied to each element of the original channel - * by the key returned by the given [keySelector] function applied to the element - * and puts to the [destination] map each group key associated with a list of corresponding values. - * - * @return The [destination] map. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun >> ReceiveChannel.groupByTo(destination: M, keySelector: (E) -> K, valueTransform: (E) -> V): M { - consumeEach { - val key = keySelector(it) - val list = destination.getOrPut(key) { ArrayList() } - list.add(valueTransform(it)) - } - return destination -} - -/** - * Returns a channel containing the results of applying the given [transform] function - * to each element in the original channel. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.map(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> R): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - consumeEach { - send(transform(it)) - } - } - -/** - * Returns a channel containing the results of applying the given [transform] function - * to each element and its index in the original channel. - * @param [transform] function that takes the index of an element and the element itself - * and returns the result of the transform applied to the element. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.mapIndexed(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (index: Int, E) -> R): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - var index = 0 - for (e in this@mapIndexed) { - send(transform(index++, e)) - } - } - -/** - * Returns a channel containing only the non-null results of applying the given [transform] function - * to each element and its index in the original channel. - * @param [transform] function that takes the index of an element and the element itself - * and returns the result of the transform applied to the element. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.mapIndexedNotNull(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (index: Int, E) -> R?): ReceiveChannel = - mapIndexed(context, transform).filterNotNull() - -/** - * Applies the given [transform] function to each element and its index in the original channel - * and appends only the non-null results to the given [destination]. - * @param [transform] function that takes the index of an element and the element itself - * and returns the result of the transform applied to the element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapIndexedNotNullTo(destination: C, transform: (index: Int, E) -> R?): C { - consumeEachIndexed { (index, element) -> - transform(index, element)?.let { destination.add(it) } - } - return destination -} - -/** - * Applies the given [transform] function to each element and its index in the original channel - * and appends only the non-null results to the given [destination]. - * @param [transform] function that takes the index of an element and the element itself - * and returns the result of the transform applied to the element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapIndexedNotNullTo(destination: C, transform: (index: Int, E) -> R?): C { - consumeEachIndexed { (index, element) -> - transform(index, element)?.let { destination.send(it) } - } - return destination -} - -/** - * Applies the given [transform] function to each element and its index in the original channel - * and appends the results to the given [destination]. - * @param [transform] function that takes the index of an element and the element itself - * and returns the result of the transform applied to the element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapIndexedTo(destination: C, transform: (index: Int, E) -> R): C { - var index = 0 - consumeEach { - destination.add(transform(index++, it)) - } - return destination -} - -/** - * Applies the given [transform] function to each element and its index in the original channel - * and appends the results to the given [destination]. - * @param [transform] function that takes the index of an element and the element itself - * and returns the result of the transform applied to the element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapIndexedTo(destination: C, transform: (index: Int, E) -> R): C { - var index = 0 - consumeEach { - destination.send(transform(index++, it)) - } - return destination -} - -/** - * Returns a channel containing only the non-null results of applying the given [transform] function - * to each element in the original channel. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.mapNotNull(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> R?): ReceiveChannel = - map(context, transform).filterNotNull() - -/** - * Applies the given [transform] function to each element in the original channel - * and appends only the non-null results to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapNotNullTo(destination: C, transform: (E) -> R?): C { - consumeEach { - transform(it)?.let { destination.add(it) } - } - return destination -} - -/** - * Applies the given [transform] function to each element in the original channel - * and appends only the non-null results to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapNotNullTo(destination: C, transform: (E) -> R?): C { - consumeEach { - transform(it)?.let { destination.send(it) } - } - return destination -} - -/** - * Applies the given [transform] function to each element of the original channel - * and appends the results to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapTo(destination: C, transform: (E) -> R): C { - consumeEach { - destination.add(transform(it)) - } - return destination -} - -/** - * Applies the given [transform] function to each element of the original channel - * and appends the results to the given [destination]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.mapTo(destination: C, transform: (E) -> R): C { - consumeEach { - destination.send(transform(it)) - } - return destination -} - -/** - * Returns a channel of [IndexedValue] for each element of the original channel. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.withIndex(context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel> = - GlobalScope.produce(context, onCompletion = consumes()) { - var index = 0 - for (e in this@withIndex) { - send(IndexedValue(index++, e)) - } - } - -/** - * Returns a channel containing only distinct elements from the given channel. - * - * The elements in the resulting channel are in the same order as they were in the source channel. - * - * The operation is _intermediate_ and _stateful_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.distinct(): ReceiveChannel = - this.distinctBy { it } - -/** - * Returns a channel containing only elements from the given channel - * having distinct keys returned by the given [selector] function. - * - * The elements in the resulting channel are in the same order as they were in the source channel. - * - * The operation is _intermediate_ and _stateful_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.distinctBy(context: CoroutineContext = Dispatchers.Unconfined, selector: suspend (E) -> K): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumes()) { - val keys = HashSet() - for (e in this@distinctBy) { - val k = selector(e) - if (k !in keys) { - send(e) - keys += k - } - } - } - -/** - * Returns a mutable set containing all distinct elements from the given channel. - * - * The returned set preserves the element iteration order of the original channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.toMutableSet(): MutableSet = - toCollection(LinkedHashSet()) - -/** - * Returns `true` if all elements match the given [predicate]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.all(predicate: (E) -> Boolean): Boolean { - consumeEach { - if (!predicate(it)) return false - } - return true -} - -/** - * Returns `true` if channel has at least one element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.any(): Boolean = - consume { - return iterator().hasNext() - } - -/** - * Returns `true` if at least one element matches the given [predicate]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.any(predicate: (E) -> Boolean): Boolean { - consumeEach { - if (predicate(it)) return true - } - return false -} - -/** - * Returns the number of elements in this channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.count(): Int { - var count = 0 - consumeEach { count++ } - return count -} - -/** - * Returns the number of elements matching the given [predicate]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.count(predicate: (E) -> Boolean): Int { - var count = 0 - consumeEach { - if (predicate(it)) count++ - } - return count -} - -/** - * Accumulates value starting with [initial] value and applying [operation] from left to right to current accumulator value and each element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.fold(initial: R, operation: (acc: R, E) -> R): R { - var accumulator = initial - consumeEach { - accumulator = operation(accumulator, it) - } - return accumulator -} - -/** - * Accumulates value starting with [initial] value and applying [operation] from left to right - * to current accumulator value and each element with its index in the original channel. - * @param [operation] function that takes the index of an element, current accumulator value - * and the element itself, and calculates the next accumulator value. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.foldIndexed(initial: R, operation: (index: Int, acc: R, E) -> R): R { - var index = 0 - var accumulator = initial - consumeEach { - accumulator = operation(index++, accumulator, it) - } - return accumulator -} - -/** - * Returns the first element yielding the largest value of the given function or `null` if there are no elements. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.maxBy(selector: (E) -> R): E? = - consume { - val iterator = iterator() - if (!iterator.hasNext()) return null - var maxElem = iterator.next() - var maxValue = selector(maxElem) - while (iterator.hasNext()) { - val e = iterator.next() - val v = selector(e) - if (maxValue < v) { - maxElem = e - maxValue = v - } - } - return maxElem - } - -/** - * Returns the first element having the largest value according to the provided [comparator] or `null` if there are no elements. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.maxWith(comparator: Comparator): E? = - consume { - val iterator = iterator() - if (!iterator.hasNext()) return null - var max = iterator.next() - while (iterator.hasNext()) { - val e = iterator.next() - if (comparator.compare(max, e) < 0) max = e - } - return max - } - -/** - * Returns the first element yielding the smallest value of the given function or `null` if there are no elements. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun > ReceiveChannel.minBy(selector: (E) -> R): E? = - consume { - val iterator = iterator() - if (!iterator.hasNext()) return null - var minElem = iterator.next() - var minValue = selector(minElem) - while (iterator.hasNext()) { - val e = iterator.next() - val v = selector(e) - if (minValue > v) { - minElem = e - minValue = v - } - } - return minElem - } - -/** - * Returns the first element having the smallest value according to the provided [comparator] or `null` if there are no elements. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.minWith(comparator: Comparator): E? = - consume { - val iterator = iterator() - if (!iterator.hasNext()) return null - var min = iterator.next() - while (iterator.hasNext()) { - val e = iterator.next() - if (comparator.compare(min, e) > 0) min = e - } - return min - } - -/** - * Returns `true` if the channel has no elements. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend fun ReceiveChannel.none(): Boolean = - consume { - return !iterator().hasNext() - } - -/** - * Returns `true` if no elements match the given [predicate]. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.none(predicate: (E) -> Boolean): Boolean { - consumeEach { - if (predicate(it)) return false - } - return true -} - -/** - * Accumulates value starting with the first element and applying [operation] from left to right to current accumulator value and each element. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.reduce(operation: (acc: S, E) -> S): S = - consume { - val iterator = this.iterator() - if (!iterator.hasNext()) throw UnsupportedOperationException("Empty channel can't be reduced.") - var accumulator: S = iterator.next() - while (iterator.hasNext()) { - accumulator = operation(accumulator, iterator.next()) - } - return accumulator - } - -/** - * Accumulates value starting with the first element and applying [operation] from left to right - * to current accumulator value and each element with its index in the original channel. - * @param [operation] function that takes the index of an element, current accumulator value - * and the element itself and calculates the next accumulator value. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.reduceIndexed(operation: (index: Int, acc: S, E) -> S): S = - consume { - val iterator = this.iterator() - if (!iterator.hasNext()) throw UnsupportedOperationException("Empty channel can't be reduced.") - var index = 1 - var accumulator: S = iterator.next() - while (iterator.hasNext()) { - accumulator = operation(index++, accumulator, iterator.next()) - } - return accumulator - } - -/** - * Returns the sum of all values produced by [selector] function applied to each element in the channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.sumBy(selector: (E) -> Int): Int { - var sum = 0 - consumeEach { - sum += selector(it) - } - return sum -} - -/** - * Returns the sum of all values produced by [selector] function applied to each element in the channel. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.sumByDouble(selector: (E) -> Double): Double { - var sum = 0.0 - consumeEach { - sum += selector(it) - } - return sum -} - -/** - * Returns an original collection containing all the non-`null` elements, throwing an [IllegalArgumentException] if there are any `null` elements. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.requireNoNulls(): ReceiveChannel = - map { it ?: throw IllegalArgumentException("null element found in $this.") } - -/** - * Splits the original channel into pair of lists, - * where *first* list contains elements for which [predicate] yielded `true`, - * while *second* list contains elements for which [predicate] yielded `false`. - * - * The operation is _terminal_. - * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel]. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public suspend inline fun ReceiveChannel.partition(predicate: (E) -> Boolean): Pair, List> { - val first = ArrayList() - val second = ArrayList() - consumeEach { - if (predicate(it)) { - first.add(it) - } else { - second.add(it) - } - } - return Pair(first, second) -} - -/** - * Returns a channel of pairs built from elements of both channels with same indexes. - * Resulting channel has length of shortest input channel. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][ReceiveChannel.consume] all elements of both the original [ReceiveChannel] and the `other` one. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public infix fun ReceiveChannel.zip(other: ReceiveChannel): ReceiveChannel> = - zip(other) { t1, t2 -> t1 to t2 } - -/** - * Returns a channel of values built from elements of both collections with same indexes using provided [transform]. Resulting channel has length of shortest input channels. - * - * The operation is _intermediate_ and _stateless_. - * This function [consumes][consume] all elements of both the original [ReceiveChannel] and the `other` one. - * - * **Note: This API will become obsolete in future updates with introduction of lazy asynchronous streams.** - * See [issue #254](https://github.com/Kotlin/kotlinx.coroutines/issues/254). - */ -@Deprecated( - message = "Channel operators are deprecated in favour of Flow and will be removed in 1.4.x", - level = DeprecationLevel.ERROR -) -public fun ReceiveChannel.zip(other: ReceiveChannel, context: CoroutineContext = Dispatchers.Unconfined, transform: (a: E, b: R) -> V): ReceiveChannel = - GlobalScope.produce(context, onCompletion = consumesAll(this, other)) { - val otherIterator = other.iterator() - this@zip.consumeEach { element1 -> - if (!otherIterator.hasNext()) return@consumeEach - val element2 = otherIterator.next() - send(transform(element1, element2)) - } - } diff --git a/kotlinx-coroutines-core/common/src/channels/Deprecated.kt b/kotlinx-coroutines-core/common/src/channels/Deprecated.kt new file mode 100644 index 0000000000..963c168c7b --- /dev/null +++ b/kotlinx-coroutines-core/common/src/channels/Deprecated.kt @@ -0,0 +1,406 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:JvmMultifileClass +@file:JvmName("ChannelsKt") +@file:Suppress("unused") + +package kotlinx.coroutines.channels + +import kotlinx.coroutines.* +import kotlin.coroutines.* +import kotlin.jvm.* + +@PublishedApi // Binary compatibility +internal fun consumesAll(vararg channels: ReceiveChannel<*>): CompletionHandler = + { cause: Throwable? -> + var exception: Throwable? = null + for (channel in channels) + try { + channel.cancelConsumed(cause) + } catch (e: Throwable) { + if (exception == null) { + exception = e + } else { + exception.addSuppressedThrowable(e) + } + } + exception?.let { throw it } + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.elementAt(index: Int): E = consume { + if (index < 0) + throw IndexOutOfBoundsException("ReceiveChannel doesn't contain element at index $index.") + var count = 0 + for (element in this) { + if (index == count++) + return element + } + throw IndexOutOfBoundsException("ReceiveChannel doesn't contain element at index $index.") +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.elementAtOrNull(index: Int): E? = + consume { + if (index < 0) + return null + var count = 0 + for (element in this) { + if (index == count++) + return element + } + return null + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.first(): E = + consume { + val iterator = iterator() + if (!iterator.hasNext()) + throw NoSuchElementException("ReceiveChannel is empty.") + return iterator.next() + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.firstOrNull(): E? = + consume { + val iterator = iterator() + if (!iterator.hasNext()) + return null + return iterator.next() + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.indexOf(element: E): Int { + var index = 0 + consumeEach { + if (element == it) + return index + index++ + } + return -1 +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.last(): E = + consume { + val iterator = iterator() + if (!iterator.hasNext()) + throw NoSuchElementException("ReceiveChannel is empty.") + var last = iterator.next() + while (iterator.hasNext()) + last = iterator.next() + return last + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.lastIndexOf(element: E): Int { + var lastIndex = -1 + var index = 0 + consumeEach { + if (element == it) + lastIndex = index + index++ + } + return lastIndex +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.lastOrNull(): E? = + consume { + val iterator = iterator() + if (!iterator.hasNext()) + return null + var last = iterator.next() + while (iterator.hasNext()) + last = iterator.next() + return last + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.single(): E = + consume { + val iterator = iterator() + if (!iterator.hasNext()) + throw NoSuchElementException("ReceiveChannel is empty.") + val single = iterator.next() + if (iterator.hasNext()) + throw IllegalArgumentException("ReceiveChannel has more than one element.") + return single + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.singleOrNull(): E? = + consume { + val iterator = iterator() + if (!iterator.hasNext()) + return null + val single = iterator.next() + if (iterator.hasNext()) + return null + return single + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.drop(n: Int, context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + require(n >= 0) { "Requested element count $n is less than zero." } + var remaining: Int = n + if (remaining > 0) + for (e in this@drop) { + remaining-- + if (remaining == 0) + break + } + for (e in this@drop) { + send(e) + } + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.dropWhile(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + for (e in this@dropWhile) { + if (!predicate(e)) { + send(e) + break + } + } + for (e in this@dropWhile) { + send(e) + } + } + +@PublishedApi +internal fun ReceiveChannel.filter(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + for (e in this@filter) { + if (predicate(e)) send(e) + } + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.filterIndexed(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (index: Int, E) -> Boolean): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + var index = 0 + for (e in this@filterIndexed) { + if (predicate(index++, e)) send(e) + } + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.filterNot(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = + filter(context) { !predicate(it) } + +@PublishedApi +@Suppress("UNCHECKED_CAST") +internal fun ReceiveChannel.filterNotNull(): ReceiveChannel = + filter { it != null } as ReceiveChannel + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun > ReceiveChannel.filterNotNullTo(destination: C): C { + consumeEach { + if (it != null) destination.add(it) + } + return destination +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun > ReceiveChannel.filterNotNullTo(destination: C): C { + consumeEach { + if (it != null) destination.send(it) + } + return destination +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.take(n: Int, context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + if (n == 0) return@produce + require(n >= 0) { "Requested element count $n is less than zero." } + var remaining: Int = n + for (e in this@take) { + send(e) + remaining-- + if (remaining == 0) + return@produce + } + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.takeWhile(context: CoroutineContext = Dispatchers.Unconfined, predicate: suspend (E) -> Boolean): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + for (e in this@takeWhile) { + if (!predicate(e)) return@produce + send(e) + } + } + +@PublishedApi +internal suspend fun > ReceiveChannel.toChannel(destination: C): C { + consumeEach { + destination.send(it) + } + return destination +} + +@PublishedApi +internal suspend fun > ReceiveChannel.toCollection(destination: C): C { + consumeEach { + destination.add(it) + } + return destination +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel>.toMap(): Map = + toMap(LinkedHashMap()) + +@PublishedApi +internal suspend fun > ReceiveChannel>.toMap(destination: M): M { + consumeEach { + destination += it + } + return destination +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.toMutableList(): MutableList = + toCollection(ArrayList()) + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.toSet(): Set = + this.toMutableSet() + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.flatMap(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> ReceiveChannel): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + for (e in this@flatMap) { + transform(e).toChannel(this) + } + } + +@PublishedApi +internal fun ReceiveChannel.map(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> R): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + consumeEach { + send(transform(it)) + } + } + +@PublishedApi +internal fun ReceiveChannel.mapIndexed(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (index: Int, E) -> R): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + var index = 0 + for (e in this@mapIndexed) { + send(transform(index++, e)) + } + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.mapIndexedNotNull(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (index: Int, E) -> R?): ReceiveChannel = + mapIndexed(context, transform).filterNotNull() + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.mapNotNull(context: CoroutineContext = Dispatchers.Unconfined, transform: suspend (E) -> R?): ReceiveChannel = + map(context, transform).filterNotNull() + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.withIndex(context: CoroutineContext = Dispatchers.Unconfined): ReceiveChannel> = + GlobalScope.produce(context, onCompletion = consumes()) { + var index = 0 + for (e in this@withIndex) { + send(IndexedValue(index++, e)) + } + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.distinct(): ReceiveChannel = + this.distinctBy { it } + +@PublishedApi +internal fun ReceiveChannel.distinctBy(context: CoroutineContext = Dispatchers.Unconfined, selector: suspend (E) -> K): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumes()) { + val keys = HashSet() + for (e in this@distinctBy) { + val k = selector(e) + if (k !in keys) { + send(e) + keys += k + } + } + } + +@PublishedApi +internal suspend fun ReceiveChannel.toMutableSet(): MutableSet = + toCollection(LinkedHashSet()) + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.any(): Boolean = + consume { + return iterator().hasNext() + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.count(): Int { + var count = 0 + consumeEach { count++ } + return count +} + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.maxWith(comparator: Comparator): E? = + consume { + val iterator = iterator() + if (!iterator.hasNext()) return null + var max = iterator.next() + while (iterator.hasNext()) { + val e = iterator.next() + if (comparator.compare(max, e) < 0) max = e + } + return max + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.minWith(comparator: Comparator): E? = + consume { + val iterator = iterator() + if (!iterator.hasNext()) return null + var min = iterator.next() + while (iterator.hasNext()) { + val e = iterator.next() + if (comparator.compare(min, e) > 0) min = e + } + return min + } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public suspend fun ReceiveChannel.none(): Boolean = + consume { + return !iterator().hasNext() + } + +@Deprecated(message = "Left for binary compatibility", level = DeprecationLevel.HIDDEN) +public fun ReceiveChannel.requireNoNulls(): ReceiveChannel = + map { it ?: throw IllegalArgumentException("null element found in $this.") } + +@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN) +public infix fun ReceiveChannel.zip(other: ReceiveChannel): ReceiveChannel> = + zip(other) { t1, t2 -> t1 to t2 } + +@PublishedApi // Binary compatibility +internal fun ReceiveChannel.zip(other: ReceiveChannel, context: CoroutineContext = Dispatchers.Unconfined, transform: (a: E, b: R) -> V): ReceiveChannel = + GlobalScope.produce(context, onCompletion = consumesAll(this, other)) { + val otherIterator = other.iterator() + this@zip.consumeEach { element1 -> + if (!otherIterator.hasNext()) return@consumeEach + val element2 = otherIterator.next() + send(transform(element1, element2)) + } + } + +@PublishedApi // Binary compatibility +internal fun ReceiveChannel<*>.consumes(): CompletionHandler = { cause: Throwable? -> + cancelConsumed(cause) +} From 6a6b34cf820d491700eeb7bc0e0e7ce01af66ae2 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 9 Dec 2020 18:52:46 +0300 Subject: [PATCH 27/55] Adjust deprecations in reactive integrations * Remove all deprecated inline methods * Promote ERROR to HIDDEN where applicable * Promote WARNING to ERROR where applicable --- .../api/kotlinx-coroutines-core.api | 5 +++ .../api/kotlinx-coroutines-reactive.api | 10 +++--- .../src/Channel.kt | 16 +++++---- .../src/Convert.kt | 4 +-- .../src/Migration.kt | 6 ++-- .../src/Publish.kt | 24 ++++++-------- .../test/IntegrationTest.kt | 28 ++-------------- .../test/PublisherSubscriptionSelectTest.kt | 4 +-- .../api/kotlinx-coroutines-reactor.api | 6 ++-- .../kotlinx-coroutines-reactor/src/Convert.kt | 4 +-- .../kotlinx-coroutines-reactor/src/Flux.kt | 27 +++++++-------- .../src/Migration.kt | 4 +-- .../kotlinx-coroutines-reactor/src/Mono.kt | 22 ++++++------- .../test/ConvertTest.kt | 7 ++-- .../api/kotlinx-coroutines-rx2.api | 14 ++++---- .../kotlinx-coroutines-rx2/src/RxChannel.kt | 33 +++++++++++-------- .../src/RxCompletable.kt | 24 ++++++-------- .../kotlinx-coroutines-rx2/src/RxFlowable.kt | 2 +- .../kotlinx-coroutines-rx2/src/RxMaybe.kt | 24 ++++++-------- .../src/RxObservable.kt | 24 ++++++-------- .../kotlinx-coroutines-rx2/src/RxSingle.kt | 24 ++++++-------- .../test/IntegrationTest.kt | 2 +- .../test/ObservableSubscriptionSelectTest.kt | 4 +-- 23 files changed, 141 insertions(+), 177 deletions(-) diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index f279d5509a..cf9906fc5a 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -674,6 +674,7 @@ public final class kotlinx/coroutines/channels/ChannelsKt { public static final synthetic fun count (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final synthetic fun distinct (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun distinctBy (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static synthetic fun distinctBy$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun drop (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun drop$default (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun dropWhile (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; @@ -681,6 +682,7 @@ public final class kotlinx/coroutines/channels/ChannelsKt { public static final synthetic fun elementAt (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final synthetic fun elementAtOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;ILkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun filter (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static synthetic fun filter$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun filterIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun filterIndexed$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun filterNot (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; @@ -697,7 +699,9 @@ public final class kotlinx/coroutines/channels/ChannelsKt { public static final synthetic fun lastIndexOf (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final synthetic fun lastOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun map (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static synthetic fun map$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun mapIndexed (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static synthetic fun mapIndexed$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun mapIndexedNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun mapIndexedNotNull$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun mapNotNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; @@ -727,6 +731,7 @@ public final class kotlinx/coroutines/channels/ChannelsKt { public static synthetic fun withIndex$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static synthetic fun zip$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; } public final class kotlinx/coroutines/channels/ClosedReceiveChannelException : java/util/NoSuchElementException { diff --git a/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api b/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api index 06ef048407..75f1b306d3 100644 --- a/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api +++ b/reactive/kotlinx-coroutines-reactive/api/kotlinx-coroutines-reactive.api @@ -12,9 +12,9 @@ public final class kotlinx/coroutines/reactive/AwaitKt { public final class kotlinx/coroutines/reactive/ChannelKt { public static final fun collect (Lorg/reactivestreams/Publisher;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun consumeEach (Lorg/reactivestreams/Publisher;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun openSubscription (Lorg/reactivestreams/Publisher;I)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun openSubscription$default (Lorg/reactivestreams/Publisher;IILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final fun toChannel (Lorg/reactivestreams/Publisher;I)Lkotlinx/coroutines/channels/ReceiveChannel; } public abstract interface class kotlinx/coroutines/reactive/ContextInjector { @@ -27,9 +27,9 @@ public final class kotlinx/coroutines/reactive/ConvertKt { } public final class kotlinx/coroutines/reactive/FlowKt { - public static final fun asFlow (Lorg/reactivestreams/Publisher;)Lkotlinx/coroutines/flow/Flow; - public static final fun asFlow (Lorg/reactivestreams/Publisher;I)Lkotlinx/coroutines/flow/Flow; - public static final fun asPublisher (Lkotlinx/coroutines/flow/Flow;)Lorg/reactivestreams/Publisher; + public static final synthetic fun asFlow (Lorg/reactivestreams/Publisher;)Lkotlinx/coroutines/flow/Flow; + public static final synthetic fun asFlow (Lorg/reactivestreams/Publisher;I)Lkotlinx/coroutines/flow/Flow; + public static final synthetic fun asPublisher (Lkotlinx/coroutines/flow/Flow;)Lorg/reactivestreams/Publisher; } public final class kotlinx/coroutines/reactive/FlowSubscription : kotlinx/coroutines/AbstractCoroutine, org/reactivestreams/Subscription { @@ -42,7 +42,7 @@ public final class kotlinx/coroutines/reactive/FlowSubscription : kotlinx/corout public final class kotlinx/coroutines/reactive/PublishKt { public static final fun publish (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lorg/reactivestreams/Publisher; - public static final fun publish (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lorg/reactivestreams/Publisher; + public static final synthetic fun publish (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lorg/reactivestreams/Publisher; public static synthetic fun publish$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/reactivestreams/Publisher; public static synthetic fun publish$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/reactivestreams/Publisher; public static final fun publishInternal (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lorg/reactivestreams/Publisher; diff --git a/reactive/kotlinx-coroutines-reactive/src/Channel.kt b/reactive/kotlinx-coroutines-reactive/src/Channel.kt index 352a505a5f..854a8829fc 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Channel.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Channel.kt @@ -26,24 +26,26 @@ import org.reactivestreams.* */ @Deprecated( message = "Transforming publisher to channel is deprecated, use asFlow() instead", - level = DeprecationLevel.WARNING) // Will be error in 1.4 + level = DeprecationLevel.ERROR) // Will be error in 1.4 public fun Publisher.openSubscription(request: Int = 1): ReceiveChannel { val channel = SubscriptionChannel(request) subscribe(channel) return channel } -// Will be promoted to error in 1.3.0, removed in 1.4.0 -@Deprecated(message = "Use collect instead", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("this.collect(action)")) -public suspend inline fun Publisher.consumeEach(action: (T) -> Unit): Unit = - openSubscription().consumeEach(action) - /** * Subscribes to this [Publisher] and performs the specified action for each received element. * Cancels subscription if any exception happens during collect. */ public suspend inline fun Publisher.collect(action: (T) -> Unit): Unit = - openSubscription().consumeEach(action) + toChannel().consumeEach(action) + +@PublishedApi +internal fun Publisher.toChannel(request: Int = 1): ReceiveChannel { + val channel = SubscriptionChannel(request) + subscribe(channel) + return channel +} @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "SubscriberImplementation") private class SubscriptionChannel( diff --git a/reactive/kotlinx-coroutines-reactive/src/Convert.kt b/reactive/kotlinx-coroutines-reactive/src/Convert.kt index 8f4b26d377..3cb05b60fd 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Convert.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Convert.kt @@ -16,8 +16,8 @@ import kotlin.coroutines.* * @param context -- the coroutine context from which the resulting observable is going to be signalled */ @Deprecated(message = "Deprecated in the favour of consumeAsFlow()", - level = DeprecationLevel.WARNING, // Error in 1.4 - replaceWith = ReplaceWith("this.consumeAsFlow().asPublisher()")) + level = DeprecationLevel.ERROR, // Error in 1.4 + replaceWith = ReplaceWith("this.consumeAsFlow().asPublisher(context)", imports = ["kotlinx.coroutines.flow.consumeAsFlow"])) public fun ReceiveChannel.asPublisher(context: CoroutineContext = EmptyCoroutineContext): Publisher = publish(context) { for (t in this@asPublisher) send(t) diff --git a/reactive/kotlinx-coroutines-reactive/src/Migration.kt b/reactive/kotlinx-coroutines-reactive/src/Migration.kt index 1bba746c03..b949f22665 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Migration.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Migration.kt @@ -14,7 +14,7 @@ import org.reactivestreams.* // Binary compatibility with Spring 5.2 RC @Deprecated( message = "Replaced in favor of ReactiveFlow extension, please import kotlinx.coroutines.reactive.* instead of kotlinx.coroutines.reactive.FlowKt", - level = DeprecationLevel.ERROR + level = DeprecationLevel.HIDDEN ) @JvmName("asFlow") public fun Publisher.asFlowDeprecated(): Flow = asFlow() @@ -22,7 +22,7 @@ public fun Publisher.asFlowDeprecated(): Flow = asFlow() // Binary compatibility with Spring 5.2 RC @Deprecated( message = "Replaced in favor of ReactiveFlow extension, please import kotlinx.coroutines.reactive.* instead of kotlinx.coroutines.reactive.FlowKt", - level = DeprecationLevel.ERROR + level = DeprecationLevel.HIDDEN ) @JvmName("asPublisher") public fun Flow.asPublisherDeprecated(): Publisher = asPublisher() @@ -30,7 +30,7 @@ public fun Flow.asPublisherDeprecated(): Publisher = asPublisher @FlowPreview @Deprecated( message = "batchSize parameter is deprecated, use .buffer() instead to control the backpressure", - level = DeprecationLevel.ERROR, + level = DeprecationLevel.HIDDEN, replaceWith = ReplaceWith("asFlow().buffer(batchSize)", imports = ["kotlinx.coroutines.flow.*"]) ) public fun Publisher.asFlow(batchSize: Int): Flow = asFlow().buffer(batchSize) diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index f66b1c3bae..d2cdbab881 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -1,9 +1,6 @@ /* * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ - -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - package kotlinx.coroutines.reactive import kotlinx.atomicfu.* @@ -43,17 +40,6 @@ public fun publish( return publishInternal(GlobalScope, context, DEFAULT_HANDLER, block) } -@Deprecated( - message = "CoroutineScope.publish is deprecated in favour of top-level publish", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("publish(context, block)") -) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0. Binary compatibility with Spring -@LowPriorityInOverloadResolution -public fun CoroutineScope.publish( - context: CoroutineContext = EmptyCoroutineContext, - @BuilderInference block: suspend ProducerScope.() -> Unit -): Publisher = publishInternal(this, context, DEFAULT_HANDLER ,block) - /** @suppress For internal use from other reactive integration modules only */ @InternalCoroutinesApi public fun publishInternal( @@ -288,3 +274,13 @@ public class PublisherCoroutine( private fun Throwable.isFatal() = this is VirtualMachineError || this is ThreadDeath || this is LinkageError } + +@Deprecated( + message = "CoroutineScope.publish is deprecated in favour of top-level publish", + level = DeprecationLevel.HIDDEN, + replaceWith = ReplaceWith("publish(context, block)") +) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0. Binary compatibility with Spring +public fun CoroutineScope.publish( + context: CoroutineContext = EmptyCoroutineContext, + @BuilderInference block: suspend ProducerScope.() -> Unit +): Publisher = publishInternal(this, context, DEFAULT_HANDLER, block) diff --git a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt index a1467080ac..72479b5c6a 100644 --- a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt @@ -81,30 +81,6 @@ class IntegrationTest( assertEquals(1, cnt) } - @Test - fun testNumbers() = runBlocking { - val n = 100 * stressTestMultiplier - val pub = publish(ctx(coroutineContext)) { - for (i in 1..n) { - send(i) - if (delay) delay(1) - } - } - assertEquals(1, pub.awaitFirst()) - assertEquals(1, pub.awaitFirstOrDefault(0)) - assertEquals(1, pub.awaitFirstOrNull()) - assertEquals(1, pub.awaitFirstOrElse { 0 }) - assertEquals(n, pub.awaitLast()) - assertFailsWith { pub.awaitSingle() } - assertFailsWith { pub.awaitSingleOrDefault(0) } - assertFailsWith { pub.awaitSingleOrNull() } - assertFailsWith { pub.awaitSingleOrElse { 0 } } - checkNumbers(n, pub) - val channel = pub.openSubscription() - checkNumbers(n, channel.asPublisher(ctx(coroutineContext))) - channel.cancel() - } - @Test fun testCancelWithoutValue() = runTest { val job = launch(Job(), start = CoroutineStart.UNDISPATCHED) { @@ -118,7 +94,7 @@ class IntegrationTest( } @Test - fun testEmptySingle() = runTest(unhandled = listOf({e -> e is NoSuchElementException})) { + fun testEmptySingle() = runTest(unhandled = listOf { e -> e is NoSuchElementException }) { expect(1) val job = launch(Job(), start = CoroutineStart.UNDISPATCHED) { publish { @@ -270,4 +246,4 @@ class IntegrationTest( assertEquals(n, last) } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt index 790cf7ec7c..740fd86ae4 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherSubscriptionSelectTest.kt @@ -28,8 +28,8 @@ class PublisherSubscriptionSelectTest(private val request: Int) : TestBase() { var a = 0 var b = 0 // open two subs - val channelA = source.openSubscription(request) - val channelB = source.openSubscription(request) + val channelA = source.toChannel(request) + val channelB = source.toChannel(request) loop@ while (true) { val done: Int = select { channelA.onReceiveCatching { result -> diff --git a/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api b/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api index b46fe338e5..3a1c8b7d31 100644 --- a/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api +++ b/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api @@ -6,19 +6,19 @@ public final class kotlinx/coroutines/reactor/ConvertKt { } public final class kotlinx/coroutines/reactor/FlowKt { - public static final fun asFlux (Lkotlinx/coroutines/flow/Flow;)Lreactor/core/publisher/Flux; + public static final synthetic fun asFlux (Lkotlinx/coroutines/flow/Flow;)Lreactor/core/publisher/Flux; } public final class kotlinx/coroutines/reactor/FluxKt { public static final fun flux (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Flux; - public static final fun flux (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Flux; + public static final synthetic fun flux (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Flux; public static synthetic fun flux$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lreactor/core/publisher/Flux; public static synthetic fun flux$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lreactor/core/publisher/Flux; } public final class kotlinx/coroutines/reactor/MonoKt { public static final fun mono (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Mono; - public static final fun mono (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Mono; + public static final synthetic fun mono (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Mono; public static synthetic fun mono$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lreactor/core/publisher/Mono; public static synthetic fun mono$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lreactor/core/publisher/Mono; } diff --git a/reactive/kotlinx-coroutines-reactor/src/Convert.kt b/reactive/kotlinx-coroutines-reactor/src/Convert.kt index 7807549ce1..3b08bd6639 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Convert.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Convert.kt @@ -46,8 +46,8 @@ public fun Deferred.asMono(context: CoroutineContext): Mono = mono(co * @param context -- the coroutine context from which the resulting flux is going to be signalled */ @Deprecated(message = "Deprecated in the favour of consumeAsFlow()", - level = DeprecationLevel.WARNING, - replaceWith = ReplaceWith("this.consumeAsFlow().asFlux()")) + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("this.consumeAsFlow().asFlux(context)", imports = ["kotlinx.coroutines.flow.consumeAsFlow"])) public fun ReceiveChannel.asFlux(context: CoroutineContext = EmptyCoroutineContext): Flux = flux(context) { for (t in this@asFlux) send(t) diff --git a/reactive/kotlinx-coroutines-reactor/src/Flux.kt b/reactive/kotlinx-coroutines-reactor/src/Flux.kt index 8d4f9cc969..b7143288ee 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Flux.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Flux.kt @@ -1,10 +1,7 @@ - /* * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - package kotlinx.coroutines.reactor import kotlinx.coroutines.* @@ -15,7 +12,6 @@ import reactor.core.* import reactor.core.publisher.* import reactor.util.context.* import kotlin.coroutines.* -import kotlin.internal.* /** * Creates cold reactive [Flux] that runs a given [block] in a coroutine. @@ -43,18 +39,6 @@ public fun flux( return Flux.from(reactorPublish(GlobalScope, context, block)) } -@Deprecated( - message = "CoroutineScope.flux is deprecated in favour of top-level flux", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("flux(context, block)") -) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0. Binary compatibility with Spring -@LowPriorityInOverloadResolution -public fun CoroutineScope.flux( - context: CoroutineContext = EmptyCoroutineContext, - @BuilderInference block: suspend ProducerScope.() -> Unit -): Flux = - Flux.from(reactorPublish(this, context, block)) - private fun reactorPublish( scope: CoroutineScope, context: CoroutineContext = EmptyCoroutineContext, @@ -80,3 +64,14 @@ private val REACTOR_HANDLER: (Throwable, CoroutineContext) -> Unit = { e, ctx -> } } } + +@Deprecated( + message = "CoroutineScope.flux is deprecated in favour of top-level flux", + level = DeprecationLevel.HIDDEN, + replaceWith = ReplaceWith("flux(context, block)") +) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0. Binary compatibility with Spring +public fun CoroutineScope.flux( + context: CoroutineContext = EmptyCoroutineContext, + @BuilderInference block: suspend ProducerScope.() -> Unit +): Flux = + Flux.from(reactorPublish(this, context, block)) diff --git a/reactive/kotlinx-coroutines-reactor/src/Migration.kt b/reactive/kotlinx-coroutines-reactor/src/Migration.kt index ec5674d932..d5d84f5c2a 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Migration.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Migration.kt @@ -11,7 +11,7 @@ import reactor.core.publisher.* @Deprecated( message = "Replaced in favor of ReactiveFlow extension, please import kotlinx.coroutines.reactor.* instead of kotlinx.coroutines.reactor.FlowKt", - level = DeprecationLevel.ERROR -) + level = DeprecationLevel.HIDDEN +) // Compatibility with Spring 5.2-RC @JvmName("asFlux") public fun Flow.asFluxDeprecated(): Flux = asFlux() diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index 6786f1abab..307ec2278c 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -34,17 +34,6 @@ public fun mono( return monoInternal(GlobalScope, context, block) } -@Deprecated( - message = "CoroutineScope.mono is deprecated in favour of top-level mono", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("mono(context, block)") -) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 -@LowPriorityInOverloadResolution -public fun CoroutineScope.mono( - context: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.() -> T? -): Mono = monoInternal(this, context, block) - private fun monoInternal( scope: CoroutineScope, // support for legacy mono in scope context: CoroutineContext, @@ -89,3 +78,14 @@ private class MonoCoroutine( override fun isDisposed(): Boolean = disposed } + +@Deprecated( + message = "CoroutineScope.mono is deprecated in favour of top-level mono", + level = DeprecationLevel.HIDDEN, + replaceWith = ReplaceWith("mono(context, block)") +) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 +public fun CoroutineScope.mono( + context: CoroutineContext = EmptyCoroutineContext, + block: suspend CoroutineScope.() -> T? +): Mono = monoInternal(this, context, block) + diff --git a/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt b/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt index 82664a2dee..8af6d3c455 100644 --- a/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/ConvertTest.kt @@ -6,6 +6,7 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* import kotlinx.coroutines.channels.* +import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* import org.junit.* import org.junit.Test @@ -96,7 +97,7 @@ class ConvertTest : TestBase() { delay(50) send("K") } - val flux = c.asFlux(Dispatchers.Unconfined) + val flux = c.consumeAsFlow().asFlux(Dispatchers.Unconfined) checkMonoValue(flux.reduce { t1, t2 -> t1 + t2 }) { assertEquals("OK", it) } @@ -110,7 +111,7 @@ class ConvertTest : TestBase() { delay(50) throw TestException("K") } - val flux = c.asFlux(Dispatchers.Unconfined) + val flux = c.consumeAsFlow().asFlux(Dispatchers.Unconfined) val mono = mono(Dispatchers.Unconfined) { var result = "" try { @@ -125,4 +126,4 @@ class ConvertTest : TestBase() { assertEquals("OK", it) } } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api b/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api index 4370325f58..eb5e2fb4ad 100644 --- a/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api +++ b/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api @@ -14,15 +14,15 @@ public final class kotlinx/coroutines/rx2/RxAwaitKt { public final class kotlinx/coroutines/rx2/RxChannelKt { public static final fun collect (Lio/reactivex/MaybeSource;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun collect (Lio/reactivex/ObservableSource;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun consumeEach (Lio/reactivex/MaybeSource;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun consumeEach (Lio/reactivex/ObservableSource;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun openSubscription (Lio/reactivex/MaybeSource;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final fun openSubscription (Lio/reactivex/ObservableSource;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final fun toChannel (Lio/reactivex/MaybeSource;)Lkotlinx/coroutines/channels/ReceiveChannel; + public static final fun toChannel (Lio/reactivex/ObservableSource;)Lkotlinx/coroutines/channels/ReceiveChannel; } public final class kotlinx/coroutines/rx2/RxCompletableKt { public static final fun rxCompletable (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Completable; - public static final fun rxCompletable (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Completable; + public static final synthetic fun rxCompletable (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Completable; public static synthetic fun rxCompletable$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Completable; public static synthetic fun rxCompletable$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Completable; } @@ -47,21 +47,21 @@ public final class kotlinx/coroutines/rx2/RxConvertKt { public final class kotlinx/coroutines/rx2/RxFlowableKt { public static final fun rxFlowable (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Flowable; - public static final fun rxFlowable (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Flowable; + public static final synthetic fun rxFlowable (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Flowable; public static synthetic fun rxFlowable$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Flowable; public static synthetic fun rxFlowable$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Flowable; } public final class kotlinx/coroutines/rx2/RxMaybeKt { public static final fun rxMaybe (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Maybe; - public static final fun rxMaybe (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Maybe; + public static final synthetic fun rxMaybe (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Maybe; public static synthetic fun rxMaybe$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Maybe; public static synthetic fun rxMaybe$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Maybe; } public final class kotlinx/coroutines/rx2/RxObservableKt { public static final fun rxObservable (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Observable; - public static final fun rxObservable (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Observable; + public static final synthetic fun rxObservable (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Observable; public static synthetic fun rxObservable$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Observable; public static synthetic fun rxObservable$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Observable; } @@ -72,7 +72,7 @@ public final class kotlinx/coroutines/rx2/RxSchedulerKt { public final class kotlinx/coroutines/rx2/RxSingleKt { public static final fun rxSingle (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Single; - public static final fun rxSingle (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Single; + public static final synthetic fun rxSingle (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lio/reactivex/Single; public static synthetic fun rxSingle$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Single; public static synthetic fun rxSingle$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/reactivex/Single; } diff --git a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt index a129196a13..35f3391eb1 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt @@ -9,6 +9,7 @@ import io.reactivex.disposables.* import kotlinx.atomicfu.* import kotlinx.coroutines.channels.* import kotlinx.coroutines.internal.* +import kotlinx.coroutines.flow.* /** * Subscribes to this [MaybeSource] and returns a channel to receive elements emitted by it. @@ -17,7 +18,7 @@ import kotlinx.coroutines.internal.* * This API is deprecated in the favour of [Flow]. * [MaybeSource] doesn't have a corresponding [Flow] adapter, so it should be transformed to [Observable] first. */ -@Deprecated(message = "Deprecated in the favour of Flow", level = DeprecationLevel.WARNING) // Will be hidden in 1.4 +@Deprecated(message = "Deprecated in the favour of Flow", level = DeprecationLevel.ERROR) // Will be hidden in 1.5 public fun MaybeSource.openSubscription(): ReceiveChannel { val channel = SubscriptionChannel() subscribe(channel) @@ -31,36 +32,40 @@ public fun MaybeSource.openSubscription(): ReceiveChannel { * This API is deprecated in the favour of [Flow]. * [ObservableSource] doesn't have a corresponding [Flow] adapter, so it should be transformed to [Observable] first. */ -@Deprecated(message = "Deprecated in the favour of Flow", level = DeprecationLevel.WARNING) // Will be hidden in 1.4 +@Deprecated(message = "Deprecated in the favour of Flow", level = DeprecationLevel.ERROR) // Will be hidden in 1.5 public fun ObservableSource.openSubscription(): ReceiveChannel { val channel = SubscriptionChannel() subscribe(channel) return channel } -// Will be promoted to error in 1.3.0, removed in 1.4.0 -@Deprecated(message = "Use collect instead", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("this.collect(action)")) -public suspend inline fun MaybeSource.consumeEach(action: (T) -> Unit): Unit = - openSubscription().consumeEach(action) - -// Will be promoted to error in 1.3.0, removed in 1.4.0 -@Deprecated(message = "Use collect instead", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("this.collect(action)")) -public suspend inline fun ObservableSource.consumeEach(action: (T) -> Unit): Unit = - openSubscription().consumeEach(action) - /** * Subscribes to this [MaybeSource] and performs the specified action for each received element. * Cancels subscription if any exception happens during collect. */ public suspend inline fun MaybeSource.collect(action: (T) -> Unit): Unit = - openSubscription().consumeEach(action) + toChannel().consumeEach(action) /** * Subscribes to this [ObservableSource] and performs the specified action for each received element. * Cancels subscription if any exception happens during collect. */ public suspend inline fun ObservableSource.collect(action: (T) -> Unit): Unit = - openSubscription().consumeEach(action) + toChannel().consumeEach(action) + +@PublishedApi +internal fun MaybeSource.toChannel(): ReceiveChannel { + val channel = SubscriptionChannel() + subscribe(channel) + return channel +} + +@PublishedApi +internal fun ObservableSource.toChannel(): ReceiveChannel { + val channel = SubscriptionChannel() + subscribe(channel) + return channel +} @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") private class SubscriptionChannel : diff --git a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt index 3400278cb3..eee84b1bb6 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt @@ -2,14 +2,11 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - package kotlinx.coroutines.rx2 import io.reactivex.* import kotlinx.coroutines.* import kotlin.coroutines.* -import kotlin.internal.* /** * Creates cold [Completable] that runs a given [block] in a coroutine and emits its result. @@ -28,17 +25,6 @@ public fun rxCompletable( return rxCompletableInternal(GlobalScope, context, block) } -@Deprecated( - message = "CoroutineScope.rxCompletable is deprecated in favour of top-level rxCompletable", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("rxCompletable(context, block)") -) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 -@LowPriorityInOverloadResolution -public fun CoroutineScope.rxCompletable( - context: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.() -> Unit -): Completable = rxCompletableInternal(this, context, block) - private fun rxCompletableInternal( scope: CoroutineScope, // support for legacy rxCompletable in scope context: CoroutineContext, @@ -72,3 +58,13 @@ private class RxCompletableCoroutine( } } } + +@Deprecated( + message = "CoroutineScope.rxCompletable is deprecated in favour of top-level rxCompletable", + level = DeprecationLevel.HIDDEN, + replaceWith = ReplaceWith("rxCompletable(context, block)") +) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 +public fun CoroutineScope.rxCompletable( + context: CoroutineContext = EmptyCoroutineContext, + block: suspend CoroutineScope.() -> Unit +): Completable = rxCompletableInternal(this, context, block) diff --git a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt index 8dfe95762e..6b299437aa 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt @@ -43,7 +43,7 @@ public fun rxFlowable( @Deprecated( message = "CoroutineScope.rxFlowable is deprecated in favour of top-level rxFlowable", - level = DeprecationLevel.ERROR, + level = DeprecationLevel.HIDDEN, replaceWith = ReplaceWith("rxFlowable(context, block)") ) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 @LowPriorityInOverloadResolution diff --git a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt index c4ca54cf48..9682373a79 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt @@ -2,14 +2,11 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - package kotlinx.coroutines.rx2 import io.reactivex.* import kotlinx.coroutines.* import kotlin.coroutines.* -import kotlin.internal.* /** * Creates cold [maybe][Maybe] that will run a given [block] in a coroutine and emits its result. @@ -29,17 +26,6 @@ public fun rxMaybe( return rxMaybeInternal(GlobalScope, context, block) } -@Deprecated( - message = "CoroutineScope.rxMaybe is deprecated in favour of top-level rxMaybe", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("rxMaybe(context, block)") -) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 -@LowPriorityInOverloadResolution -public fun CoroutineScope.rxMaybe( - context: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.() -> T? -): Maybe = rxMaybeInternal(this, context, block) - private fun rxMaybeInternal( scope: CoroutineScope, // support for legacy rxMaybe in scope context: CoroutineContext, @@ -73,3 +59,13 @@ private class RxMaybeCoroutine( } } } + +@Deprecated( + message = "CoroutineScope.rxMaybe is deprecated in favour of top-level rxMaybe", + level = DeprecationLevel.HIDDEN, + replaceWith = ReplaceWith("rxMaybe(context, block)") +) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 +public fun CoroutineScope.rxMaybe( + context: CoroutineContext = EmptyCoroutineContext, + block: suspend CoroutineScope.() -> T? +): Maybe = rxMaybeInternal(this, context, block) diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index edbb106253..56ce30c370 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -2,8 +2,6 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - package kotlinx.coroutines.rx2 import io.reactivex.* @@ -14,7 +12,6 @@ import kotlinx.coroutines.channels.* import kotlinx.coroutines.selects.* import kotlinx.coroutines.sync.* import kotlin.coroutines.* -import kotlin.internal.* /** * Creates cold [observable][Observable] that will run a given [block] in a coroutine. @@ -42,17 +39,6 @@ public fun rxObservable( return rxObservableInternal(GlobalScope, context, block) } -@Deprecated( - message = "CoroutineScope.rxObservable is deprecated in favour of top-level rxObservable", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("rxObservable(context, block)") -) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 -@LowPriorityInOverloadResolution -public fun CoroutineScope.rxObservable( - context: CoroutineContext = EmptyCoroutineContext, - @BuilderInference block: suspend ProducerScope.() -> Unit -): Observable = rxObservableInternal(this, context, block) - private fun rxObservableInternal( scope: CoroutineScope, // support for legacy rxObservable in scope context: CoroutineContext, @@ -212,3 +198,13 @@ internal fun Throwable.isFatal() = try { } catch (e: Throwable) { true } + +@Deprecated( + message = "CoroutineScope.rxObservable is deprecated in favour of top-level rxObservable", + level = DeprecationLevel.HIDDEN, + replaceWith = ReplaceWith("rxObservable(context, block)") +) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 +public fun CoroutineScope.rxObservable( + context: CoroutineContext = EmptyCoroutineContext, + @BuilderInference block: suspend ProducerScope.() -> Unit +): Observable = rxObservableInternal(this, context, block) diff --git a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt index 3b81445312..49a9bb8dd6 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt @@ -2,14 +2,11 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - package kotlinx.coroutines.rx2 import io.reactivex.* import kotlinx.coroutines.* import kotlin.coroutines.* -import kotlin.internal.* /** * Creates cold [single][Single] that will run a given [block] in a coroutine and emits its result. @@ -28,17 +25,6 @@ public fun rxSingle( return rxSingleInternal(GlobalScope, context, block) } -@Deprecated( - message = "CoroutineScope.rxSingle is deprecated in favour of top-level rxSingle", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("rxSingle(context, block)") -) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 -@LowPriorityInOverloadResolution -public fun CoroutineScope.rxSingle( - context: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.() -> T -): Single = rxSingleInternal(this, context, block) - private fun rxSingleInternal( scope: CoroutineScope, // support for legacy rxSingle in scope context: CoroutineContext, @@ -72,3 +58,13 @@ private class RxSingleCoroutine( } } } + +@Deprecated( + message = "CoroutineScope.rxSingle is deprecated in favour of top-level rxSingle", + level = DeprecationLevel.HIDDEN, + replaceWith = ReplaceWith("rxSingle(context, block)") +) // Since 1.3.0, will be error in 1.3.1 and hidden in 1.4.0 +public fun CoroutineScope.rxSingle( + context: CoroutineContext = EmptyCoroutineContext, + block: suspend CoroutineScope.() -> T +): Single = rxSingleInternal(this, context, block) diff --git a/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt index 540fa76b7e..4bf3e4536e 100644 --- a/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt @@ -92,7 +92,7 @@ class IntegrationTest( assertEquals(n, observable.awaitLast()) assertFailsWith { observable.awaitSingle() } checkNumbers(n, observable) - val channel = observable.openSubscription() + val channel = observable.toChannel() checkNumbers(n, channel.consumeAsFlow().asObservable(ctx(coroutineContext))) channel.cancel() } diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt index 396d19d159..2c22cbf035 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableSubscriptionSelectTest.kt @@ -20,8 +20,8 @@ class ObservableSubscriptionSelectTest : TestBase() { var a = 0 var b = 0 // open two subs - val channelA = source.openSubscription() - val channelB = source.openSubscription() + val channelA = source.toChannel() + val channelB = source.toChannel() loop@ while (true) { val done: Int = select { channelA.onReceiveCatching { result -> From 80862a5ac63c1d7d43d3a2f7c83048b3512f98c5 Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Thu, 8 Apr 2021 15:34:11 +0300 Subject: [PATCH 28/55] Fix a flaky test in MonoTest (#2635) --- .../test/MonoTest.kt | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt index 97b195c517..a98c514f19 100644 --- a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt @@ -311,6 +311,7 @@ class MonoTest : TestBase() { * this is considered normal behavior and exceptions are not propagated. */ @Test fun testDownstreamCancellationDoesNotThrow() = runTest { + var i = 0 /** Attach a hook that handles exceptions from publishers that are known to be disposed of. We don't expect it * to be fired in this case, as the reason for the publisher in this test to accept an exception is simply * cancellation from the downstream. */ @@ -319,15 +320,20 @@ class MonoTest : TestBase() { t } /** A Mono that doesn't emit a value and instead waits indefinitely. */ - val mono = mono { expect(3); delay(Long.MAX_VALUE) } - .doOnSubscribe { expect(2) } + val mono = mono(Dispatchers.Unconfined) { expect(5 * i + 3); delay(Long.MAX_VALUE) } + .doOnSubscribe { expect(5 * i + 2) } .doOnNext { expectUnreached() } .doOnSuccess { expectUnreached() } .doOnError { expectUnreached() } - .doOnCancel { expect(4) } - expect(1) - mono.awaitCancelAndJoin() - finish(5) + .doOnCancel { expect(5 * i + 4) } + val n = 1000 + repeat(n) { + i = it + expect(5 * i + 1) + mono.awaitCancelAndJoin() + expect(5 * i + 5) + } + finish(5 * n + 1) Hooks.resetOnOperatorError("testDownstreamCancellationDoesNotThrow") } @@ -335,47 +341,45 @@ class MonoTest : TestBase() { * error is propagated to [Hooks.onOperatorError]. */ @Test fun testRethrowingDownstreamCancellation() = runTest { + var i = 0 /** Attach a hook that handles exceptions from publishers that are known to be disposed of. We expect it * to be fired in this case. */ Hooks.onOperatorError("testDownstreamCancellationDoesNotThrow") { t, a -> - expect(5) + expect(i * 6 + 5) t } /** A Mono that doesn't emit a value and instead waits indefinitely, and, when cancelled, throws. */ - val mono = mono { - expect(3); + val mono = mono(Dispatchers.Unconfined) { + expect(i * 6 + 3) try { delay(Long.MAX_VALUE) } catch (e: CancellationException) { throw TestException() } } - .doOnSubscribe { expect(2) } + .doOnSubscribe { expect(i * 6 + 2) } .doOnNext { expectUnreached() } .doOnSuccess { expectUnreached() } .doOnError { expectUnreached() } - .doOnCancel { expect(4) } - expect(1) - mono.awaitCancelAndJoin() - finish(6) /** if this line fails, see the comment for [awaitCancelAndJoin] */ + .doOnCancel { expect(i * 6 + 4) } + val n = 1000 + repeat(n) { + i = it + expect(i * 6 + 1) + mono.awaitCancelAndJoin() + expect(i * 6 + 6) + } + finish(n * 6 + 1) Hooks.resetOnOperatorError("testDownstreamCancellationDoesNotThrow") } - /** Run the given [Mono], cancel it, wait for the cancellation handler to finish, and *return only then*. + /** Run the given [Publisher], cancel it, wait for the cancellation handler to finish, and return only then. * - * There are no guarantees about the execution context in which the cancellation handler will run, but we have - * to wait for it to finish to check its behavior. The contraption below seems to ensure that everything works out. - * If it stops giving that guarantee, then [testRethrowingDownstreamCancellation] should fail more or less - * consistently because the hook won't have enough time to fire before a call to [finish]. - */ - private suspend fun Mono.awaitCancelAndJoin() = coroutineScope { - val job = async(start = CoroutineStart.UNDISPATCHED) { + * Will not work in the general case, but here, when the publisher uses [Dispatchers.Unconfined], this seems to + * ensure that the cancellation handler will have nowhere to execute but serially with the cancellation. */ + private suspend fun Publisher.awaitCancelAndJoin() = coroutineScope { + async(start = CoroutineStart.UNDISPATCHED) { awaitFirstOrNull() - } - newSingleThreadContext("monoCancellationCleanup").use { pool -> - launch(pool) { - job.cancelAndJoin() - } - }.join() + }.cancelAndJoin() } } From 12f4dbc9acfdb3e8d117e6eff21a455afcf1746c Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 8 Apr 2021 19:17:09 +0300 Subject: [PATCH 29/55] Remove debug output from test --- .../jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt index 555d99fc94..ae2554ad4a 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt @@ -67,7 +67,6 @@ class StackTraceRecoveryChannelsTest : TestBase() { block() expectUnreached() } catch (e: RecoverableTestException) { - e.printStackTrace() verifyStackTrace("channels/${name.methodName}", e) } } From d905e82162b9a05cae5b8d6801a2eadbdcb1a3aa Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 13 Apr 2021 13:05:08 +0300 Subject: [PATCH 30/55] Embrace new channel API, stabilize callbackFlow (#2643) * Embrace new channel API * Introduce trySendBlocking and deprecate sendBlocking * Use it in callbackFlow example * Stabilize callbackFlow and channelFlow as they finally have error-safe API * Irrelevant: migrate from deprecated stdlib API to be able to build with Kotlin 1.5 Co-authored-by: Roman Elizarov --- .../api/kotlinx-coroutines-core.api | 1 + .../common/src/flow/Builders.kt | 11 +-- .../jvm/src/channels/Channels.kt | 77 ++++++++++++++++++- .../jvm/test/VirtualTimeSource.kt | 2 +- .../jvm/test/channels/ChannelsJvmTest.kt | 33 +++++++- .../jvm/test/flow/StateFlowStressTest.kt | 4 +- .../jvm/test/scheduling/SchedulerTestBase.kt | 4 +- .../kotlinx-coroutines-rx2/src/RxConvert.kt | 10 ++- .../kotlinx-coroutines-rx3/src/RxConvert.kt | 10 ++- 9 files changed, 128 insertions(+), 24 deletions(-) diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index cf9906fc5a..48df9648aa 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -727,6 +727,7 @@ public final class kotlinx/coroutines/channels/ChannelsKt { public static final synthetic fun toMutableList (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun toMutableSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final synthetic fun toSet (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun trySendBlocking (Lkotlinx/coroutines/channels/SendChannel;Ljava/lang/Object;)Ljava/lang/Object; public static final synthetic fun withIndex (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/channels/ReceiveChannel; public static synthetic fun withIndex$default (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lkotlinx/coroutines/channels/ReceiveChannel; public static final synthetic fun zip (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/channels/ReceiveChannel; diff --git a/kotlinx-coroutines-core/common/src/flow/Builders.kt b/kotlinx-coroutines-core/common/src/flow/Builders.kt index 3a43ccb7e0..83ae9136eb 100644 --- a/kotlinx-coroutines-core/common/src/flow/Builders.kt +++ b/kotlinx-coroutines-core/common/src/flow/Builders.kt @@ -261,7 +261,6 @@ public fun flowViaChannel( * } * ``` */ -@ExperimentalCoroutinesApi public fun channelFlow(@BuilderInference block: suspend ProducerScope.() -> Unit): Flow = ChannelFlowBuilder(block) @@ -302,11 +301,10 @@ public fun channelFlow(@BuilderInference block: suspend ProducerScope.() * override fun onNextValue(value: T) { * // To avoid blocking you can configure channel capacity using * // either buffer(Channel.CONFLATED) or buffer(Channel.UNLIMITED) to avoid overfill - * try { - * sendBlocking(value) - * } catch (e: Exception) { - * // Handle exception from the channel: failure in flow or premature closing - * } + * trySendBlocking(value) + * .onFailure { throwable -> + * // Downstream has been cancelled or failed, can log here + * } * } * override fun onApiError(cause: Throwable) { * cancel(CancellationException("API Error", cause)) @@ -327,7 +325,6 @@ public fun channelFlow(@BuilderInference block: suspend ProducerScope.() * > `awaitClose` block can be called at any time due to asynchronous nature of cancellation, even * > concurrently with the call of the callback. */ -@ExperimentalCoroutinesApi public fun callbackFlow(@BuilderInference block: suspend ProducerScope.() -> Unit): Flow = CallbackFlowBuilder(block) // ChannelFlow implementation that is the first in the chain of flow operations and introduces (builds) a flow diff --git a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt index 52adfea276..ea0b639e90 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt @@ -10,12 +10,44 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* /** - * Adds [element] into to this channel, **blocking** the caller while this channel is full, - * or throws exception if the channel [Channel.isClosedForSend] (see [Channel.close] for details). + * **Deprecated** blocking variant of send. + * This method is deprecated in the favour of [trySendBlocking]. * - * This is a way to call [Channel.send] method inside a blocking code using [runBlocking], - * so this function should not be used from coroutine. + * `sendBlocking` is a dangerous primitive — it throws an exception + * if the channel was closed or, more commonly, cancelled. + * Cancellation exceptions in non-blocking code are unexpected and frequently + * trigger internal failures. + * + * These bugs are hard-to-spot during code review and they forced users to write + * their own wrappers around `sendBlocking`. + * So this function is deprecated and replaced with a more explicit primitive. + * + * The real-world example of broken usage with Firebase: + * + * ```kotlin + * callbackFlow { + * val listener = object : ValueEventListener { + * override fun onDataChange(snapshot: DataSnapshot) { + * // This line may fail and crash the app when the downstream flow is cancelled + * sendBlocking(DataSnapshot(snapshot)) + * } + * + * override fun onCancelled(error: DatabaseError) { + * close(error.toException()) + * } + * } + * + * firebaseQuery.addValueEventListener(listener) + * awaitClose { firebaseQuery.removeEventListener(listener) } + * } + * ``` */ +@Deprecated( + level = DeprecationLevel.WARNING, + message = "Deprecated in the favour of 'trySendBlocking'. " + + "Consider handling the result of 'trySendBlocking' explicitly and rethrow exception if necessary", + replaceWith = ReplaceWith("trySendBlocking(element)") +) public fun SendChannel.sendBlocking(element: E) { // fast path if (offer(element)) @@ -25,3 +57,40 @@ public fun SendChannel.sendBlocking(element: E) { send(element) } } + +/** + * Adds [element] into to this channel, **blocking** the caller while this channel is full, + * and returning either [successful][ChannelResult.isSuccess] result when the element was added, or + * failed result representing closed channel with a corresponding exception. + * + * This is a way to call [Channel.send] method in a safe manner inside a blocking code using [runBlocking] and catching, + * so this function should not be used from coroutine. + * + * Example of usage: + * + * ``` + * // From callback API + * channel.trySendBlocking(element) + * .onSuccess { /* request next element or debug log */ } + * .onFailure { t: Throwable? -> /* throw or log */ } + * ``` + * + * For this operation it is guaranteed that [failure][ChannelResult.failed] always contains an exception in it. + * + * @throws [InterruptedException] if the current thread is interrupted during the blocking send operation. + */ +@Throws(InterruptedException::class) +public fun SendChannel.trySendBlocking(element: E): ChannelResult { + /* + * Sent successfully -- bail out. + * But failure may indicate either that the channel it full or that + * it is close. Go to slow path on failure to simplify the successful path and + * to materialize default exception. + */ + trySend(element).onSuccess { return ChannelResult.success(Unit) } + return runBlocking { + val r = runCatching { send(element) } + if (r.isSuccess) ChannelResult.success(Unit) + else ChannelResult.closed(r.exceptionOrNull()) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/VirtualTimeSource.kt b/kotlinx-coroutines-core/jvm/test/VirtualTimeSource.kt index ca399f5325..d2dcfd2f89 100644 --- a/kotlinx-coroutines-core/jvm/test/VirtualTimeSource.kt +++ b/kotlinx-coroutines-core/jvm/test/VirtualTimeSource.kt @@ -142,7 +142,7 @@ internal class VirtualTimeSource( } private fun minParkedTill(): Long = - threads.values.map { if (it.permit) NOT_PARKED else it.parkedTill }.min() ?: NOT_PARKED + threads.values.map { if (it.permit) NOT_PARKED else it.parkedTill }.minOrNull() ?: NOT_PARKED @Synchronized fun shutdown() { diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt index da20f0c5ee..2e4ba9ac0e 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt @@ -11,7 +11,7 @@ import kotlin.test.* class ChannelsJvmTest : TestBase() { @Test - fun testBlocking() { + fun testTrySendBlocking() { val ch = Channel() val sum = GlobalScope.async { var sum = 0 @@ -19,9 +19,38 @@ class ChannelsJvmTest : TestBase() { sum } repeat(10) { - ch.sendBlocking(it) + assertTrue(ch.trySendBlocking(it).isSuccess) } ch.close() assertEquals(45, runBlocking { sum.await() }) } + + // Uncomment lines when migrated to 1.5, these are bugs in inline classes codegen + @Test + fun testTrySendBlockingClosedChannel() { + run { + val channel = Channel().also { it.close() } + channel.trySendBlocking(Unit) + .onSuccess { expectUnreached() } + .onFailure { assertTrue(it is ClosedSendChannelException) } +// .also { assertTrue { it.isClosed } } + } + + run { + val channel = Channel().also { it.close(TestException()) } + channel.trySendBlocking(Unit) + .onSuccess { expectUnreached() } + .onFailure { assertTrue(it is TestException) } +// .also { assertTrue { it.isClosed } } + } + + run { + val channel = Channel().also { it.cancel(TestCancellationException()) } + channel.trySendBlocking(Unit) + .onSuccess { expectUnreached() } + .onFailure { assertTrue(it is TestCancellationException) } +// .also { assertTrue { it.isClosed } } + } + } + } diff --git a/kotlinx-coroutines-core/jvm/test/flow/StateFlowStressTest.kt b/kotlinx-coroutines-core/jvm/test/flow/StateFlowStressTest.kt index dc3cd43c93..e55eaad127 100644 --- a/kotlinx-coroutines-core/jvm/test/flow/StateFlowStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/flow/StateFlowStressTest.kt @@ -62,7 +62,7 @@ class StateFlowStressTest : TestBase() { for (second in 1..nSeconds) { delay(1000) val cs = collected.map { it.sum() } - println("$second: emitted=${emitted.sum()}, collected=${cs.min()}..${cs.max()}") + println("$second: emitted=${emitted.sum()}, collected=${cs.minOrNull()}..${cs.maxOrNull()}") } emitters.cancelAndJoin() collectors.cancelAndJoin() @@ -77,4 +77,4 @@ class StateFlowStressTest : TestBase() { @Test fun testTenEmittersAndCollectors() = stress(10, 10) -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt b/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt index bfabf5b2f3..72d7c90882 100644 --- a/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt +++ b/kotlinx-coroutines-core/jvm/test/scheduling/SchedulerTestBase.kt @@ -49,7 +49,7 @@ abstract class SchedulerTestBase : TestBase() { private fun maxSequenceNumber(): Int? { return Thread.getAllStackTraces().keys.asSequence().filter { it is CoroutineScheduler.Worker } - .map { sequenceNumber(it.name) }.max() + .map { sequenceNumber(it.name) }.maxOrNull() } private fun sequenceNumber(threadName: String): Int { @@ -105,4 +105,4 @@ abstract class SchedulerTestBase : TestBase() { } } } -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt index 14c24942ac..a64e6d02ed 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt @@ -82,10 +82,14 @@ public fun ObservableSource.asFlow(): Flow = callbackFlow { override fun onComplete() { close() } override fun onSubscribe(d: Disposable) { if (!disposableRef.compareAndSet(null, d)) d.dispose() } override fun onNext(t: T) { + /* + * Channel was closed by the downstream, so the exception (if any) + * also was handled by the same downstream + */ try { - sendBlocking(t) - } catch (ignored: Throwable) { // TODO: Replace when this issue is fixed: https://github.com/Kotlin/kotlinx.coroutines/issues/974 - // Is handled by the downstream flow + trySendBlocking(t) + } catch (e: InterruptedException) { + // RxJava interrupts the source } } override fun onError(e: Throwable) { close(e) } diff --git a/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt b/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt index 63e30f2617..b0ba4ab2e0 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt @@ -82,10 +82,14 @@ public fun ObservableSource.asFlow(): Flow = callbackFlow { override fun onComplete() { close() } override fun onSubscribe(d: Disposable) { if (!disposableRef.compareAndSet(null, d)) d.dispose() } override fun onNext(t: T) { + /* + * Channel was closed by the downstream, so the exception (if any) + * also was handled by the same downstream + */ try { - sendBlocking(t) - } catch (ignored: Throwable) { // TODO: Replace when this issue is fixed: https://github.com/Kotlin/kotlinx.coroutines/issues/974 - // Is handled by the downstream flow + trySendBlocking(t) + } catch (e: InterruptedException) { + // RxJava interrupts the source } } override fun onError(e: Throwable) { close(e) } From df16bbf8d6dd3031a6a402b39ef06d0d27d6cdec Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Tue, 13 Apr 2021 15:00:58 +0300 Subject: [PATCH 31/55] Better NonCancellable docs (#2633) --- kotlinx-coroutines-core/common/src/NonCancellable.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kotlinx-coroutines-core/common/src/NonCancellable.kt b/kotlinx-coroutines-core/common/src/NonCancellable.kt index a9c68f090a..5d3644ba91 100644 --- a/kotlinx-coroutines-core/common/src/NonCancellable.kt +++ b/kotlinx-coroutines-core/common/src/NonCancellable.kt @@ -18,6 +18,11 @@ import kotlin.coroutines.* * // this code will not be cancelled * } * ``` + * + * **WARNING**: This object is not designed to be used with [launch], [async], and other coroutine builders. + * if you write `launch(NonCancellable) { ... }` then not only the newly launched job will not be cancelled + * when the parent is cancelled, the whole parent-child relation between parent and child is severed. + * The parent will not wait for the child's completion, nor will be cancelled when the child crashed. */ public object NonCancellable : AbstractCoroutineContextElement(Job), Job { /** From 7b1f3b379f1863521a26a7d3086b943d7fe3b58e Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 13 Apr 2021 16:39:26 +0300 Subject: [PATCH 32/55] Introduce runningFold operator, make scan alias to runningFold (#2645) * Otherwise Kotlin users with non-reactive background get confused by flow/stdlib inconsistency * Make it experimental to delay the final decision about the name Fixes #2641 --- .../api/kotlinx-coroutines-core.api | 1 + .../common/src/flow/operators/Transform.kt | 18 ++++++++++++++++-- .../common/test/flow/operators/ScanTest.kt | 7 +++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 48df9648aa..23f29b02a3 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -994,6 +994,7 @@ public final class kotlinx/coroutines/flow/FlowKt { public static synthetic fun retry$default (Lkotlinx/coroutines/flow/Flow;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; public static synthetic fun retry$default (Lkotlinx/coroutines/flow/Flow;JLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; public static final fun retryWhen (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function4;)Lkotlinx/coroutines/flow/Flow; + public static final fun runningFold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow; public static final fun runningReduce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow; public static final fun sample (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow; public static final fun sample-HG0u8IE (Lkotlinx/coroutines/flow/Flow;D)Lkotlinx/coroutines/flow/Flow; diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt index 80a2cf27d4..5929886495 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Transform.kt @@ -74,6 +74,20 @@ public fun Flow.onEach(action: suspend (T) -> Unit): Flow = transform return@transform emit(value) } +/** + * Folds the given flow with [operation], emitting every intermediate result, including [initial] value. + * Note that initial value should be immutable (or should not be mutated) as it is shared between different collectors. + * For example: + * ``` + * flowOf(1, 2, 3).scan(emptyList()) { acc, value -> acc + value }.toList() + * ``` + * will produce `[], [1], [1, 2], [1, 2, 3]]`. + * + * This function is an alias to [runningFold] operator. + */ +@ExperimentalCoroutinesApi +public fun Flow.scan(initial: R, @BuilderInference operation: suspend (accumulator: R, value: T) -> R): Flow = runningFold(initial, operation) + /** * Folds the given flow with [operation], emitting every intermediate result, including [initial] value. * Note that initial value should be immutable (or should not be mutated) as it is shared between different collectors. @@ -84,7 +98,7 @@ public fun Flow.onEach(action: suspend (T) -> Unit): Flow = transform * will produce `[], [1], [1, 2], [1, 2, 3]]`. */ @ExperimentalCoroutinesApi -public fun Flow.scan(initial: R, @BuilderInference operation: suspend (accumulator: R, value: T) -> R): Flow = flow { +public fun Flow.runningFold(initial: R, @BuilderInference operation: suspend (accumulator: R, value: T) -> R): Flow = flow { var accumulator: R = initial emit(accumulator) collect { value -> @@ -100,7 +114,7 @@ public fun Flow.scan(initial: R, @BuilderInference operation: suspend * * For example: * ``` - * flowOf(1, 2, 3, 4).runningReduce { (v1, v2) -> v1 + v2 }.toList() + * flowOf(1, 2, 3, 4).runningReduce { acc, value -> acc + value }.toList() * ``` * will produce `[1, 3, 6, 10]` */ diff --git a/kotlinx-coroutines-core/common/test/flow/operators/ScanTest.kt b/kotlinx-coroutines-core/common/test/flow/operators/ScanTest.kt index 20e07873bb..c6be36d01e 100644 --- a/kotlinx-coroutines-core/common/test/flow/operators/ScanTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/operators/ScanTest.kt @@ -23,6 +23,13 @@ class ScanTest : TestBase() { assertEquals(listOf(emptyList(), listOf(1), listOf(1, 2), listOf(1, 2, 3)), result) } + @Test + fun testFoldWithInitial() = runTest { + val flow = flowOf(1, 2, 3) + val result = flow.runningFold(emptyList()) { acc, value -> acc + value }.toList() + assertEquals(listOf(emptyList(), listOf(1), listOf(1, 2), listOf(1, 2, 3)), result) + } + @Test fun testNulls() = runTest { val flow = flowOf(null, 2, null, null, null, 5) From 3c83c0cfea619f68f1eb323773d1f057f294023f Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 13 Apr 2021 16:47:55 +0300 Subject: [PATCH 33/55] Deprecate SendChannel.offer and ReceiveChannel.poll, replace their usages along the codebase (#2644) * Deprecate SendChannel.offer and replace its usages along the codebase * Deprecate ReceiveChannel.poll and replace its usages along the codebase Co-authored-by: Roman Elizarov Addresses #974 --- .../common/src/channels/AbstractChannel.kt | 1 + .../common/src/channels/Channel.kt | 191 ++++++++++-------- .../src/channels/ConflatedBroadcastChannel.kt | 2 +- .../common/src/channels/ConflatedChannel.kt | 4 +- .../common/src/channels/LinkedListChannel.kt | 2 +- .../common/src/flow/SharedFlow.kt | 2 +- .../common/src/flow/StateFlow.kt | 2 +- .../common/src/flow/internal/Combine.kt | 2 +- .../common/test/channels/ArrayChannelTest.kt | 20 +- .../test/channels/BasicOperationsTest.kt | 45 +---- .../channels/ChannelBufferOverflowTest.kt | 26 +-- .../channels/ConflatedBroadcastChannelTest.kt | 2 +- .../test/channels/ConflatedChannelTest.kt | 14 +- .../test/channels/LinkedListChannelTest.kt | 4 +- .../test/channels/RendezvousChannelTest.kt | 12 +- .../flow/channels/ChannelBuildersFlowTest.kt | 12 +- .../test/flow/channels/ChannelFlowTest.kt | 22 +- .../test/flow/sharing/ShareInFusionTest.kt | 4 +- .../common/test/flow/sharing/ShareInTest.kt | 6 +- .../jvm/src/channels/Channels.kt | 2 +- .../jvm/src/channels/TickerChannels.kt | 8 +- .../channels/testCancelledOffer.txt | 10 - .../ReusableCancellableContinuationTest.kt | 2 +- .../jvm/test/RunInterruptibleTest.kt | 2 +- ...annelCancelUndeliveredElementStressTest.kt | 30 +-- .../ChannelUndeliveredElementStressTest.kt | 2 +- ...nflatedBroadcastChannelNotifyStressTest.kt | 6 +- .../ConflatedChannelCloseStressTest.kt | 12 +- .../test/channels/TickerChannelCommonTest.kt | 8 +- .../StackTraceRecoveryChannelsTest.kt | 19 -- .../test/exceptions/StackTraceRecoveryTest.kt | 2 +- .../jvm/test/flow/CallbackFlowTest.kt | 6 +- .../jvm/test/lincheck/ChannelsLincheckTest.kt | 30 +-- .../test/PublisherAsFlowTest.kt | 2 +- .../src/Channel.kt | 2 +- .../src/Publish.kt | 2 +- .../src/ReactiveFlow.kt | 2 +- .../test/PublisherAsFlowTest.kt | 2 +- .../kotlinx-coroutines-rx2/src/RxChannel.kt | 4 +- .../src/RxObservable.kt | 2 +- .../test/ObservableSourceAsFlowStressTest.kt | 2 +- .../kotlinx-coroutines-rx3/src/RxChannel.kt | 4 +- .../src/RxObservable.kt | 2 +- .../test/ObservableSourceAsFlowStressTest.kt | 2 +- ui/coroutines-guide-ui.md | 18 +- .../src/JavaFxConvert.kt | 15 +- .../test/JavaFxObservableAsFlowTest.kt | 16 ++ .../test/guide/example-ui-actor-02.kt | 2 +- .../test/guide/example-ui-actor-03.kt | 4 +- .../test/guide/example-ui-blocking-01.kt | 2 +- .../test/guide/example-ui-blocking-02.kt | 2 +- .../test/guide/example-ui-blocking-03.kt | 2 +- 52 files changed, 289 insertions(+), 310 deletions(-) delete mode 100644 kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt index 52e47227ad..bcf1921594 100644 --- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt @@ -137,6 +137,7 @@ internal abstract class AbstractSendChannel( } override fun offer(element: E): Boolean { + // Temporary migration for offer users who rely on onUndeliveredElement try { return super.offer(element) } catch (e: Throwable) { diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 7b6cc0ad9d..f006efc720 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -24,7 +24,7 @@ import kotlin.jvm.* public interface SendChannel { /** * Returns `true` if this channel was closed by an invocation of [close]. This means that - * calling [send] or [offer] will result in an exception. + * calling [send] will result in an exception. * * **Note: This is an experimental api.** This property may change its semantics and/or name in the future. */ @@ -51,7 +51,7 @@ public interface SendChannel { * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. * * This function can be used in [select] invocations with the [onSend] clause. - * Use [offer] to try sending to this channel without waiting. + * Use [trySend] to try sending to this channel without waiting. */ public suspend fun send(element: E) @@ -64,23 +64,6 @@ public interface SendChannel { */ public val onSend: SelectClause2> - /** - * Immediately adds the specified [element] to this channel, if this doesn't violate its capacity restrictions, - * and returns `true`. Otherwise, just returns `false`. This is a synchronous variant of [send] which backs off - * in situations when `send` suspends. - * - * Throws an exception if the channel [is closed for `send`][isClosedForSend] (see [close] for details). - * - * When `offer` call returns `false` it guarantees that the element was not delivered to the consumer and it - * it does not call `onUndeliveredElement` that was installed for this channel. If the channel was closed, - * then it calls `onUndeliveredElement` before throwing an exception. - * See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements. - */ - public fun offer(element: E): Boolean { - val result = trySend(element) - if (result.isSuccess) return true - throw recoverStackTrace(result.exceptionOrNull() ?: return false) - } /** * Immediately adds the specified [element] to this channel, if this doesn't violate its capacity restrictions, @@ -103,7 +86,7 @@ public interface SendChannel { * on the side of [ReceiveChannel] starts returning `true` only after all previously sent elements * are received. * - * A channel that was closed without a [cause] throws a [ClosedSendChannelException] on attempts to [send] or [offer] + * A channel that was closed without a [cause] throws a [ClosedSendChannelException] on attempts to [send] * and [ClosedReceiveChannelException] on attempts to [receive][ReceiveChannel.receive]. * A channel that was closed with non-null [cause] is called a _failed_ channel. Attempts to send or * receive on a failed channel throw the specified [cause] exception. @@ -122,10 +105,11 @@ public interface SendChannel { * * the cause of `close` or `cancel` otherwise. * * Example of usage (exception handling is omitted): + * * ``` * val events = Channel(UNLIMITED) * callbackBasedApi.registerCallback { event -> - * events.offer(event) + * events.trySend(event) * } * * val uiUpdater = launch(Dispatchers.Main, parent = UILifecycle) { @@ -134,7 +118,6 @@ public interface SendChannel { * } * * events.invokeOnClose { callbackBasedApi.stop() } - * * ``` * * **Note: This is an experimental api.** This function may change its semantics, parameters or return type in the future. @@ -146,6 +129,33 @@ public interface SendChannel { */ @ExperimentalCoroutinesApi public fun invokeOnClose(handler: (cause: Throwable?) -> Unit) + + /** + * **Deprecated** offer method. + * + * This method was deprecated in the favour of [trySend]. + * It has proven itself as the most error-prone method in Channel API: + * + * * `Boolean` return type creates the false sense of security, implying that `false` + * is returned instead of throwing an exception. + * * It was used mostly from non-suspending APIs where CancellationException triggered + * internal failures in the application (the most common source of bugs). + * * Due to signature and explicit `if (ch.offer(...))` checks it was easy to + * oversee such error during code review. + * * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead. + * + * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context. + */ + @Deprecated( + level = DeprecationLevel.WARNING, + message = "Deprecated in the favour of 'trySend' method", + replaceWith = ReplaceWith("trySend(element).isSuccess") + ) // Since 1.5.0 + public fun offer(element: E): Boolean { + val result = trySend(element) + if (result.isSuccess) return true + throw recoverStackTrace(result.exceptionOrNull() ?: return false) + } } /** @@ -188,7 +198,7 @@ public interface ReceiveChannel { * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. * * This function can be used in [select] invocations with the [onReceive] clause. - * Use [poll] to try receiving from this channel without waiting. + * Use [tryReceive] to try receiving from this channel without waiting. */ public suspend fun receive(): E @@ -200,51 +210,6 @@ public interface ReceiveChannel { */ public val onReceive: SelectClause1 - /** - * This function was deprecated since 1.3.0 and is no longer recommended to use - * or to implement in subclasses. - * - * It had the following pitfalls: - * - Didn't allow to distinguish 'null' as "closed channel" from "null as a value" - * - Was throwing if the channel has failed even though its signature may suggest it returns 'null' - * - It didn't really belong to core channel API and can be exposed as an extension instead. - * - * @suppress doc - */ - @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - @LowPriorityInOverloadResolution - @Deprecated( - message = "Deprecated in favor of receiveCatching", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("receiveCatching().getOrNull()") - ) // Warning since 1.3.0, error in 1.5.0, will be hidden in 1.6.0 - public suspend fun receiveOrNull(): E? = receiveCatching().getOrNull() - - /** - * This function was deprecated since 1.3.0 and is no longer recommended to use - * or to implement in subclasses. - * See [receiveOrNull] documentation. - * - * @suppress **Deprecated**: in favor of onReceiveCatching extension. - */ - @Deprecated( - message = "Deprecated in favor of onReceiveCatching extension", - level = DeprecationLevel.ERROR, - replaceWith = ReplaceWith("onReceiveCatching") - ) // Warning since 1.3.0, error in 1.5.0, will be hidden or removed in 1.6.0 - public val onReceiveOrNull: SelectClause1 - get() { - return object : SelectClause1 { - @InternalCoroutinesApi - override fun registerSelectClause1(select: SelectInstance, block: suspend (E?) -> R) { - onReceiveCatching.registerSelectClause1(select) { - it.exceptionOrNull()?.let { throw it } - block(it.getOrNull()) - } - } - } - } - /** * Retrieves and removes an element from this channel if it's not empty, or suspends the caller while this channel is empty. * This method returns [ChannelResult] with the value of an element successfully retrieved from the channel @@ -262,7 +227,7 @@ public interface ReceiveChannel { * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed. * * This function can be used in [select] invocations with the [onReceiveCatching] clause. - * Use [poll] to try receiving from this channel without waiting. + * Use [tryReceive] to try receiving from this channel without waiting. */ public suspend fun receiveCatching(): ChannelResult @@ -273,17 +238,6 @@ public interface ReceiveChannel { */ public val onReceiveCatching: SelectClause1> - /** - * Retrieves and removes an element from this channel if it's not empty or returns `null` if the channel is empty - * or is [is closed for `receive`][isClosedForReceive] without a cause. - * It throws the original [close][SendChannel.close] cause exception if the channel has _failed_. - */ - public fun poll(): E? { - val result = tryReceive() - if (result.isSuccess) return result.getOrThrow() - throw recoverStackTrace(result.exceptionOrNull() ?: return null) - } - /** * Retrieves and removes an element from this channel if it's not empty, returning a [successful][ChannelResult.success] * result, returns [failed][ChannelResult.failed] result if the channel is empty, and [closed][ChannelResult.closed] @@ -325,6 +279,75 @@ public interface ReceiveChannel { */ @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") public fun cancel(cause: Throwable? = null): Boolean + + /** + * **Deprecated** poll method. + * + * This method was deprecated in the favour of [tryReceive]. + * It has proven itself as error-prone method in Channel API: + * + * * Nullable return type creates the false sense of security, implying that `null` + * is returned instead of throwing an exception. + * * It was used mostly from non-suspending APIs where CancellationException triggered + * internal failures in the application (the most common source of bugs). + * * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead. + * + * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context. + */ + @Deprecated(level = DeprecationLevel.WARNING, + message = "Deprecated in the favour of 'tryReceive'", + replaceWith = ReplaceWith("tryReceive().getOrNull()") + ) // Since 1.5.0 + public fun poll(): E? { + val result = tryReceive() + if (result.isSuccess) return result.getOrThrow() + throw recoverStackTrace(result.exceptionOrNull() ?: return null) + } + + /** + * This function was deprecated since 1.3.0 and is no longer recommended to use + * or to implement in subclasses. + * + * It had the following pitfalls: + * - Didn't allow to distinguish 'null' as "closed channel" from "null as a value" + * - Was throwing if the channel has failed even though its signature may suggest it returns 'null' + * - It didn't really belong to core channel API and can be exposed as an extension instead. + * + * @suppress doc + */ + @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") + @LowPriorityInOverloadResolution + @Deprecated( + message = "Deprecated in favor of receiveCatching", + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("receiveCatching().getOrNull()") + ) // Warning since 1.3.0, error in 1.5.0, will be hidden in 1.6.0 + public suspend fun receiveOrNull(): E? = receiveCatching().getOrNull() + + /** + * This function was deprecated since 1.3.0 and is no longer recommended to use + * or to implement in subclasses. + * See [receiveOrNull] documentation. + * + * @suppress **Deprecated**: in favor of onReceiveCatching extension. + */ + @Deprecated( + message = "Deprecated in favor of onReceiveCatching extension", + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("onReceiveCatching") + ) // Warning since 1.3.0, error in 1.5.0, will be hidden or removed in 1.6.0 + public val onReceiveOrNull: SelectClause1 + get() { + return object : SelectClause1 { + @InternalCoroutinesApi + override fun registerSelectClause1(select: SelectInstance, block: suspend (E?) -> R) { + onReceiveCatching.registerSelectClause1(select) { + it.exceptionOrNull()?.let { throw it } + block(it.getOrNull()) + } + } + } + } } /** @@ -544,14 +567,14 @@ public interface ChannelIterator { * * * When `capacity` is [Channel.UNLIMITED] — it creates a channel with effectively unlimited buffer. * This channel has a linked-list buffer of unlimited capacity (limited only by available memory). - * [Sending][send] to this channel never suspends, and [offer] always returns `true`. + * [Sending][send] to this channel never suspends, and [trySend] always succeeds. * * * When `capacity` is [Channel.CONFLATED] — it creates a _conflated_ channel - * This channel buffers at most one element and conflates all subsequent `send` and `offer` invocations, + * This channel buffers at most one element and conflates all subsequent `send` and `trySend` invocations, * so that the receiver always gets the last element sent. * Back-to-back sent elements are conflated — only the last sent element is received, * while previously sent elements **are lost**. - * [Sending][send] to this channel never suspends, and [offer] always returns `true`. + * [Sending][send] to this channel never suspends, and [trySend] always succeeds. * * * When `capacity` is positive but less than [UNLIMITED] — it creates an array-based channel with the specified capacity. * This channel has an array buffer of a fixed `capacity`. @@ -598,8 +621,6 @@ public interface ChannelIterator { * * * When [send][SendChannel.send] operation throws an exception because it was cancelled before it had a chance to actually * send the element or because the channel was [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel]. - * * When [offer][SendChannel.offer] operation throws an exception when - * the channel was [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel]. * * When [receive][ReceiveChannel.receive], [receiveOrNull][ReceiveChannel.receiveOrNull], or [hasNext][ChannelIterator.hasNext] * operation throws an exception when it had retrieved the element from the * channel but was cancelled before the code following the receive call resumed. diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt index c84afb2fe0..8283bcb166 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt @@ -17,7 +17,7 @@ import kotlin.jvm.* * Back-to-send sent elements are _conflated_ -- only the 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 [offer] always returns `true`. + * 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. diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt index 0e686447c1..e1e2b140f5 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt @@ -9,11 +9,11 @@ import kotlinx.coroutines.internal.* import kotlinx.coroutines.selects.* /** - * Channel that buffers at most one element and conflates all subsequent `send` and `offer` invocations, + * Channel that buffers at most one element and conflates all subsequent `send` and `trySend` invocations, * so that the receiver always gets the most recently sent element. * Back-to-send sent elements are _conflated_ -- only the most recently sent element is received, * while previously sent elements **are lost**. - * Sender to this channel never suspends and [offer] always returns `true`. + * Sender to this channel never suspends and [trySend] always succeeds. * * This channel is created by `Channel(Channel.CONFLATED)` factory function invocation. */ diff --git a/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt b/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt index 831752739c..b5f607b230 100644 --- a/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.selects.* /** * Channel with linked-list buffer of a unlimited capacity (limited only by available memory). - * Sender to this channel never suspends and [offer] always returns `true`. + * Sender to this channel never suspends and [trySend] always succeeds. * * This channel is created by `Channel(Channel.UNLIMITED)` factory function invocation. * diff --git a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt index ac9e66b0d5..490b221b58 100644 --- a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt @@ -92,7 +92,7 @@ import kotlin.native.concurrent.* * * 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 - * values to new subscribers). Replace [send][BroadcastChannel.send] and [offer][BroadcastChannel.offer] calls + * values to new subscribers). Replace [send][BroadcastChannel.send] and [trySend][BroadcastChannel.trySend] calls * with [emit][MutableStateFlow.emit] and [tryEmit][MutableStateFlow.tryEmit], and convert subscribers' code to flow operators. * * ### Concurrency diff --git a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt index fc8aa02f20..74d3314075 100644 --- a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt @@ -107,7 +107,7 @@ import kotlin.native.concurrent.* * * To migrate [ConflatedBroadcastChannel] usage to [StateFlow], start by replacing usages of the `ConflatedBroadcastChannel()` * constructor with `MutableStateFlow(initialValue)`, using `null` as an initial value if you don't have one. - * Replace [send][ConflatedBroadcastChannel.send] and [offer][ConflatedBroadcastChannel.offer] calls + * Replace [send][ConflatedBroadcastChannel.send] and [trySend][ConflatedBroadcastChannel.trySend] calls * with updates to the state flow's [MutableStateFlow.value], and convert subscribers' code to flow operators. * You can use the [filterNotNull] operator to mimic behavior of a `ConflatedBroadcastChannel` without initial value. * diff --git a/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt b/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt index 6e5f3f11aa..c924c09025 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/Combine.kt @@ -65,7 +65,7 @@ internal suspend fun FlowCollector.combineInternal( // Received the second value from the same flow in the same epoch -- bail out if (lastReceivedEpoch[index] == currentEpoch) break lastReceivedEpoch[index] = currentEpoch - element = resultChannel.poll() ?: break + element = resultChannel.tryReceive().getOrNull() ?: break } // Process batch result if there is enough data diff --git a/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt index 3900c2db90..632fd2928b 100644 --- a/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt @@ -86,31 +86,31 @@ class ArrayChannelTest : TestBase() { } @Test - fun testOfferAndPoll() = runTest { + fun testTryOp() = runTest { val q = Channel(1) - assertTrue(q.offer(1)) + assertTrue(q.trySend(1).isSuccess) expect(1) launch { expect(3) - assertEquals(1, q.poll()) + assertEquals(1, q.tryReceive().getOrNull()) expect(4) - assertNull(q.poll()) + assertNull(q.tryReceive().getOrNull()) expect(5) assertEquals(2, q.receive()) // suspends expect(9) - assertEquals(3, q.poll()) + assertEquals(3, q.tryReceive().getOrNull()) expect(10) - assertNull(q.poll()) + assertNull(q.tryReceive().getOrNull()) expect(11) } expect(2) yield() expect(6) - assertTrue(q.offer(2)) + assertTrue(q.trySend(2).isSuccess) expect(7) - assertTrue(q.offer(3)) + assertTrue(q.trySend(3).isSuccess) expect(8) - assertFalse(q.offer(4)) + assertFalse(q.trySend(4).isSuccess) yield() finish(12) } @@ -157,7 +157,7 @@ class ArrayChannelTest : TestBase() { val capacity = 42 val channel = Channel(capacity) repeat(4) { - channel.offer(-1) + channel.trySend(-1) } repeat(4) { channel.receiveCatching().getOrNull() diff --git a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt index a64284aec1..8962acc3dc 100644 --- a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt @@ -15,8 +15,8 @@ class BasicOperationsTest : TestBase() { } @Test - fun testOfferAfterClose() = runTest { - TestChannelKind.values().forEach { kind -> testOffer(kind) } + fun testTrySendAfterClose() = runTest { + TestChannelKind.values().forEach { kind -> testTrySend(kind) } } @Test @@ -39,7 +39,7 @@ class BasicOperationsTest : TestBase() { } } expect(1) - channel.offer(42) + channel.trySend(42) expect(2) channel.close(AssertionError()) finish(4) @@ -80,28 +80,6 @@ class BasicOperationsTest : TestBase() { } } - private suspend fun testReceiveCatchingException(kind: TestChannelKind) = coroutineScope { - val channel = kind.create() - val d = async(NonCancellable) { - channel.receive() - } - - yield() - channel.close(TestException()) - assertTrue(channel.isClosedForReceive) - - assertFailsWith { channel.poll() } - try { - channel.receiveCatching().getOrThrow() - expectUnreached() - } catch (e: TestException) { - // Expected - } - - d.join() - assertTrue(d.getCancellationException().cause is TestException) - } - @Suppress("ReplaceAssertBooleanWithAssertEquality") private suspend fun testReceiveCatching(kind: TestChannelKind) = coroutineScope { reset() @@ -131,22 +109,21 @@ class BasicOperationsTest : TestBase() { finish(6) } - private suspend fun testOffer(kind: TestChannelKind) = coroutineScope { + private suspend fun testTrySend(kind: TestChannelKind) = coroutineScope { val channel = kind.create() val d = async { channel.send(42) } yield() channel.close() assertTrue(channel.isClosedForSend) - try { - channel.offer(2) - fail() - } catch (e: ClosedSendChannelException) { - if (!kind.isConflated) { - assertEquals(42, channel.receive()) + channel.trySend(2) + .onSuccess { expectUnreached() } + .onFailure { + assertTrue { it is ClosedSendChannelException} + if (!kind.isConflated) { + assertEquals(42, channel.receive()) + } } - } - d.await() } diff --git a/kotlinx-coroutines-core/common/test/channels/ChannelBufferOverflowTest.kt b/kotlinx-coroutines-core/common/test/channels/ChannelBufferOverflowTest.kt index 41f60479f2..0b9a0fdbe4 100644 --- a/kotlinx-coroutines-core/common/test/channels/ChannelBufferOverflowTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ChannelBufferOverflowTest.kt @@ -11,30 +11,30 @@ class ChannelBufferOverflowTest : TestBase() { @Test fun testDropLatest() = runTest { val c = Channel(2, BufferOverflow.DROP_LATEST) - assertTrue(c.offer(1)) - assertTrue(c.offer(2)) - assertTrue(c.offer(3)) // overflows, dropped + assertTrue(c.trySend(1).isSuccess) + assertTrue(c.trySend(2).isSuccess) + assertTrue(c.trySend(3).isSuccess) // overflows, dropped c.send(4) // overflows dropped assertEquals(1, c.receive()) - assertTrue(c.offer(5)) - assertTrue(c.offer(6)) // overflows, dropped + assertTrue(c.trySend(5).isSuccess) + assertTrue(c.trySend(6).isSuccess) // overflows, dropped assertEquals(2, c.receive()) assertEquals(5, c.receive()) - assertEquals(null, c.poll()) + assertEquals(null, c.tryReceive().getOrNull()) } @Test fun testDropOldest() = runTest { val c = Channel(2, BufferOverflow.DROP_OLDEST) - assertTrue(c.offer(1)) - assertTrue(c.offer(2)) - assertTrue(c.offer(3)) // overflows, keeps 2, 3 + assertTrue(c.trySend(1).isSuccess) + assertTrue(c.trySend(2).isSuccess) + assertTrue(c.trySend(3).isSuccess) // overflows, keeps 2, 3 c.send(4) // overflows, keeps 3, 4 assertEquals(3, c.receive()) - assertTrue(c.offer(5)) - assertTrue(c.offer(6)) // overflows, keeps 5, 6 + assertTrue(c.trySend(5).isSuccess) + assertTrue(c.trySend(6).isSuccess) // overflows, keeps 5, 6 assertEquals(5, c.receive()) assertEquals(6, c.receive()) - assertEquals(null, c.poll()) + assertEquals(null, c.tryReceive().getOrNull()) } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt index 856a66fbd2..a8c2a29c1b 100644 --- a/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ConflatedBroadcastChannelTest.kt @@ -42,7 +42,7 @@ class ConflatedBroadcastChannelTest : TestBase() { launch(start = CoroutineStart.UNDISPATCHED) { expect(2) val sub = broadcast.openSubscription() - assertNull(sub.poll()) + assertNull(sub.tryReceive().getOrNull()) expect(3) assertEquals("one", sub.receive()) // suspends expect(6) diff --git a/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt index 87194f72f9..370fd5b9a2 100644 --- a/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt @@ -12,14 +12,14 @@ open class ConflatedChannelTest : TestBase() { Channel(Channel.CONFLATED) @Test - fun testBasicConflationOfferPoll() { + fun testBasicConflationOfferTryReceive() { val q = createConflatedChannel() - assertNull(q.poll()) - assertTrue(q.offer(1)) - assertTrue(q.offer(2)) - assertTrue(q.offer(3)) - assertEquals(3, q.poll()) - assertNull(q.poll()) + assertNull(q.tryReceive().getOrNull()) + assertTrue(q.trySend(1).isSuccess) + assertTrue(q.trySend(2).isSuccess) + assertTrue(q.trySend(3).isSuccess) + assertEquals(3, q.tryReceive().getOrNull()) + assertNull(q.tryReceive().getOrNull()) } @Test diff --git a/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt index cdec8a776e..501affb4d9 100644 --- a/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt @@ -12,12 +12,12 @@ class LinkedListChannelTest : TestBase() { fun testBasic() = runTest { val c = Channel(Channel.UNLIMITED) c.send(1) - check(c.offer(2)) + assertTrue(c.trySend(2).isSuccess) c.send(3) check(c.close()) check(!c.close()) assertEquals(1, c.receive()) - assertEquals(2, c.poll()) + assertEquals(2, c.tryReceive().getOrNull()) assertEquals(3, c.receiveCatching().getOrNull()) assertNull(c.receiveCatching().getOrNull()) } diff --git a/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt index ab0292a831..c83813e40e 100644 --- a/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt @@ -80,26 +80,26 @@ class RendezvousChannelTest : TestBase() { } @Test - fun testOfferAndPool() = runTest { + fun testTrySendTryReceive() = runTest { val q = Channel(Channel.RENDEZVOUS) - assertFalse(q.offer(1)) + assertFalse(q.trySend(1).isSuccess) expect(1) launch { expect(3) - assertNull(q.poll()) + assertNull(q.tryReceive().getOrNull()) expect(4) assertEquals(2, q.receive()) expect(7) - assertNull(q.poll()) + assertNull(q.tryReceive().getOrNull()) yield() expect(9) - assertEquals(3, q.poll()) + assertEquals(3, q.tryReceive().getOrNull()) expect(10) } expect(2) yield() expect(5) - assertTrue(q.offer(2)) + assertTrue(q.trySend(2).isSuccess) expect(6) yield() expect(8) diff --git a/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt b/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt index f93d039933..4763d13b05 100644 --- a/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt @@ -166,15 +166,15 @@ class ChannelBuildersFlowTest : TestBase() { var expected = 0 launch { - assertTrue(channel.offer(1)) // Handed to the coroutine - assertTrue(channel.offer(2)) // Buffered - assertFalse(channel.offer(3)) // Failed to offer + assertTrue(channel.trySend(1).isSuccess) // Handed to the coroutine + assertTrue(channel.trySend(2).isSuccess) // Buffered + assertFalse(channel.trySend(3).isSuccess) // Failed to offer channel.send(3) yield() assertEquals(1, expected) - assertTrue(channel.offer(4)) // Handed to the coroutine - assertTrue(channel.offer(5)) // Buffered - assertFalse(channel.offer(6)) // Failed to offer + assertTrue(channel.trySend(4).isSuccess) // Handed to the coroutine + assertTrue(channel.trySend(5).isSuccess) // Buffered + assertFalse(channel.trySend(6).isSuccess) // Failed to offer channel.send(6) assertEquals(2, expected) } diff --git a/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt b/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt index 31a929b2d8..f197a214f5 100644 --- a/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/channels/ChannelFlowTest.kt @@ -12,9 +12,9 @@ class ChannelFlowTest : TestBase() { @Test fun testRegular() = runTest { val flow = channelFlow { - assertTrue(offer(1)) - assertTrue(offer(2)) - assertTrue(offer(3)) + assertTrue(trySend(1).isSuccess) + assertTrue(trySend(2).isSuccess) + assertTrue(trySend(3).isSuccess) } assertEquals(listOf(1, 2, 3), flow.toList()) } @@ -22,9 +22,9 @@ class ChannelFlowTest : TestBase() { @Test fun testBuffer() = runTest { val flow = channelFlow { - assertTrue(offer(1)) - assertTrue(offer(2)) - assertFalse(offer(3)) + assertTrue(trySend(1).isSuccess) + assertTrue(trySend(2).isSuccess) + assertFalse(trySend(3).isSuccess) }.buffer(1) assertEquals(listOf(1, 2), flow.toList()) } @@ -32,10 +32,10 @@ class ChannelFlowTest : TestBase() { @Test fun testConflated() = runTest { val flow = channelFlow { - assertTrue(offer(1)) - assertTrue(offer(2)) - assertTrue(offer(3)) - assertTrue(offer(4)) + assertTrue(trySend(1).isSuccess) + assertTrue(trySend(2).isSuccess) + assertTrue(trySend(3).isSuccess) + assertTrue(trySend(4).isSuccess) }.buffer(Channel.CONFLATED) assertEquals(listOf(1, 4), flow.toList()) // two elements in the middle got conflated } @@ -43,7 +43,7 @@ class ChannelFlowTest : TestBase() { @Test fun testFailureCancelsChannel() = runTest { val flow = channelFlow { - offer(1) + trySend(1) invokeOnClose { expect(2) } diff --git a/kotlinx-coroutines-core/common/test/flow/sharing/ShareInFusionTest.kt b/kotlinx-coroutines-core/common/test/flow/sharing/ShareInFusionTest.kt index 371d01472e..85a17ba0fe 100644 --- a/kotlinx-coroutines-core/common/test/flow/sharing/ShareInFusionTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/sharing/ShareInFusionTest.kt @@ -42,7 +42,7 @@ class ShareInFusionTest : TestBase() { val flow = channelFlow { // send a batch of 10 elements using [offer] for (i in 1..10) { - assertTrue(offer(i)) // offer must succeed, because buffer + assertTrue(trySend(i).isSuccess) // offer must succeed, because buffer } send(0) // done }.buffer(10) // request a buffer of 10 @@ -53,4 +53,4 @@ class ShareInFusionTest : TestBase() { .collect { i -> expect(i + 1) } finish(12) } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt b/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt index 42cdb1e19f..db69e2bc06 100644 --- a/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/sharing/ShareInTest.kt @@ -167,11 +167,11 @@ class ShareInTest : TestBase() { subs += shared .onEach { value -> // only the first threshold subscribers get the value when (i) { - in 1..threshold -> log.offer("sub$i: $value") + in 1..threshold -> log.trySend("sub$i: $value") else -> expectUnreached() } } - .onCompletion { log.offer("sub$i: completion") } + .onCompletion { log.trySend("sub$i: completion") } .launchIn(this) checkStartTransition(i) } @@ -210,4 +210,4 @@ class ShareInTest : TestBase() { stop() } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt index ea0b639e90..0df8278b77 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Channels.kt @@ -50,7 +50,7 @@ import kotlinx.coroutines.* ) public fun SendChannel.sendBlocking(element: E) { // fast path - if (offer(element)) + if (trySend(element).isSuccess) return // slow path runBlocking { diff --git a/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt b/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt index 099e70b3b9..6c23982ee7 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/TickerChannels.kt @@ -21,13 +21,13 @@ public enum class TickerMode { * ``` * val channel = ticker(delay = 100) * delay(350) // 250 ms late - * println(channel.poll()) // prints Unit - * println(channel.poll()) // prints null + * println(channel.tryReceive().getOrNull()) // prints Unit + * println(channel.tryReceive().getOrNull()) // prints null * * delay(50) - * println(channel.poll()) // prints Unit, delay was adjusted + * println(channel.tryReceive().getOrNull()) // prints Unit, delay was adjusted * delay(50) - * println(channel.poll()) // prints null, we'are not late relatively to previous element + * println(channel.tryReceive().getOrNull()) // prints null, we're not late relatively to previous element * ``` */ FIXED_PERIOD, diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt deleted file mode 100644 index 010dab389d..0000000000 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt +++ /dev/null @@ -1,10 +0,0 @@ -kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@2a06d350 - at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) - at kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:170) - at kotlinx.coroutines.channels.ChannelCoroutine.offer(ChannelCoroutine.kt) - at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testCancelledOffer$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:153) -Caused by: kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@2a06d350 - at kotlinx.coroutines.JobSupport.cancel(JobSupport.kt:599) - at kotlinx.coroutines.Job$DefaultImpls.cancel$default(Job.kt:164) - at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testCancelledOffer$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:151) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) diff --git a/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt index 56f1e28313..06839f4a04 100644 --- a/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt +++ b/kotlinx-coroutines-core/jvm/test/ReusableCancellableContinuationTest.kt @@ -39,7 +39,7 @@ class ReusableCancellableContinuationTest : TestBase() { repeat(iterations) { suspender { - assertTrue(channel.offer(it)) + assertTrue(channel.trySend(it).isSuccess) } } channel.close() diff --git a/kotlinx-coroutines-core/jvm/test/RunInterruptibleTest.kt b/kotlinx-coroutines-core/jvm/test/RunInterruptibleTest.kt index e755b17d91..49c93c7fe2 100644 --- a/kotlinx-coroutines-core/jvm/test/RunInterruptibleTest.kt +++ b/kotlinx-coroutines-core/jvm/test/RunInterruptibleTest.kt @@ -41,7 +41,7 @@ class RunInterruptibleTest : TestBase() { val job = launch { runInterruptible(Dispatchers.IO) { expect(2) - latch.offer(Unit) + latch.trySend(Unit) try { Thread.sleep(10_000L) expectUnreached() diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt index a6a5340389..86adfee049 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelCancelUndeliveredElementStressTest.kt @@ -15,7 +15,7 @@ class ChannelCancelUndeliveredElementStressTest : TestBase() { // total counters private var sendCnt = 0 - private var offerFailedCnt = 0 + private var trySendFailedCnt = 0 private var receivedCnt = 0 private var undeliveredCnt = 0 @@ -23,7 +23,7 @@ class ChannelCancelUndeliveredElementStressTest : TestBase() { private var lastReceived = 0 private var dSendCnt = 0 private var dSendExceptionCnt = 0 - private var dOfferFailedCnt = 0 + private var dTrySendFailedCnt = 0 private var dReceivedCnt = 0 private val dUndeliveredCnt = AtomicInteger() @@ -43,30 +43,30 @@ class ChannelCancelUndeliveredElementStressTest : TestBase() { joinAll(j1, j2) // All elements must be either received or undelivered (IN every run) - if (dSendCnt - dOfferFailedCnt != dReceivedCnt + dUndeliveredCnt.get()) { + if (dSendCnt - dTrySendFailedCnt != dReceivedCnt + dUndeliveredCnt.get()) { println(" Send: $dSendCnt") - println("Send Exception: $dSendExceptionCnt") - println(" Offer failed: $dOfferFailedCnt") + println("Send exception: $dSendExceptionCnt") + println("trySend failed: $dTrySendFailedCnt") println(" Received: $dReceivedCnt") println(" Undelivered: ${dUndeliveredCnt.get()}") error("Failed") } - offerFailedCnt += dOfferFailedCnt + trySendFailedCnt += dTrySendFailedCnt receivedCnt += dReceivedCnt undeliveredCnt += dUndeliveredCnt.get() // clear for next run dSendCnt = 0 dSendExceptionCnt = 0 - dOfferFailedCnt = 0 + dTrySendFailedCnt = 0 dReceivedCnt = 0 dUndeliveredCnt.set(0) } // Stats - println(" Send: $sendCnt") - println(" Offer failed: $offerFailedCnt") - println(" Received: $receivedCnt") - println(" Undelivered: $undeliveredCnt") - assertEquals(sendCnt - offerFailedCnt, receivedCnt + undeliveredCnt) + println(" Send: $sendCnt") + println("trySend failed: $trySendFailedCnt") + println(" Received: $receivedCnt") + println(" Undelivered: $undeliveredCnt") + assertEquals(sendCnt - trySendFailedCnt, receivedCnt + undeliveredCnt) } private suspend fun sendOne(channel: Channel) { @@ -75,11 +75,11 @@ class ChannelCancelUndeliveredElementStressTest : TestBase() { try { when (Random.nextInt(2)) { 0 -> channel.send(i) - 1 -> if (!channel.offer(i)) { - dOfferFailedCnt++ + 1 -> if (!channel.trySend(i).isSuccess) { + dTrySendFailedCnt++ } } - } catch(e: Throwable) { + } catch (e: Throwable) { assertTrue(e is CancellationException) // the only exception possible in this test dSendExceptionCnt++ throw e diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt index 8f5224db79..1233432615 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ChannelUndeliveredElementStressTest.kt @@ -68,7 +68,7 @@ class ChannelUndeliveredElementStressTest(private val kind: TestChannelKind) : T try { block() } finally { - if (!done.offer(true)) + if (!done.trySend(true).isSuccess) error(IllegalStateException("failed to offer to done channel")) } } diff --git a/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt index eb7be57500..2b3c05bcc6 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ConflatedBroadcastChannelNotifyStressTest.kt @@ -29,7 +29,7 @@ class ConflatedBroadcastChannelNotifyStressTest : TestBase() { launch(Dispatchers.Default + CoroutineName("Sender$senderId")) { repeat(nEvents) { i -> if (i % nSenders == senderId) { - broadcast.offer(i) + broadcast.trySend(i) sentTotal.incrementAndGet() yield() } @@ -63,7 +63,7 @@ class ConflatedBroadcastChannelNotifyStressTest : TestBase() { try { withTimeout(timeLimit) { senders.forEach { it.join() } - broadcast.offer(nEvents) // last event to signal receivers termination + broadcast.trySend(nEvents) // last event to signal receivers termination receivers.forEach { it.join() } } } catch (e: CancellationException) { @@ -86,4 +86,4 @@ class ConflatedBroadcastChannelNotifyStressTest : TestBase() { cancel() value } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt b/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt index fd26144faf..793d7e44f5 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/ConflatedChannelCloseStressTest.kt @@ -5,11 +5,8 @@ package kotlinx.coroutines.channels import kotlinx.coroutines.* -import org.junit.After -import org.junit.Test -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicReference -import kotlin.coroutines.* +import org.junit.* +import java.util.concurrent.atomic.* class ConflatedChannelCloseStressTest : TestBase() { @@ -37,12 +34,9 @@ class ConflatedChannelCloseStressTest : TestBase() { var x = senderId try { while (isActive) { - try { - curChannel.get().offer(x) + curChannel.get().trySend(x).onSuccess { x += nSenders sent.incrementAndGet() - } catch (e: ClosedSendChannelException) { - // ignore } } } finally { diff --git a/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt b/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt index 6ce2f20d5a..fbc28a1855 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt +++ b/kotlinx-coroutines-core/jvm/test/channels/TickerChannelCommonTest.kt @@ -48,7 +48,7 @@ class TickerChannelCommonTest(private val channelFactory: Channel) : TestBase() delayChannel.cancel() delay(5100) - assertFailsWith { delayChannel.poll() } + assertFailsWith { delayChannel.tryReceive().getOrThrow() } } } @@ -159,9 +159,9 @@ class TickerChannelCommonTest(private val channelFactory: Channel) : TestBase() } } -fun ReceiveChannel.checkEmpty() = assertNull(poll()) +fun ReceiveChannel.checkEmpty() = assertNull(tryReceive().getOrNull()) fun ReceiveChannel.checkNotEmpty() { - assertNotNull(poll()) - assertNull(poll()) + assertNotNull(tryReceive().getOrNull()) + assertNull(tryReceive().getOrNull()) } diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt index ae2554ad4a..2d8c0ebc75 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt @@ -137,25 +137,6 @@ class StackTraceRecoveryChannelsTest : TestBase() { deferred.await() } - // See https://github.com/Kotlin/kotlinx.coroutines/issues/950 - @Test - fun testCancelledOffer() = runTest { - expect(1) - val job = Job() - val actor = actor(job, Channel.UNLIMITED) { - consumeEach { - expectUnreached() // is cancelled before offer - } - } - job.cancel() - try { - actor.offer(1) - } catch (e: Exception) { - verifyStackTrace("channels/${name.methodName}", e) - finish(2) - } - } - private suspend fun Channel.sendWithContext(ctx: CoroutineContext) = withContext(ctx) { sendInChannel() yield() // TCE diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt index 1a381547ba..9afcab537d 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt @@ -261,7 +261,7 @@ class StackTraceRecoveryTest : TestBase() { private suspend fun awaitCallback(channel: Channel) { suspendCancellableCoroutine { cont -> - channel.offer(Callback(cont)) + channel.trySend(Callback(cont)) } yield() // nop to make sure it is not a tail call } diff --git a/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt b/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt index e3db2626ce..f1be284cae 100644 --- a/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt +++ b/kotlinx-coroutines-core/jvm/test/flow/CallbackFlowTest.kt @@ -36,7 +36,7 @@ class CallbackFlowTest : TestBase() { fun testThrowingConsumer() = runTest { var i = 0 val api = CallbackApi { - runCatching { it.offer(++i) } + it.trySend(++i) } val flow = callbackFlow { @@ -77,13 +77,13 @@ class CallbackFlowTest : TestBase() { var i = 0 val api = CallbackApi { if (i < 5) { - it.offer(++i) + it.trySend(++i) } else { it.close(RuntimeException()) } } - val flow = callbackFlow() { + val flow = callbackFlow { api.start(channel) awaitClose { api.stop() diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt index fbd5c0d8f3..74cc17836b 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/ChannelsLincheckTest.kt @@ -63,11 +63,12 @@ abstract class ChannelLincheckTestBase( } @Operation - fun offer(@Param(name = "value") value: Int): Any = try { - c.offer(value) - } catch (e: NumberedCancellationException) { - e.testResult - } + fun trySend(@Param(name = "value") value: Int): Any = c.trySend(value) + .onSuccess { return true } + .onFailure { + return if (it is NumberedCancellationException) it.testResult + else false + } // TODO: this operation should be (and can be!) linearizable, but is not // @Operation @@ -85,11 +86,10 @@ abstract class ChannelLincheckTestBase( } @Operation - fun poll(): Any? = try { - c.poll() - } catch (e: NumberedCancellationException) { - e.testResult - } + fun tryReceive(): Any? = + c.tryReceive() + .onSuccess { return it } + .onFailure { return if (it is NumberedCancellationException) it.testResult else null } // TODO: this operation should be (and can be!) linearizable, but is not // @Operation @@ -131,7 +131,7 @@ abstract class SequentialIntChannelBase(private val capacity: Int) : VerifierSta private val buffer = ArrayList() private var closedMessage: String? = null - suspend fun send(x: Int): Any = when (val offerRes = offer(x)) { + suspend fun send(x: Int): Any = when (val offerRes = trySend(x)) { true -> Unit false -> suspendCancellableCoroutine { cont -> senders.add(cont to x) @@ -139,7 +139,7 @@ abstract class SequentialIntChannelBase(private val capacity: Int) : VerifierSta else -> offerRes } - fun offer(element: Int): Any { + fun trySend(element: Int): Any { if (closedMessage !== null) return closedMessage!! if (capacity == CONFLATED) { if (resumeFirstReceiver(element)) return true @@ -163,11 +163,11 @@ abstract class SequentialIntChannelBase(private val capacity: Int) : VerifierSta return false } - suspend fun receive(): Any = poll() ?: suspendCancellableCoroutine { cont -> + suspend fun receive(): Any = tryReceive() ?: suspendCancellableCoroutine { cont -> receivers.add(cont) } - fun poll(): Any? { + fun tryReceive(): Any? { if (buffer.isNotEmpty()) { val el = buffer.removeAt(0) resumeFirstSender().also { @@ -221,4 +221,4 @@ private fun CancellableContinuation.resume(res: T): Boolean { val token = tryResume(res) ?: return false completeResume(token) return true -} \ No newline at end of file +} diff --git a/reactive/kotlinx-coroutines-jdk9/test/PublisherAsFlowTest.kt b/reactive/kotlinx-coroutines-jdk9/test/PublisherAsFlowTest.kt index 97f106b3c5..b5b2a0a2b8 100644 --- a/reactive/kotlinx-coroutines-jdk9/test/PublisherAsFlowTest.kt +++ b/reactive/kotlinx-coroutines-jdk9/test/PublisherAsFlowTest.kt @@ -70,7 +70,7 @@ class PublisherAsFlowTest : TestBase() { send(it + 1) expect(it + 1) } - assertFalse { offer(-1) } + assertFalse { trySend(-1).isSuccess } } publisher.asFlow().collect { diff --git a/reactive/kotlinx-coroutines-reactive/src/Channel.kt b/reactive/kotlinx-coroutines-reactive/src/Channel.kt index 854a8829fc..d9c9b91a2c 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Channel.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Channel.kt @@ -109,7 +109,7 @@ private class SubscriptionChannel( override fun onNext(t: T) { _requested.decrementAndGet() - offer(t) + trySend(t) // Safe to ignore return value here, expectedly racing with cancellation } override fun onComplete() { diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index d2cdbab881..383a17d836 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -89,7 +89,7 @@ public class PublisherCoroutine( public override suspend fun send(element: T) { // fast-path -- try send without suspension - if (offer(element)) return + if (trySend(element).isSuccess) return // slow-path does suspend return sendSuspend(element) } diff --git a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt index cb8de7a636..f0245388bf 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt @@ -132,7 +132,7 @@ private class ReactiveSubscriber( override fun onNext(value: T) { // Controlled by requestSize - require(channel.offer(value)) { "Element $value was not added to channel because it was full, $channel" } + require(channel.trySend(value).isSuccess) { "Element $value was not added to channel because it was full, $channel" } } override fun onComplete() { diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt index e2c86c97ff..7a0e0fac38 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherAsFlowTest.kt @@ -71,7 +71,7 @@ class PublisherAsFlowTest : TestBase() { send(it + 1) expect(it + 1) } - assertFalse { offer(-1) } + assertFalse { trySend(-1).isSuccess } } publisher.asFlow().collect { diff --git a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt index 35f3391eb1..3bc50c8d57 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt @@ -84,11 +84,11 @@ private class SubscriptionChannel : } override fun onSuccess(t: T) { - offer(t) + trySend(t) // Safe to ignore return value here, expectedly racing with cancellation } override fun onNext(t: T) { - offer(t) + trySend(t) // Safe to ignore return value here, expectedly racing with cancellation } override fun onComplete() { diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index 56ce30c370..09c5dc1d9f 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -84,7 +84,7 @@ private class RxObservableCoroutine( public override suspend fun send(element: T) { // fast-path -- try send without suspension - if (offer(element)) return + if (trySend(element).isSuccess) return // slow-path does suspend return sendSuspend(element) } diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableSourceAsFlowStressTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableSourceAsFlowStressTest.kt index 159f3729c8..0253fcedd8 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableSourceAsFlowStressTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableSourceAsFlowStressTest.kt @@ -26,7 +26,7 @@ class ObservableSourceAsFlowStressTest : TestBase() { val latch = Channel(1) var i = 0 val observable = Observable.interval(100L, TimeUnit.MICROSECONDS) - .doOnNext { if (++i > 100) latch.offer(Unit) } + .doOnNext { if (++i > 100) latch.trySend(Unit) } val job = observable.asFlow().launchIn(CoroutineScope(Dispatchers.Default)) latch.receive() job.cancelAndJoin() diff --git a/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt b/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt index 76333f2eb8..ad780f7504 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt @@ -69,11 +69,11 @@ private class SubscriptionChannel : } override fun onSuccess(t: T) { - offer(t) + trySend(t) // Safe to ignore return value here, expectedly racing with cancellation } override fun onNext(t: T) { - offer(t) + trySend(t) // Safe to ignore return value here, expectedly racing with cancellation } override fun onComplete() { diff --git a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt index a17e32dd31..55794f9adf 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt @@ -78,7 +78,7 @@ private class RxObservableCoroutine( public override suspend fun send(element: T) { // fast-path -- try send without suspension - if (offer(element)) return + if (trySend(element).isSuccess) return // slow-path does suspend return sendSuspend(element) } diff --git a/reactive/kotlinx-coroutines-rx3/test/ObservableSourceAsFlowStressTest.kt b/reactive/kotlinx-coroutines-rx3/test/ObservableSourceAsFlowStressTest.kt index 431a7a789e..01d6a20e36 100644 --- a/reactive/kotlinx-coroutines-rx3/test/ObservableSourceAsFlowStressTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/ObservableSourceAsFlowStressTest.kt @@ -27,7 +27,7 @@ class ObservableSourceAsFlowStressTest : TestBase() { val latch = Channel(1) var i = 0 val observable = Observable.interval(100L, TimeUnit.MICROSECONDS) - .doOnNext { if (++i > 100) latch.offer(Unit) } + .doOnNext { if (++i > 100) latch.trySend(Unit) } val job = observable.asFlow().launchIn(CoroutineScope(Dispatchers.Default)) latch.receive() job.cancelAndJoin() diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md index 7673c8f2b9..d5bd2320b7 100644 --- a/ui/coroutines-guide-ui.md +++ b/ui/coroutines-guide-ui.md @@ -268,7 +268,7 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { } // install a listener to offer events to this actor onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } ``` @@ -276,12 +276,12 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { > You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt). The key idea that underlies an integration of an actor coroutine and a regular event handler is that -there is an [offer][SendChannel.offer] function on [SendChannel] that does not wait. It sends an element to the actor immediately, -if it is possible, or discards an element otherwise. An `offer` actually returns a `Boolean` result which we ignore here. +there is an [trySend][SendChannel.trySend] function on [SendChannel] that does not wait. It sends an element to the actor immediately, +if it is possible, or discards an element otherwise. A `trySend` actually returns a `ChanneResult` instance which we ignore here. Try clicking repeatedly on a circle in this version of the code. The clicks are just ignored while the countdown animation is running. This happens because the actor is busy with an animation and does not receive from its channel. -By default, an actor's mailbox is backed by `RendezvousChannel`, whose `offer` operation succeeds only when +By default, an actor's mailbox is backed by `RendezvousChannel`, whose `trySend` operation succeeds only when the `receive` is active. > On Android, there is `View` sent in OnClickListener, so we send the `View` to the actor as a signal. @@ -295,7 +295,7 @@ fun View.onClick(action: suspend (View) -> Unit) { } // install a listener to activate this actor setOnClickListener { - eventActor.offer(it) + eventActor.trySend(it) } } ``` @@ -319,9 +319,9 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { val eventActor = GlobalScope.actor(Dispatchers.Main, capacity = Channel.CONFLATED) { // <--- Changed here for (event in channel) action(event) // pass event to action } - // install a listener to offer events to this actor + // install a listener to send events to this actor onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } ``` @@ -359,7 +359,7 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { for (event in channel) action(event) // pass event to action } onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } --> @@ -623,7 +623,7 @@ After delay [actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html -[SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html +[SendChannel.trySend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html [SendChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/index.html [Channel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/index.html [Channel.CONFLATED]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/-c-o-n-f-l-a-t-e-d.html diff --git a/ui/kotlinx-coroutines-javafx/src/JavaFxConvert.kt b/ui/kotlinx-coroutines-javafx/src/JavaFxConvert.kt index 1cbf9b6fe9..ebeaa3b84b 100644 --- a/ui/kotlinx-coroutines-javafx/src/JavaFxConvert.kt +++ b/ui/kotlinx-coroutines-javafx/src/JavaFxConvert.kt @@ -4,10 +4,9 @@ package kotlinx.coroutines.javafx -import javafx.beans.value.ChangeListener -import javafx.beans.value.ObservableValue +import javafx.beans.value.* import kotlinx.coroutines.* -import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.channels.* import kotlinx.coroutines.flow.* /** @@ -27,11 +26,11 @@ import kotlinx.coroutines.flow.* @ExperimentalCoroutinesApi // Since 1.3.x public fun ObservableValue.asFlow(): Flow = callbackFlow { val listener = ChangeListener { _, _, newValue -> - try { - offer(newValue) - } catch (e: CancellationException) { - // In case the event fires after the channel is closed - } + /* + * Do not propagate the exception to the ObservableValue, it + * already should've been handled by the downstream + */ + trySend(newValue) } addListener(listener) send(value) diff --git a/ui/kotlinx-coroutines-javafx/test/JavaFxObservableAsFlowTest.kt b/ui/kotlinx-coroutines-javafx/test/JavaFxObservableAsFlowTest.kt index 6964050102..bc40b0fdfb 100644 --- a/ui/kotlinx-coroutines-javafx/test/JavaFxObservableAsFlowTest.kt +++ b/ui/kotlinx-coroutines-javafx/test/JavaFxObservableAsFlowTest.kt @@ -83,4 +83,20 @@ class JavaFxObservableAsFlowTest : TestBase() { } } + @Test + fun testIntermediateCrash() = runTest { + if (!initPlatform()) { + println("Skipping JavaFxTest in headless environment") + return@runTest // ignore test in headless environments + } + + val property = SimpleIntegerProperty(0) + + assertFailsWith { + property.asFlow().onEach { + yield() + throw TestException() + }.collect() + } + } } diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt index 51e94779e7..ec8a09f9bd 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt @@ -67,6 +67,6 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { } // install a listener to offer events to this actor onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt index 81371678a9..aa152b7937 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt @@ -65,8 +65,8 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { val eventActor = GlobalScope.actor(Dispatchers.Main, capacity = Channel.CONFLATED) { // <--- Changed here for (event in channel) action(event) // pass event to action } - // install a listener to offer events to this actor + // install a listener to send events to this actor onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt index ea5ac90a4b..0c89ea70f7 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt @@ -55,7 +55,7 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { for (event in channel) action(event) // pass event to action } onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt index 504f2ee6c7..6e8b984a20 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt @@ -55,7 +55,7 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { for (event in channel) action(event) // pass event to action } onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } diff --git a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt index 0e1153673a..3ff5d7d5d0 100644 --- a/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt +++ b/ui/kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt @@ -55,7 +55,7 @@ fun Node.onClick(action: suspend (MouseEvent) -> Unit) { for (event in channel) action(event) // pass event to action } onMouseClicked = EventHandler { event -> - eventActor.offer(event) + eventActor.trySend(event) } } From 7872f8fcdf972b4146a9d9b51816e9acc90ce7ea Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 15 Apr 2021 20:13:26 +0300 Subject: [PATCH 34/55] =?UTF-8?q?Mark=20BroadcastChannel,=20ConflatedBroad?= =?UTF-8?q?castChannel=20and=20all=20related=20oper=E2=80=A6=20(#2647)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mark BroadcastChannel, ConflatedBroadcastChannel and all related operators as obsolete API replaced with SharedFlow and StateFlow * Remove operator fusion with deprecated broadcastIn in order to simplify further Flow maintenance --- .../api/kotlinx-coroutines-core.api | 1 - .../common/src/channels/Broadcast.kt | 13 +- .../common/src/channels/BroadcastChannel.kt | 12 +- .../src/channels/ConflatedBroadcastChannel.kt | 6 +- .../common/src/flow/Builders.kt | 4 +- .../common/src/flow/Channels.kt | 44 +++--- .../common/src/flow/SharedFlow.kt | 2 +- .../common/src/flow/StateFlow.kt | 2 +- .../common/src/flow/internal/ChannelFlow.kt | 18 +-- .../common/src/flow/operators/Context.kt | 6 +- .../common/src/flow/operators/Merge.kt | 8 +- .../flow/channels/ChannelBuildersFlowTest.kt | 139 ------------------ 12 files changed, 56 insertions(+), 199 deletions(-) diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 23f29b02a3..0d684b4454 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -1102,7 +1102,6 @@ public abstract class kotlinx/coroutines/flow/internal/ChannelFlow : kotlinx/cor public final field onBufferOverflow Lkotlinx/coroutines/channels/BufferOverflow; public fun (Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)V protected fun additionalToStringProps ()Ljava/lang/String; - public fun broadcastImpl (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/CoroutineStart;)Lkotlinx/coroutines/channels/BroadcastChannel; public fun collect (Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected abstract fun collectTo (Lkotlinx/coroutines/channels/ProducerScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; protected abstract fun create (Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)Lkotlinx/coroutines/flow/internal/ChannelFlow; diff --git a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt index 3ed4bc7fff..b1c24b456d 100644 --- a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt +++ b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt @@ -35,11 +35,13 @@ import kotlin.coroutines.intrinsics.* * [send][BroadcastChannel.send] and [close][BroadcastChannel.close] operations that interfere with * the broadcasting coroutine in hard-to-specify ways. * - * **Note: This API is obsolete.** It will be deprecated and replaced with the - * [Flow.shareIn][kotlinx.coroutines.flow.shareIn] operator when it becomes stable. + * **Note: This API is obsolete since 1.5.0.** It will be deprecated with warning in 1.6.0 + * and with error in 1.7.0. It is replaced with [Flow.shareIn][kotlinx.coroutines.flow.shareIn] + * operator. * * @param start coroutine start option. The default value is [CoroutineStart.LAZY]. */ +@ObsoleteCoroutinesApi public fun ReceiveChannel.broadcast( capacity: Int = 1, start: CoroutineStart = CoroutineStart.LAZY @@ -95,10 +97,12 @@ public fun ReceiveChannel.broadcast( * * ### Future replacement * + * This API is obsolete since 1.5.0. * This function has an inappropriate result type of [BroadcastChannel] which provides * [send][BroadcastChannel.send] and [close][BroadcastChannel.close] operations that interfere with - * the broadcasting coroutine in hard-to-specify ways. It will be replaced with - * sharing operators on [Flow][kotlinx.coroutines.flow.Flow] in the future. + * the broadcasting coroutine in hard-to-specify ways. It will be deprecated with warning in 1.6.0 + * and with error in 1.7.0. It is replaced with [Flow.shareIn][kotlinx.coroutines.flow.shareIn] + * operator. * * @param context additional to [CoroutineScope.coroutineContext] context of the coroutine. * @param capacity capacity of the channel's buffer (1 by default). @@ -106,6 +110,7 @@ public fun ReceiveChannel.broadcast( * @param onCompletion optional completion handler for the producer coroutine (see [Job.invokeOnCompletion]). * @param block the coroutine code. */ +@ObsoleteCoroutinesApi public fun CoroutineScope.broadcast( context: CoroutineContext = EmptyCoroutineContext, capacity: Int = 1, diff --git a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt index 6cd79373b2..c82b8dbd63 100644 --- a/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/BroadcastChannel.kt @@ -20,10 +20,10 @@ import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED * See `BroadcastChannel()` factory function for the description of available * broadcast channel implementations. * - * **Note: This API is obsolete.** It will be deprecated and replaced by [SharedFlow][kotlinx.coroutines.flow.SharedFlow] - * when it becomes stable. + * **Note: This API is obsolete since 1.5.0.** It will be deprecated with warning in 1.6.0 + * and with error in 1.7.0. It is replaced with [SharedFlow][kotlinx.coroutines.flow.SharedFlow]. */ -@ExperimentalCoroutinesApi // not @ObsoleteCoroutinesApi to reduce burden for people who are still using it +@ObsoleteCoroutinesApi public interface BroadcastChannel : SendChannel { /** * Subscribes to this [BroadcastChannel] and returns a channel to receive elements from it. @@ -60,9 +60,11 @@ public interface BroadcastChannel : SendChannel { * * when `capacity` is [BUFFERED] -- creates `ArrayBroadcastChannel` with a default capacity. * * otherwise -- throws [IllegalArgumentException]. * - * **Note: This is an experimental api.** It may be changed in the future updates. + * **Note: This API is obsolete since 1.5.0.** It will be deprecated with warning in 1.6.0 + * and with error in 1.7.0. It is replaced with [StateFlow][kotlinx.coroutines.flow.StateFlow] + * and [SharedFlow][kotlinx.coroutines.flow.SharedFlow]. */ -@ExperimentalCoroutinesApi +@ObsoleteCoroutinesApi public fun BroadcastChannel(capacity: Int): BroadcastChannel = when (capacity) { 0 -> throw IllegalArgumentException("Unsupported 0 capacity for BroadcastChannel") diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt index 8283bcb166..b768d7c38c 100644 --- a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt @@ -26,10 +26,10 @@ import kotlin.jvm.* * [opening][openSubscription] and [closing][ReceiveChannel.cancel] subscription takes O(N) time, where N is the * number of subscribers. * - * **Note: This API is obsolete.** It will be deprecated and replaced by [StateFlow][kotlinx.coroutines.flow.StateFlow] - * when it becomes stable. + * **Note: This API is obsolete since 1.5.0.** It will be deprecated with warning in 1.6.0 + * and with error in 1.7.0. It is replaced with [StateFlow][kotlinx.coroutines.flow.StateFlow]. */ -@ExperimentalCoroutinesApi // not @ObsoleteCoroutinesApi to reduce burden for people who are still using it +@ObsoleteCoroutinesApi public class ConflatedBroadcastChannel() : BroadcastChannel { /** * Creates an instance of this class that already holds a value. diff --git a/kotlinx-coroutines-core/common/src/flow/Builders.kt b/kotlinx-coroutines-core/common/src/flow/Builders.kt index 83ae9136eb..66b55a90c0 100644 --- a/kotlinx-coroutines-core/common/src/flow/Builders.kt +++ b/kotlinx-coroutines-core/common/src/flow/Builders.kt @@ -234,7 +234,7 @@ public fun flowViaChannel( * resulting flow to specify a user-defined value and to control what happens when data is produced faster * than consumed, i.e. to control the back-pressure behavior. * - * Adjacent applications of [channelFlow], [flowOn], [buffer], [produceIn], and [broadcastIn] are + * Adjacent applications of [channelFlow], [flowOn], [buffer], and [produceIn] are * always fused so that only one properly configured channel is used for execution. * * Examples of usage: @@ -289,7 +289,7 @@ public fun channelFlow(@BuilderInference block: suspend ProducerScope.() * resulting flow to specify a user-defined value and to control what happens when data is produced faster * than consumed, i.e. to control the back-pressure behavior. * - * Adjacent applications of [callbackFlow], [flowOn], [buffer], [produceIn], and [broadcastIn] are + * Adjacent applications of [callbackFlow], [flowOn], [buffer], and [produceIn] are * always fused so that only one properly configured channel is used for execution. * * Example of usage that converts a multi-shot callback API to a flow. diff --git a/kotlinx-coroutines-core/common/src/flow/Channels.kt b/kotlinx-coroutines-core/common/src/flow/Channels.kt index 5b126f0ed7..4b50ad3108 100644 --- a/kotlinx-coroutines-core/common/src/flow/Channels.kt +++ b/kotlinx-coroutines-core/common/src/flow/Channels.kt @@ -133,17 +133,12 @@ private class ChannelAsFlow( override fun create(context: CoroutineContext, capacity: Int, onBufferOverflow: BufferOverflow): ChannelFlow = ChannelAsFlow(channel, consume, context, capacity, onBufferOverflow) - override fun dropChannelOperators(): Flow? = + override fun dropChannelOperators(): Flow = ChannelAsFlow(channel, consume) override suspend fun collectTo(scope: ProducerScope) = SendingCollector(scope).emitAllImpl(channel, consume) // use efficient channel receiving code from emitAll - override fun broadcastImpl(scope: CoroutineScope, start: CoroutineStart): BroadcastChannel { - markConsumed() // fail fast on repeated attempt to collect it - return super.broadcastImpl(scope, start) - } - override fun produceImpl(scope: CoroutineScope): ReceiveChannel { markConsumed() // fail fast on repeated attempt to collect it return if (capacity == Channel.OPTIONAL_CHANNEL) { @@ -173,22 +168,16 @@ private class ChannelAsFlow( * 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. */ -@FlowPreview +@Deprecated( + level = DeprecationLevel.WARNING, + message = "'BroadcastChannel' is obsolete and all coreresponding operators are deprecated " + + "in the favour of StateFlow and SharedFlow" +) // Since 1.5.0, was @FlowPreview, safe to remove in 1.7.0 public fun BroadcastChannel.asFlow(): Flow = flow { emitAll(openSubscription()) } /** - * Creates a [broadcast] coroutine that collects the given flow. - * - * This transformation is **stateful**, it launches a [broadcast] coroutine - * that collects the given flow and thus resulting channel should be properly closed or cancelled. - * - * A channel with [default][Channel.Factory.BUFFERED] buffer size is created. - * Use [buffer] operator on the flow before calling `broadcastIn` to specify a value other than - * default and to control what happens when data is produced faster than it is consumed, - * that is to control backpressure behavior. - * * ### Deprecated * * **This API is deprecated.** The [BroadcastChannel] provides a complex channel-like API for hot flows. @@ -202,13 +191,26 @@ public fun BroadcastChannel.asFlow(): Flow = flow { @Deprecated( message = "Use shareIn operator and the resulting SharedFlow as a replacement for BroadcastChannel", replaceWith = ReplaceWith("this.shareIn(scope, SharingStarted.Lazily, 0)"), - level = DeprecationLevel.WARNING -) + level = DeprecationLevel.ERROR +) // WARNING in 1.4.0, error in 1.5.0, removed in 1.6.0 (was @FlowPreview) public fun Flow.broadcastIn( scope: CoroutineScope, start: CoroutineStart = CoroutineStart.LAZY -): BroadcastChannel = - asChannelFlow().broadcastImpl(scope, start) +): BroadcastChannel { + // Backwards compatibility with operator fusing + val channelFlow = asChannelFlow() + val capacity = when (channelFlow.onBufferOverflow) { + BufferOverflow.SUSPEND -> channelFlow.produceCapacity + BufferOverflow.DROP_OLDEST -> Channel.CONFLATED + BufferOverflow.DROP_LATEST -> + throw IllegalArgumentException("Broadcast channel does not support BufferOverflow.DROP_LATEST") + } + return scope.broadcast(channelFlow.context, capacity = capacity, start = start) { + collect { value -> + send(value) + } + } +} /** * Creates a [produce] coroutine that collects the given flow. diff --git a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt index 490b221b58..25118d75a1 100644 --- a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt @@ -80,7 +80,7 @@ import kotlin.native.concurrent.* * ### SharedFlow vs BroadcastChannel * * Conceptually shared flow is similar to [BroadcastChannel][BroadcastChannel] - * and is designed to completely replace `BroadcastChannel` in the future. + * and is designed to completely replace it. * It has the following important differences: * * * `SharedFlow` is simpler, because it does not have to implement all the [Channel] APIs, which allows diff --git a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt index 74d3314075..49407e27b4 100644 --- a/kotlinx-coroutines-core/common/src/flow/StateFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/StateFlow.kt @@ -88,7 +88,7 @@ import kotlin.native.concurrent.* * ### StateFlow vs ConflatedBroadcastChannel * * Conceptually, state flow is similar to [ConflatedBroadcastChannel] - * and is designed to completely replace `ConflatedBroadcastChannel` in the future. + * and is designed to completely replace it. * It has the following important differences: * * * `StateFlow` is simpler, because it does not have to implement all the [Channel] APIs, which allows diff --git a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt index bf82cf9ae9..0efe5f86db 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt @@ -37,7 +37,7 @@ public interface FusibleFlow : Flow { /** * Operators that use channels as their "output" extend this `ChannelFlow` and are always fused with each other. * This class servers as a skeleton implementation of [FusibleFlow] and provides other cross-cutting - * methods like ability to [produceIn] and [broadcastIn] the corresponding flow, thus making it + * methods like ability to [produceIn] the corresponding flow, thus making it * possible to directly use the backing channel if it exists (hence the `ChannelFlow` name). * * @suppress **This an internal API and should not be used from general code.** @@ -59,7 +59,7 @@ public abstract class ChannelFlow( internal val collectToFun: suspend (ProducerScope) -> Unit get() = { collectTo(it) } - private val produceCapacity: Int + internal val produceCapacity: Int get() = if (capacity == Channel.OPTIONAL_CHANNEL) Channel.BUFFERED else capacity /** @@ -107,18 +107,6 @@ public abstract class ChannelFlow( protected abstract suspend fun collectTo(scope: ProducerScope) - // broadcastImpl is used in broadcastIn operator which is obsolete and replaced by SharedFlow. - // BroadcastChannel does not support onBufferOverflow beyond simple conflation - public open fun broadcastImpl(scope: CoroutineScope, start: CoroutineStart): BroadcastChannel { - val broadcastCapacity = when (onBufferOverflow) { - BufferOverflow.SUSPEND -> produceCapacity - BufferOverflow.DROP_OLDEST -> Channel.CONFLATED - BufferOverflow.DROP_LATEST -> - throw IllegalArgumentException("Broadcast channel does not support BufferOverflow.DROP_LATEST") - } - return scope.broadcast(context, broadcastCapacity, start, block = collectToFun) - } - /** * Here we use ATOMIC start for a reason (#1825). * NB: [produceImpl] is used for [flowOn]. @@ -201,7 +189,7 @@ internal class ChannelFlowOperatorImpl( override fun create(context: CoroutineContext, capacity: Int, onBufferOverflow: BufferOverflow): ChannelFlow = ChannelFlowOperatorImpl(flow, context, capacity, onBufferOverflow) - override fun dropChannelOperators(): Flow? = flow + override fun dropChannelOperators(): Flow = flow override suspend fun flowCollect(collector: FlowCollector) = flow.collect(collector) diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Context.kt b/kotlinx-coroutines-core/common/src/flow/operators/Context.kt index cbbb4196d5..6686896766 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Context.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Context.kt @@ -79,7 +79,7 @@ import kotlin.jvm.* * * ### Operator fusion * - * Adjacent applications of [channelFlow], [flowOn], [buffer], [produceIn], and [broadcastIn] are + * Adjacent applications of [channelFlow], [flowOn], [buffer], and [produceIn] are * always fused so that only one properly configured channel is used for execution. * * Explicitly specified buffer capacity takes precedence over `buffer()` or `buffer(Channel.BUFFERED)` calls, @@ -176,7 +176,7 @@ public fun Flow.buffer(capacity: Int = BUFFERED): Flow = buffer(capaci * * ### Operator fusion * - * Adjacent applications of `conflate`/[buffer], [channelFlow], [flowOn], [produceIn], and [broadcastIn] are + * Adjacent applications of `conflate`/[buffer], [channelFlow], [flowOn] and [produceIn] are * always fused so that only one properly configured channel is used for execution. * **Conflation takes precedence over `buffer()` calls with any other capacity.** * @@ -219,7 +219,7 @@ public fun Flow.conflate(): Flow = buffer(CONFLATED) * * ### Operator fusion * - * Adjacent applications of [channelFlow], [flowOn], [buffer], [produceIn], and [broadcastIn] are + * Adjacent applications of [channelFlow], [flowOn], [buffer], and [produceIn] are * always fused so that only one properly configured channel is used for execution. * * Multiple `flowOn` operators fuse to a single `flowOn` with a combined context. The elements of the context of diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt index d1cbe72b5b..432160f340 100644 --- a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt +++ b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt @@ -57,7 +57,7 @@ public fun Flow.flatMapConcat(transform: suspend (value: T) -> Flow * * ### Operator fusion * - * Applications of [flowOn], [buffer], [produceIn], and [broadcastIn] _after_ this operator are fused with + * Applications of [flowOn], [buffer], and [produceIn] _after_ this operator are fused with * its concurrent merging so that only one properly configured channel is used for execution of merging logic. * * @param concurrency controls the number of in-flight flows, at most [concurrency] flows are collected @@ -87,7 +87,7 @@ public fun Flow>.flattenConcat(): Flow = flow { * * ### Operator fusion * - * Applications of [flowOn], [buffer], [produceIn], and [broadcastIn] _after_ this operator are fused with + * Applications of [flowOn], [buffer], and [produceIn] _after_ this operator are fused with * its concurrent merging so that only one properly configured channel is used for execution of merging logic. */ @ExperimentalCoroutinesApi @@ -111,7 +111,7 @@ public fun Iterable>.merge(): Flow { * * ### Operator fusion * - * Applications of [flowOn], [buffer], [produceIn], and [broadcastIn] _after_ this operator are fused with + * Applications of [flowOn], [buffer], and [produceIn] _after_ this operator are fused with * its concurrent merging so that only one properly configured channel is used for execution of merging logic. */ @ExperimentalCoroutinesApi @@ -126,7 +126,7 @@ public fun merge(vararg flows: Flow): Flow = flows.asIterable().merge( * * ### Operator fusion * - * Applications of [flowOn], [buffer], [produceIn], and [broadcastIn] _after_ this operator are fused with + * Applications of [flowOn], [buffer], and [produceIn] _after_ this operator are fused with * its concurrent merging so that only one properly configured channel is used for execution of merging logic. * * When [concurrency] is greater than 1, this operator is [buffered][buffer] by default diff --git a/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt b/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt index 4763d13b05..410955ce4d 100644 --- a/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt @@ -127,145 +127,6 @@ class ChannelBuildersFlowTest : TestBase() { finish(6) } - @Test - fun testBroadcastChannelAsFlow() = runTest { - val channel = broadcast { - repeat(10) { - send(it + 1) - } - } - - val sum = channel.asFlow().sum() - assertEquals(55, sum) - } - - @Test - fun testExceptionInBroadcast() = runTest { - expect(1) - val channel = broadcast(NonCancellable) { // otherwise failure will cancel scope as well - repeat(10) { - send(it + 1) - } - throw TestException() - } - assertEquals(15, channel.asFlow().take(5).sum()) - - // Workaround for JS bug - try { - channel.asFlow().collect { /* Do nothing */ } - expectUnreached() - } catch (e: TestException) { - finish(2) - } - } - - @Test - fun testBroadcastChannelAsFlowLimits() = runTest { - val channel = BroadcastChannel(1) - val flow = channel.asFlow().map { it * it }.drop(1).take(2) - - var expected = 0 - launch { - assertTrue(channel.trySend(1).isSuccess) // Handed to the coroutine - assertTrue(channel.trySend(2).isSuccess) // Buffered - assertFalse(channel.trySend(3).isSuccess) // Failed to offer - channel.send(3) - yield() - assertEquals(1, expected) - assertTrue(channel.trySend(4).isSuccess) // Handed to the coroutine - assertTrue(channel.trySend(5).isSuccess) // Buffered - assertFalse(channel.trySend(6).isSuccess) // Failed to offer - channel.send(6) - assertEquals(2, expected) - } - - val sum = flow.sum() - assertEquals(13, sum) - ++expected - val sum2 = flow.sum() - assertEquals(61, sum2) - ++expected - } - - @Test - fun flowAsBroadcast() = runTest { - val flow = flow { - repeat(10) { - emit(it) - } - } - - val channel = flow.broadcastIn(this) - assertEquals((0..9).toList(), channel.openSubscription().toList()) - } - - @Test - fun flowAsBroadcastMultipleSubscription() = runTest { - val flow = flow { - repeat(10) { - emit(it) - } - } - - val broadcast = flow.broadcastIn(this) - val channel = broadcast.openSubscription() - val channel2 = broadcast.openSubscription() - - assertEquals(0, channel.receive()) - assertEquals(0, channel2.receive()) - yield() - assertEquals(1, channel.receive()) - assertEquals(1, channel2.receive()) - - channel.cancel() - channel2.cancel() - yield() - ensureActive() - } - - @Test - fun flowAsBroadcastException() = runTest { - val flow = flow { - repeat(10) { - emit(it) - } - - throw TestException() - } - - val channel = flow.broadcastIn(this + NonCancellable) - assertFailsWith { channel.openSubscription().toList() } - assertTrue(channel.isClosedForSend) // Failure in the flow fails the channel - } - - // Semantics of these tests puzzle me, we should figure out the way to prohibit such chains - @Test - fun testFlowAsBroadcastAsFlow() = runTest { - val flow = flow { - emit(1) - emit(2) - emit(3) - }.broadcastIn(this).asFlow() - - assertEquals(6, flow.sum()) - assertEquals(0, flow.sum()) // Well suddenly flow is no longer idempotent and cold - } - - @Test - fun testBroadcastAsFlowAsBroadcast() = runTest { - val channel = broadcast { - send(1) - }.asFlow().broadcastIn(this) - - channel.openSubscription().consumeEach { - assertEquals(1, it) - } - - channel.openSubscription().consumeEach { - fail() - } - } - @Test fun testProduceInAtomicity() = runTest { val flow = flowOf(1).onCompletion { expect(2) } From e62f8f712ce98517d63dfe7aa40b2689ebd584c5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 15 Apr 2021 22:57:35 +0300 Subject: [PATCH 35/55] Migration to Kotlin 1.5.0-RC (#2653) * Migrate inline classes to value classes * Adjust debugger/state recovery for new codegen * Temporarily disable R8 tests * Update webpack for new K/JS --- gradle.properties | 2 +- .../api/kotlinx-coroutines-slf4j.api | 4 -- js/example-frontend-js/build.gradle.kts | 2 +- .../api/kotlinx-coroutines-core.api | 16 ++++---- .../common/src/channels/Channel.kt | 4 +- .../jvm/resources/DebugProbesKt.bin | Bin 1728 -> 1714 bytes .../jvm/src/debug/internal/DebugProbesImpl.kt | 19 +++++---- .../stacktraces/select/testSelectJoin.txt | 8 ++-- .../StackTraceRecoveryNestedScopesTest.kt | 1 - .../StackTraceRecoveryNestedTest.kt | 2 +- .../StackTraceRecoverySelectTest.kt | 4 +- .../test/exceptions/StackTraceRecoveryTest.kt | 22 +++++----- .../test/DebugProbesTest.kt | 3 -- .../test/SanitizedProbesTest.kt | 1 - .../build.gradle.kts | 38 +++++++++--------- .../test/R8ServiceLoaderOptimizationTest.kt | 1 + 16 files changed, 59 insertions(+), 68 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0f3ccc1b55..a27edd12f4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ # Kotlin version=1.4.3-SNAPSHOT group=org.jetbrains.kotlinx -kotlin_version=1.4.30 +kotlin_version=1.5.0-RC # Dependencies junit_version=4.12 diff --git a/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api b/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api index a8bf271ba4..6b565d4c1a 100644 --- a/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api +++ b/integration/kotlinx-coroutines-slf4j/api/kotlinx-coroutines-slf4j.api @@ -3,11 +3,7 @@ public final class kotlinx/coroutines/slf4j/MDCContext : kotlin/coroutines/Abstr public fun ()V public fun (Ljava/util/Map;)V public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public final fun getContextMap ()Ljava/util/Map; - public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; - public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; public synthetic fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)V public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Ljava/util/Map;)V public synthetic fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Ljava/lang/Object; diff --git a/js/example-frontend-js/build.gradle.kts b/js/example-frontend-js/build.gradle.kts index 5b18966fcd..e5e7a3e2a5 100644 --- a/js/example-frontend-js/build.gradle.kts +++ b/js/example-frontend-js/build.gradle.kts @@ -23,5 +23,5 @@ kotlin { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-html-js:${version("html")}") - implementation(devNpm("html-webpack-plugin", "3.2.0")) + implementation(devNpm("html-webpack-plugin", "5.3.1")) } diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index 0d684b4454..dcbf62b565 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -267,7 +267,7 @@ public final class kotlinx/coroutines/Delay$DefaultImpls { public final class kotlinx/coroutines/DelayKt { public static final fun awaitCancellation (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun delay (JLkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun delay-VtjQ1oo (DLkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun delay-VtjQ1oo (JLkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlinx/coroutines/Dispatchers { @@ -535,9 +535,9 @@ public final class kotlinx/coroutines/TimeoutCancellationException : java/util/c public final class kotlinx/coroutines/TimeoutKt { public static final fun withTimeout (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun withTimeout-KLykuaI (DLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withTimeout-KLykuaI (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun withTimeoutOrNull (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun withTimeoutOrNull-KLykuaI (DLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withTimeoutOrNull-KLykuaI (JLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlinx/coroutines/YieldKt { @@ -920,7 +920,7 @@ public final class kotlinx/coroutines/flow/FlowKt { public static final fun count (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun debounce (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow; public static final fun debounce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow; - public static final fun debounce-HG0u8IE (Lkotlinx/coroutines/flow/Flow;D)Lkotlinx/coroutines/flow/Flow; + public static final fun debounce-HG0u8IE (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow; public static final fun debounceDuration (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow; public static final fun delayEach (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow; public static final fun delayFlow (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow; @@ -997,7 +997,7 @@ public final class kotlinx/coroutines/flow/FlowKt { public static final fun runningFold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow; public static final fun runningReduce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow; public static final fun sample (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow; - public static final fun sample-HG0u8IE (Lkotlinx/coroutines/flow/Flow;D)Lkotlinx/coroutines/flow/Flow; + public static final fun sample-HG0u8IE (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow; public static final fun scan (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow; public static final fun scanFold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow; public static final fun scanReduce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow; @@ -1084,8 +1084,8 @@ public final class kotlinx/coroutines/flow/SharingStarted$Companion { } public final class kotlinx/coroutines/flow/SharingStartedKt { - public static final fun WhileSubscribed-5qebJ5I (Lkotlinx/coroutines/flow/SharingStarted$Companion;DD)Lkotlinx/coroutines/flow/SharingStarted; - public static synthetic fun WhileSubscribed-5qebJ5I$default (Lkotlinx/coroutines/flow/SharingStarted$Companion;DDILjava/lang/Object;)Lkotlinx/coroutines/flow/SharingStarted; + public static final fun WhileSubscribed-5qebJ5I (Lkotlinx/coroutines/flow/SharingStarted$Companion;JJ)Lkotlinx/coroutines/flow/SharingStarted; + public static synthetic fun WhileSubscribed-5qebJ5I$default (Lkotlinx/coroutines/flow/SharingStarted$Companion;JJILjava/lang/Object;)Lkotlinx/coroutines/flow/SharingStarted; } public abstract interface class kotlinx/coroutines/flow/StateFlow : kotlinx/coroutines/flow/SharedFlow { @@ -1215,7 +1215,7 @@ public abstract interface class kotlinx/coroutines/selects/SelectInstance { } public final class kotlinx/coroutines/selects/SelectKt { - public static final fun onTimeout-8Mi8wO0 (Lkotlinx/coroutines/selects/SelectBuilder;DLkotlin/jvm/functions/Function1;)V + public static final fun onTimeout-8Mi8wO0 (Lkotlinx/coroutines/selects/SelectBuilder;JLkotlin/jvm/functions/Function1;)V public static final fun select (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index f006efc720..1812bddfa7 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -363,8 +363,8 @@ public interface ReceiveChannel { * * The closed result represents an operation attempt to a closed channel and also implies that the operation has failed. */ -@Suppress("UNCHECKED_CAST") -public inline class ChannelResult +@JvmInline +public value class ChannelResult @PublishedApi internal constructor(@PublishedApi internal val holder: Any?) { /** * Returns `true` if this instance represents a successful diff --git a/kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin b/kotlinx-coroutines-core/jvm/resources/DebugProbesKt.bin index ea8c9da4c2f57926d3a26ab4a34e82832fde3d51..397aaf67ac52cdf6b99d39da8ddf1065bc098293 100644 GIT binary patch delta 305 zcmX@WyNQ?U)W2Q(7#J8#80_G6Vd zHU|o@F)#u#7lQ?m%>!gJFz_*OGDrbwOCV-qU}vxb(jZkFK-zlp0#*^AZUHul$!A&F zZ4f5#pqLH+vGwv zVPj1OZBGUr23=1EJqG<~2F79r0|vur2F_vz)?x;!Vg{pP5L>#K!MK<~u9!i&n85@9 D7akyi delta 352 zcma)$y-or_6otRpaYZ&j?SeutG8GT79WCWeXn6=;M8wL%P+R&yhC7=m zYGrZeoO3hxn|aP}^U?d~>l@H#ypCRMd|u5S!jx)}`THDc~>qi#}M(EK`$O>^N6opIir6gPl JWuYou?*MEiB5?o! diff --git a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt index 49294d73e1..88be35e328 100644 --- a/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt +++ b/kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt @@ -281,7 +281,7 @@ internal object DebugProbesImpl { it.fileName == "ContinuationImpl.kt" } - val (continuationStartFrame, frameSkipped) = findContinuationStartIndex( + val (continuationStartFrame, delta) = findContinuationStartIndex( indexOfResumeWith, actualTrace, coroutineTrace @@ -289,7 +289,6 @@ internal object DebugProbesImpl { if (continuationStartFrame == -1) return coroutineTrace - val delta = if (frameSkipped) 1 else 0 val expectedSize = indexOfResumeWith + coroutineTrace.size - continuationStartFrame - 1 - delta val result = ArrayList(expectedSize) for (index in 0 until indexOfResumeWith - delta) { @@ -311,16 +310,22 @@ internal object DebugProbesImpl { * If method above `resumeWith` has no line number (thus it is `stateMachine.invokeSuspend`), * it's skipped and attempt to match next one is made because state machine could have been missing in the original coroutine stacktrace. * - * Returns index of such frame (or -1) and flag indicating whether frame with state machine was skipped + * Returns index of such frame (or -1) and number of skipped frames (up to 2, for state machine and for access$). */ private fun findContinuationStartIndex( indexOfResumeWith: Int, actualTrace: Array, coroutineTrace: List - ): Pair { - val result = findIndexOfFrame(indexOfResumeWith - 1, actualTrace, coroutineTrace) - if (result == -1) return findIndexOfFrame(indexOfResumeWith - 2, actualTrace, coroutineTrace) to true - return result to false + ): Pair { + /* + * Since Kotlin 1.5.0 we have these access$ methods that we have to skip. + * So we have to test next frame for invokeSuspend, for $access and for actual suspending call. + */ + repeat(3) { + val result = findIndexOfFrame(indexOfResumeWith - 1 - it, actualTrace, coroutineTrace) + if (result != -1) return result to it + } + return -1 to 0 } private fun findIndexOfFrame( diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt index 3f404cd937..5a9a65e400 100644 --- a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt +++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/select/testSelectJoin.txt @@ -1,7 +1,7 @@ kotlinx.coroutines.RecoverableTestException - at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$doSelect$$inlined$select$lambda$1.invokeSuspend(StackTraceRecoverySelectTest.kt:33) + at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$doSelect$2$1.invokeSuspend(StackTraceRecoverySelectTest.kt) at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt) - at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$testSelectJoin$1.invokeSuspend(StackTraceRecoverySelectTest.kt:20) + at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$testSelectJoin$1.invokeSuspend(StackTraceRecoverySelectTest.kt) Caused by: kotlinx.coroutines.RecoverableTestException - at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$doSelect$$inlined$select$lambda$1.invokeSuspend(StackTraceRecoverySelectTest.kt:33) - at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) + at kotlinx.coroutines.exceptions.StackTraceRecoverySelectTest$doSelect$2$1.invokeSuspend(StackTraceRecoverySelectTest.kt) + at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt) diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt index bc9d056668..dbb1ead568 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedScopesTest.kt @@ -86,7 +86,6 @@ class StackTraceRecoveryNestedScopesTest : TestBase() { "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$callWithTimeout\$2.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:37)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$callCoroutineScope\$2.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:43)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$testAwaitNestedScopes\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:68)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest.verifyAwait(StackTraceRecoveryNestedScopesTest.kt:76)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedScopesTest\$testAwaitNestedScopes\$1.invokeSuspend(StackTraceRecoveryNestedScopesTest.kt:71)\n" + "Caused by: kotlinx.coroutines.RecoverableTestException\n" + diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedTest.kt index 5073b7fdfa..02607c0308 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedTest.kt @@ -58,7 +58,7 @@ class StackTraceRecoveryNestedTest : TestBase() { try { rootAsync.awaitRootLevel() } catch (e: RecoverableTestException) { - e.verifyException("await\$suspendImpl", "awaitRootLevel") + e.verifyException("awaitRootLevel") finish(8) } } diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoverySelectTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoverySelectTest.kt index 290420e49a..0d7648c54d 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoverySelectTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoverySelectTest.kt @@ -45,9 +45,9 @@ class StackTraceRecoverySelectTest : TestBase() { private suspend fun doSelectAwait(deferred: Deferred): Int { return select { deferred.onAwait { - yield() // Hide the stackstrace + yield() // Hide the frame 42 } } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt index 9afcab537d..574b27a815 100644 --- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt +++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt @@ -34,14 +34,13 @@ class StackTraceRecoveryTest : TestBase() { val deferred = createDeferred(3) val traces = listOf( "java.util.concurrent.ExecutionException\n" + - "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testAsync\$1\$1\$1.invokeSuspend(StackTraceRecoveryTest.kt:99)\n" + + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testAsync\$1\$createDeferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:99)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.oneMoreNestedMethod(StackTraceRecoveryTest.kt:49)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.nestedMethod(StackTraceRecoveryTest.kt:44)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testAsync\$1.invokeSuspend(StackTraceRecoveryTest.kt:17)\n", "Caused by: java.util.concurrent.ExecutionException\n" + - "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testAsync\$1\$1\$1.invokeSuspend(StackTraceRecoveryTest.kt:21)\n" + + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testAsync\$1\$createDeferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:21)\n" + "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)\n" ) nestedMethod(deferred, *traces.toTypedArray()) @@ -59,7 +58,6 @@ class StackTraceRecoveryTest : TestBase() { "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCompletedAsync\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:44)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.oneMoreNestedMethod(StackTraceRecoveryTest.kt:81)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.nestedMethod(StackTraceRecoveryTest.kt:75)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCompletedAsync\$1.invokeSuspend(StackTraceRecoveryTest.kt:71)", @@ -94,7 +92,6 @@ class StackTraceRecoveryTest : TestBase() { "kotlinx.coroutines.RecoverableTestException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testWithContext\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:143)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.innerMethod(StackTraceRecoveryTest.kt:158)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$outerMethod\$2.invokeSuspend(StackTraceRecoveryTest.kt:151)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.outerMethod(StackTraceRecoveryTest.kt:150)\n" + @@ -132,7 +129,6 @@ class StackTraceRecoveryTest : TestBase() { "kotlinx.coroutines.RecoverableTestException\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCoroutineScope\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryTest.kt:143)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.innerMethod(StackTraceRecoveryTest.kt:158)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$outerScopedMethod\$2\$1.invokeSuspend(StackTraceRecoveryTest.kt:193)\n" + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$outerScopedMethod\$2.invokeSuspend(StackTraceRecoveryTest.kt:151)\n" + @@ -228,13 +224,13 @@ class StackTraceRecoveryTest : TestBase() { val e = exception assertNotNull(e) verifyStackTrace(e, "kotlinx.coroutines.RecoverableTestException\n" + - "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.throws(StackTraceRecoveryTest.kt:280)\n" + - "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$throws\$1.invokeSuspend(StackTraceRecoveryTest.kt)\n" + - "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + - "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.awaiter(StackTraceRecoveryTest.kt:285)\n" + - "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testNonDispatchedRecovery\$await\$1.invokeSuspend(StackTraceRecoveryTest.kt:291)\n" + - "Caused by: kotlinx.coroutines.RecoverableTestException") + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.throws(StackTraceRecoveryTest.kt:280)\n" + + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.access\$throws(StackTraceRecoveryTest.kt:20)\n" + + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$throws\$1.invokeSuspend(StackTraceRecoveryTest.kt)\n" + + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.awaiter(StackTraceRecoveryTest.kt:285)\n" + + "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testNonDispatchedRecovery\$await\$1.invokeSuspend(StackTraceRecoveryTest.kt:291)\n" + + "Caused by: kotlinx.coroutines.RecoverableTestException") } private class Callback(val cont: CancellableContinuation<*>) diff --git a/kotlinx-coroutines-debug/test/DebugProbesTest.kt b/kotlinx-coroutines-debug/test/DebugProbesTest.kt index 1acaee2b62..bc0c1e3f24 100644 --- a/kotlinx-coroutines-debug/test/DebugProbesTest.kt +++ b/kotlinx-coroutines-debug/test/DebugProbesTest.kt @@ -21,7 +21,6 @@ class DebugProbesTest : DebugTestBase() { "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:14)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:49)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:44)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsync\$1.invokeSuspend(DebugProbesTest.kt:17)\n", @@ -42,7 +41,6 @@ class DebugProbesTest : DebugTestBase() { "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:62)\n" + @@ -74,7 +72,6 @@ class DebugProbesTest : DebugTestBase() { "java.util.concurrent.ExecutionException\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$createDeferred\$1.invokeSuspend(DebugProbesTest.kt:16)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.oneMoreNestedMethod(DebugProbesTest.kt:71)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest.nestedMethod(DebugProbesTest.kt:66)\n" + "\tat kotlinx.coroutines.debug.DebugProbesTest\$testAsyncWithSanitizedProbes\$1\$1.invokeSuspend(DebugProbesTest.kt:87)\n" + diff --git a/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt b/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt index a329be7bcd..779c624533 100644 --- a/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt +++ b/kotlinx-coroutines-debug/test/SanitizedProbesTest.kt @@ -28,7 +28,6 @@ class SanitizedProbesTest : DebugTestBase() { "java.util.concurrent.ExecutionException\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest\$createDeferredNested\$1.invokeSuspend(SanitizedProbesTest.kt:97)\n" + "\tat _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)\n" + - "\tat kotlinx.coroutines.DeferredCoroutine.await\$suspendImpl(Builders.common.kt:99)\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.oneMoreNestedMethod(SanitizedProbesTest.kt:67)\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest.nestedMethod(SanitizedProbesTest.kt:61)\n" + "\tat definitely.not.kotlinx.coroutines.SanitizedProbesTest\$testRecoveredStackTrace\$1.invokeSuspend(SanitizedProbesTest.kt:50)\n" + diff --git a/ui/kotlinx-coroutines-android/build.gradle.kts b/ui/kotlinx-coroutines-android/build.gradle.kts index 08d45ffe71..2af2d4f3a9 100644 --- a/ui/kotlinx-coroutines-android/build.gradle.kts +++ b/ui/kotlinx-coroutines-android/build.gradle.kts @@ -2,10 +2,6 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink -import org.jetbrains.dokka.gradle.DokkaTask -import java.net.URL - configurations { create("r8") } @@ -22,7 +18,7 @@ dependencies { testImplementation("org.robolectric:robolectric:${version("robolectric")}") testImplementation("org.smali:baksmali:${version("baksmali")}") - "r8"("com.android.tools.build:builder:4.0.0-alpha06") // Contains r8-2.0.4-dev + "r8"("com.android.tools.build:builder:7.0.0-alpha14") } val optimizedDexDir = File(buildDir, "dex-optim/") @@ -45,21 +41,23 @@ val runR8NoOptim by tasks.registering(RunR8::class) { dependsOn("jar") } -tasks.test { - // Ensure the R8-processed dex is built and supply its path as a property to the test. - dependsOn(runR8) - dependsOn(runR8NoOptim) - - inputs.files(optimizedDexFile, unOptimizedDexFile) - - systemProperty("dexPath", optimizedDexFile.absolutePath) - systemProperty("noOptimDexPath", unOptimizedDexFile.absolutePath) - - // Output custom metric with the size of the optimized dex - doLast { - println("##teamcity[buildStatisticValue key='optimizedDexSize' value='${optimizedDexFile.length()}']") - } -} +// TODO: Disable the test until we have published version of R8 that supports Kotlin 1.5.0 metadata + +//tasks.test { +// // Ensure the R8-processed dex is built and supply its path as a property to the test. +// dependsOn(runR8) +// dependsOn(runR8NoOptim) +// +// inputs.files(optimizedDexFile, unOptimizedDexFile) +// +// systemProperty("dexPath", optimizedDexFile.absolutePath) +// systemProperty("noOptimDexPath", unOptimizedDexFile.absolutePath) +// +// // Output custom metric with the size of the optimized dex +// doLast { +// println("##teamcity[buildStatisticValue key='optimizedDexSize' value='${optimizedDexFile.length()}']") +// } +//} externalDocumentationLink( url = "https://developer.android.com/reference/" diff --git a/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt b/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt index 47beb85bbf..5d60d641aa 100644 --- a/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt +++ b/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt @@ -11,6 +11,7 @@ import java.io.* import java.util.stream.* import kotlin.test.* +@Ignore class R8ServiceLoaderOptimizationTest : TestBase() { private val r8Dex = File(System.getProperty("dexPath")!!).asDexFile() private val r8DexNoOptim = File(System.getProperty("noOptimDexPath")!!).asDexFile() From 95ad4449e56fec0c3b800d6b0a44218a6822c1e6 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 19 Apr 2021 16:30:09 +0300 Subject: [PATCH 36/55] Fix missing indent in @InternalCoroutinesApi warning message --- kotlinx-coroutines-core/common/src/Annotations.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/common/src/Annotations.kt b/kotlinx-coroutines-core/common/src/Annotations.kt index 70adad9b97..1ade389d37 100644 --- a/kotlinx-coroutines-core/common/src/Annotations.kt +++ b/kotlinx-coroutines-core/common/src/Annotations.kt @@ -59,7 +59,7 @@ public annotation class ObsoleteCoroutinesApi @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, AnnotationTarget.PROPERTY) @RequiresOptIn( level = RequiresOptIn.Level.ERROR, message = "This is an internal kotlinx.coroutines API that " + - "should not be used from outside of kotlinx.coroutines. No compatibility guarantees are provided." + + "should not be used from outside of kotlinx.coroutines. No compatibility guarantees are provided. " + "It is recommended to report your use-case of internal API to kotlinx.coroutines issue tracker, " + "so stable API could be provided instead" ) From 8bb5210c2825c7d248eb172d06975c5628aa6671 Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Tue, 20 Apr 2021 11:21:32 +0300 Subject: [PATCH 37/55] Consistently handle exceptions in reactive streams (#2646) * Fixed `PublisherCoroutine`, `rxObservable`, and `Flow.toPublisher` ignoring cancellations. * Fatal exceptions are not treated in a special manner by us anymore. Instead, we follow the requirement in the reactive streams specification that, in case some method of `Subscriber` throws, that subscriber MUST be considered canceled, and the exception MUST be reported in someplace other than `onError`. * Fixed `trySend` sometimes throwing in `PublisherCoroutine` and `rxObservable`. * When an exception happens inside a cancellation handler, we now consistently throw the original exception passed to the handler, with the new exception added as suppressed. * Fixed `PublisherCoroutine` and `rxObservable` claiming that the channel is not closed for send for some time after `close()` has finished. * Fixed publishers sometimes signalling `onComplete()` after cancellation even though their streams are not finite. Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/2173 --- .../test/FlowAsPublisherTest.kt | 21 ++- .../test/IntegrationTest.kt | 20 ++- .../test/PublishTest.kt | 158 ++++++++++++++---- .../src/Publish.kt | 155 ++++++++++------- .../src/ReactiveFlow.kt | 31 ++-- .../test/FlowAsPublisherTest.kt | 24 ++- .../test/IntegrationTest.kt | 51 +++--- .../test/PublishTest.kt | 158 ++++++++++++++---- .../test/PublisherRequestStressTest.kt | 14 +- .../kotlinx-coroutines-reactor/src/Flux.kt | 9 +- .../kotlinx-coroutines-reactor/src/Mono.kt | 5 +- .../test/FluxTest.kt | 37 +++- reactive/kotlinx-coroutines-rx2/README.md | 4 - .../src/RxCancellable.kt | 1 + .../src/RxCompletable.kt | 7 +- .../kotlinx-coroutines-rx2/src/RxMaybe.kt | 7 +- .../src/RxObservable.kt | 117 ++++++------- .../kotlinx-coroutines-rx2/src/RxSingle.kt | 7 +- .../test/FlowableExceptionHandlingTest.kt | 16 +- .../test/IntegrationTest.kt | 16 ++ .../test/ObservableExceptionHandlingTest.kt | 37 ++-- .../src/RxCancellable.kt | 1 + .../src/RxCompletable.kt | 7 +- .../kotlinx-coroutines-rx3/src/RxMaybe.kt | 7 +- .../src/RxObservable.kt | 112 ++++++------- .../kotlinx-coroutines-rx3/src/RxSingle.kt | 7 +- .../test/FlowableExceptionHandlingTest.kt | 16 +- .../test/IntegrationTest.kt | 15 ++ .../test/ObservableExceptionHandlingTest.kt | 37 ++-- 29 files changed, 740 insertions(+), 357 deletions(-) diff --git a/reactive/kotlinx-coroutines-jdk9/test/FlowAsPublisherTest.kt b/reactive/kotlinx-coroutines-jdk9/test/FlowAsPublisherTest.kt index 488695dea2..b860e16209 100644 --- a/reactive/kotlinx-coroutines-jdk9/test/FlowAsPublisherTest.kt +++ b/reactive/kotlinx-coroutines-jdk9/test/FlowAsPublisherTest.kt @@ -15,7 +15,7 @@ class FlowAsPublisherTest : TestBase() { @Test fun testErrorOnCancellationIsReported() { expect(1) - flow { + flow { try { emit(2) } finally { @@ -50,13 +50,13 @@ class FlowAsPublisherTest : TestBase() { @Test fun testCancellationIsNotReported() { expect(1) - flow { + flow { emit(2) }.asPublisher().subscribe(object : JFlow.Subscriber { private lateinit var subscription: JFlow.Subscription override fun onComplete() { - expect(3) + expectUnreached() } override fun onSubscribe(s: JFlow.Subscription?) { @@ -73,6 +73,21 @@ class FlowAsPublisherTest : TestBase() { expectUnreached() } }) + finish(3) + } + + @Test + fun testFlowWithTimeout() = runTest { + val publisher = flow { + expect(2) + withTimeout(1) { delay(Long.MAX_VALUE) } + }.asPublisher() + try { + expect(1) + publisher.awaitFirstOrNull() + } catch (e: CancellationException) { + expect(3) + } finish(4) } } diff --git a/reactive/kotlinx-coroutines-jdk9/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-jdk9/test/IntegrationTest.kt index 5bfddfee17..5b3542ad89 100644 --- a/reactive/kotlinx-coroutines-jdk9/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-jdk9/test/IntegrationTest.kt @@ -5,10 +5,12 @@ package kotlinx.coroutines.jdk9 import kotlinx.coroutines.* +import kotlinx.coroutines.exceptions.* import org.junit.Test import kotlinx.coroutines.flow.flowOn import org.junit.runner.* import org.junit.runners.* +import kotlin.contracts.* import java.util.concurrent.Flow as JFlow import kotlin.coroutines.* import kotlin.test.* @@ -129,4 +131,20 @@ class IntegrationTest( assertEquals(n, last) } -} \ No newline at end of file +} + +@OptIn(ExperimentalContracts::class) +internal suspend inline fun assertCallsExceptionHandlerWith( + crossinline operation: suspend (CoroutineExceptionHandler) -> Unit): E { + contract { + callsInPlace(operation, InvocationKind.EXACTLY_ONCE) + } + val handler = CapturingHandler() + return withContext(handler) { + operation(handler) + handler.getException().let { + assertTrue(it is E, it.toString()) + it + } + } +} diff --git a/reactive/kotlinx-coroutines-jdk9/test/PublishTest.kt b/reactive/kotlinx-coroutines-jdk9/test/PublishTest.kt index 1a36a389fa..3682d5e318 100644 --- a/reactive/kotlinx-coroutines-jdk9/test/PublishTest.kt +++ b/reactive/kotlinx-coroutines-jdk9/test/PublishTest.kt @@ -5,6 +5,7 @@ package kotlinx.coroutines.jdk9 import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* import org.junit.Test import java.util.concurrent.Flow as JFlow import kotlin.test.* @@ -121,44 +122,110 @@ class PublishTest : TestBase() { finish(7) } + /** Tests that, as soon as `ProducerScope.close` is called, `isClosedForSend` starts returning `true`. */ @Test - fun testOnNextError() = runTest { + fun testChannelClosing() = runTest { expect(1) - val publisher = flowPublish(currentDispatcher()) { + val publisher = flowPublish(Dispatchers.Unconfined) { + expect(3) + close() + assert(isClosedForSend) expect(4) - try { - send("OK") - } catch(e: Throwable) { - expect(6) - assert(e is TestException) - } } - expect(2) + try { + expect(2) + publisher.awaitFirstOrNull() + } catch (e: CancellationException) { + expect(5) + } + finish(6) + } + + @Test + fun testOnNextError() = runTest { val latch = CompletableDeferred() - publisher.subscribe(object : JFlow.Subscriber { - override fun onComplete() { - expectUnreached() + expect(1) + assertCallsExceptionHandlerWith { exceptionHandler -> + val publisher = flowPublish(currentDispatcher() + exceptionHandler) { + expect(4) + try { + send("OK") + } catch (e: Throwable) { + expect(6) + assert(e is TestException) + assert(isClosedForSend) + latch.complete(Unit) + } } + expect(2) + publisher.subscribe(object : JFlow.Subscriber { + override fun onComplete() { + expectUnreached() + } - override fun onSubscribe(s: JFlow.Subscription) { - expect(3) - s.request(1) - } + override fun onSubscribe(s: JFlow.Subscription) { + expect(3) + s.request(1) + } - override fun onNext(t: String) { - expect(5) - assertEquals("OK", t) - throw TestException() - } + override fun onNext(t: String) { + expect(5) + assertEquals("OK", t) + throw TestException() + } - override fun onError(t: Throwable) { - expect(7) - assert(t is TestException) - latch.complete(Unit) + override fun onError(t: Throwable) { + expectUnreached() + } + }) + latch.await() + } + finish(7) + } + + /** Tests the behavior when a call to `onNext` fails after the channel is already closed. */ + @Test + fun testOnNextErrorAfterCancellation() = runTest { + assertCallsExceptionHandlerWith { handler -> + var producerScope: ProducerScope? = null + CompletableDeferred() + expect(1) + var job: Job? = null + val publisher = flowPublish(handler + Dispatchers.Unconfined) { + producerScope = this + expect(4) + job = launch { + delay(Long.MAX_VALUE) + } } - }) - latch.await() - finish(8) + expect(2) + publisher.subscribe(object: JFlow.Subscriber { + override fun onSubscribe(s: JFlow.Subscription) { + expect(3) + s.request(Long.MAX_VALUE) + } + override fun onNext(t: Int) { + expect(6) + assertEquals(1, t) + job!!.cancel() + throw TestException() + } + override fun onError(t: Throwable?) { + /* Correct changes to the implementation could lead to us entering or not entering this method, but + it only matters that if we do, it is the "correct" exception that was validly used to cancel the + coroutine that gets passed here and not `TestException`. */ + assertTrue(t is CancellationException) + } + override fun onComplete() { expectUnreached() } + }) + expect(5) + val result: ChannelResult = producerScope!!.trySend(1) + val e = result.exceptionOrNull()!! + assertTrue(e is CancellationException, "The actual error: $e") + assertTrue(producerScope!!.isClosedForSend) + assertTrue(result.isFailure) + } + finish(7) } @Test @@ -182,4 +249,39 @@ class PublishTest : TestBase() { fun testIllegalArgumentException() { assertFailsWith { flowPublish(Job()) { } } } + + /** Tests that `trySend` doesn't throw in `flowPublish`. */ + @Test + fun testTrySendNotThrowing() = runTest { + var producerScope: ProducerScope? = null + expect(1) + val publisher = flowPublish(Dispatchers.Unconfined) { + producerScope = this + expect(3) + delay(Long.MAX_VALUE) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + expect(2) + publisher.awaitFirstOrNull() + expectUnreached() + } + job.cancel() + expect(4) + val result = producerScope!!.trySend(1) + assertTrue(result.isFailure) + finish(5) + } + + /** Tests that all methods on `flowPublish` fail without closing the channel when attempting to emit `null`. */ + @Test + fun testEmittingNull() = runTest { + val publisher = flowPublish { + assertFailsWith { send(null) } + assertFailsWith { trySend(null) } + @Suppress("DEPRECATION") + assertFailsWith { offer(null) } + send("OK") + } + assertEquals("OK", publisher.awaitFirstOrNull()) + } } \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 383a17d836..37113849ab 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.selects.* import kotlinx.coroutines.sync.* import org.reactivestreams.* import kotlin.coroutines.* -import kotlin.internal.* /** * Creates cold reactive [Publisher] that runs a given [block] in a coroutine. @@ -74,29 +73,26 @@ public class PublisherCoroutine( private val _nRequested = atomic(0L) // < 0 when closed (CLOSED or SIGNALLED) @Volatile - private var cancelled = false // true when Subscription.cancel() is invoked + private var cancelled = false // true after Subscription.cancel() is invoked - override val isClosedForSend: Boolean get() = isCompleted + override val isClosedForSend: Boolean get() = !isActive override fun close(cause: Throwable?): Boolean = cancelCoroutine(cause) override fun invokeOnClose(handler: (Throwable?) -> Unit): Nothing = throw UnsupportedOperationException("PublisherCoroutine doesn't support invokeOnClose") - override fun trySend(element: T): ChannelResult { - if (!mutex.tryLock()) return ChannelResult.failure() - doLockedNext(element) - return ChannelResult.success(Unit) - } + override fun trySend(element: T): ChannelResult = + if (!mutex.tryLock()) { + ChannelResult.failure() + } else { + when (val throwable = doLockedNext(element)) { + null -> ChannelResult.success(Unit) + else -> ChannelResult.closed(throwable) + } + } public override suspend fun send(element: T) { - // fast-path -- try send without suspension - if (trySend(element).isSuccess) return - // slow-path does suspend - return sendSuspend(element) - } - - private suspend fun sendSuspend(element: T) { mutex.lock() - doLockedNext(element) + doLockedNext(element)?.let { throw it } } override val onSend: SelectClause2> @@ -106,13 +102,13 @@ public class PublisherCoroutine( @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") override fun registerSelectClause2(select: SelectInstance, element: T, block: suspend (SendChannel) -> R) { mutex.onLock.registerSelectClause2(select, null) { - doLockedNext(element) + doLockedNext(element)?.let { throw it } block(this) } } /* - * This code is not trivial because of the two properties: + * This code is not trivial because of the following properties: * 1. It ensures conformance to the reactive specification that mandates that onXXX invocations should not * be concurrent. It uses Mutex to protect all onXXX invocation and ensure conformance even when multiple * coroutines are invoking `send` function. @@ -121,27 +117,61 @@ public class PublisherCoroutine( * globally-scoped coroutine that is invoking `send` outside of this context. Without extra precaution this may * lead to `onNext` that is concurrent with `onComplete/onError`, so that is why signalling for * `onComplete/onError` is also done under the same mutex. + * 3. The reactive specification forbids emitting more elements than requested, so `onNext` is forbidden until the + * subscriber actually requests some elements. This is implemented by the mutex being locked when emitting + * elements is not permitted (`_nRequested.value == 0`). */ - // assert: mutex.isLocked() - private fun doLockedNext(elem: T) { - // check if already closed for send, note that isActive becomes false as soon as cancel() is invoked, - // because the job is cancelled, so this check also ensure conformance to the reactive specification's - // requirement that after cancellation requested we don't call onXXX + /** + * Attempts to emit a value to the subscriber and, if back-pressure permits this, unlock the mutex. + * + * Requires that the caller has locked the mutex before this invocation. + * + * If the channel is closed, returns the corresponding [Throwable]; otherwise, returns `null` to denote success. + * + * @throws NullPointerException if the passed element is `null` + */ + private fun doLockedNext(elem: T): Throwable? { + if (elem == null) { + unlockAndCheckCompleted() + throw NullPointerException("Attempted to emit `null` inside a reactive publisher") + } + /** This guards against the case when the caller of this function managed to lock the mutex not because some + * elements were requested--and thus it is permitted to call `onNext`--but because the channel was closed. + * + * It may look like there is a race condition here between `isActive` and a concurrent cancellation, but it's + * okay for a cancellation to happen during `onNext`, as the reactive spec only requires that we *eventually* + * stop signalling the subscriber. */ if (!isActive) { unlockAndCheckCompleted() - throw getCancellationException() + return getCancellationException() } - // notify subscriber + // notify the subscriber try { subscriber.onNext(elem) - } catch (e: Throwable) { - // If onNext fails with exception, then we cancel coroutine (with this exception) and then rethrow it - // to abort the corresponding send/offer invocation. From the standpoint of coroutines machinery, - // this failure is essentially equivalent to a failure of a child coroutine. - cancelCoroutine(e) + } catch (cause: Throwable) { + /** The reactive streams spec forbids the subscribers from throwing from [Subscriber.onNext] unless the + * element is `null`, which we check not to be the case. Therefore, we report this exception to the handler + * for uncaught exceptions and consider the subscription cancelled, as mandated by + * https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#2.13. + * + * Some reactive implementations, like RxJava or Reactor, are known to throw from [Subscriber.onNext] if the + * execution encounters an exception they consider to be "fatal", like [VirtualMachineError] or + * [ThreadDeath]. Us using the handler for the undeliverable exceptions to signal "fatal" exceptions is + * inconsistent with RxJava and Reactor, which attempt to bubble the exception up the call chain as soon as + * possible. However, we can't do much better here, as simply throwing from all methods indiscriminately + * would violate the contracts we place on them. */ + cancelled = true + val causeDelivered = close(cause) unlockAndCheckCompleted() - throw e + return if (causeDelivered) { + // `cause` is the reason this channel is closed + cause + } else { + // Someone else closed the channel during `onNext`. We report `cause` as an undeliverable exception. + exceptionOnCancelHandler(cause, context) + getCancellationException() + } } // now update nRequested while (true) { // lock-free loop on nRequested @@ -152,12 +182,13 @@ public class PublisherCoroutine( if (_nRequested.compareAndSet(current, updated)) { if (updated == 0L) { // return to keep locked due to back-pressure - return + return null } break // unlock if updated > 0 } } unlockAndCheckCompleted() + return null } private fun unlockAndCheckCompleted() { @@ -177,38 +208,31 @@ public class PublisherCoroutine( // assert: mutex.isLocked() & isCompleted private fun doLockedSignalCompleted(cause: Throwable?, handled: Boolean) { try { - if (_nRequested.value >= CLOSED) { - _nRequested.value = SIGNALLED // we'll signal onError/onCompleted (that the final state -- no CAS needed) - // Specification requires that after cancellation requested we don't call onXXX - if (cancelled) { - // If the parent had failed to handle our exception, then we must not lose this exception - if (cause != null && !handled) exceptionOnCancelHandler(cause, context) - return - } - + if (_nRequested.value == SIGNALLED) + return + _nRequested.value = SIGNALLED // we'll signal onError/onCompleted (the final state, so no CAS needed) + // Specification requires that after the cancellation is requested we eventually stop calling onXXX + if (cancelled) { + // If the parent failed to handle this exception, then we must not lose the exception + if (cause != null && !handled) exceptionOnCancelHandler(cause, context) + return + } + if (cause == null) { try { - if (cause != null && cause !is CancellationException) { - /* - * Reactive frameworks have two types of exceptions: regular and fatal. - * Regular are passed to onError. - * Fatal can be passed to onError, but even the standard implementations **can just swallow it** (e.g. see #1297). - * Such behaviour is inconsistent, leads to silent failures and we can't possibly know whether - * the cause will be handled by onError (and moreover, it depends on whether a fatal exception was - * thrown by subscriber or upstream). - * To make behaviour consistent and least surprising, we always handle fatal exceptions - * by coroutines machinery, anyway, they should not be present in regular program flow, - * thus our goal here is just to expose it as soon as possible. - */ - subscriber.onError(cause) - if (!handled && cause.isFatal()) { - exceptionOnCancelHandler(cause, context) - } - } else { - subscriber.onComplete() - } + subscriber.onComplete() } catch (e: Throwable) { handleCoroutineException(context, e) } + } else { + try { + // This can't be the cancellation exception from `cancel`, as then `cancelled` would be `true`. + subscriber.onError(cause) + } catch (e: Throwable) { + if (e !== cause) { + cause.addSuppressed(e) + } + handleCoroutineException(context, cause) + } } } finally { mutex.unlock() @@ -217,13 +241,13 @@ public class PublisherCoroutine( override fun request(n: Long) { if (n <= 0) { - // Specification requires IAE for n <= 0 + // Specification requires to call onError with IAE for n <= 0 cancelCoroutine(IllegalArgumentException("non-positive subscription request $n")) return } while (true) { // lock-free loop for nRequested val cur = _nRequested.value - if (cur < 0) return // already closed for send, ignore requests + if (cur < 0) return // already closed for send, ignore requests, as mandated by the reactive streams spec var upd = cur + n if (upd < 0 || n == Long.MAX_VALUE) upd = Long.MAX_VALUE @@ -231,6 +255,11 @@ public class PublisherCoroutine( if (_nRequested.compareAndSet(cur, upd)) { // unlock the mutex when we don't have back-pressure anymore if (cur == 0L) { + /** In a sense, after a successful CAS, it is this invocation, not the coroutine itself, that owns + * the lock, given that `upd` is necessarily strictly positive. Thus, no other operation has the + * right to lower the value on [_nRequested], it can only grow or become [CLOSED]. Therefore, it is + * impossible for any other operations to assume that they own the lock without actually acquiring + * it. */ unlockAndCheckCompleted() } return @@ -271,8 +300,6 @@ public class PublisherCoroutine( cancelled = true super.cancel(null) } - - private fun Throwable.isFatal() = this is VirtualMachineError || this is ThreadDeath || this is LinkageError } @Deprecated( diff --git a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt index f0245388bf..1f197f94ed 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.intrinsics.* import org.reactivestreams.* import java.util.* import kotlin.coroutines.* +import kotlinx.coroutines.internal.* /** * Transforms the given reactive [Publisher] into [Flow]. @@ -195,6 +196,8 @@ public class FlowSubscription( */ private val requested = atomic(0L) private val producer = atomic?>(createInitialContinuation()) + @Volatile + private var cancellationRequested = false // This code wraps startCoroutineCancellable into continuation private fun createInitialContinuation(): Continuation = Continuation(coroutineContext) { @@ -204,18 +207,25 @@ public class FlowSubscription( private suspend fun flowProcessing() { try { consumeFlow() - subscriber.onComplete() - } catch (e: Throwable) { - try { - if (e is CancellationException) { - subscriber.onComplete() - } else { - subscriber.onError(e) + } catch (cause: Throwable) { + @Suppress("INVISIBLE_MEMBER") + val unwrappedCause = unwrap(cause) + if (!cancellationRequested || isActive || unwrappedCause !== getCancellationException()) { + try { + subscriber.onError(cause) + } catch (e: Throwable) { + // Last ditch report + cause.addSuppressed(e) + handleCoroutineException(coroutineContext, cause) } - } catch (e: Throwable) { - // Last ditch report - handleCoroutineException(coroutineContext, e) } + return + } + // We only call this if `consumeFlow()` finished successfully + try { + subscriber.onComplete() + } catch (e: Throwable) { + handleCoroutineException(coroutineContext, e) } } @@ -239,6 +249,7 @@ public class FlowSubscription( } override fun cancel() { + cancellationRequested = true cancel(null) } diff --git a/reactive/kotlinx-coroutines-reactive/test/FlowAsPublisherTest.kt b/reactive/kotlinx-coroutines-reactive/test/FlowAsPublisherTest.kt index e7b8cb17ae..02c9e242e9 100644 --- a/reactive/kotlinx-coroutines-reactive/test/FlowAsPublisherTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/FlowAsPublisherTest.kt @@ -5,6 +5,7 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.* import org.junit.Test import org.reactivestreams.* @@ -15,7 +16,7 @@ class FlowAsPublisherTest : TestBase() { @Test fun testErrorOnCancellationIsReported() { expect(1) - flow { + flow { try { emit(2) } finally { @@ -50,13 +51,13 @@ class FlowAsPublisherTest : TestBase() { @Test fun testCancellationIsNotReported() { expect(1) - flow { + flow { emit(2) }.asPublisher().subscribe(object : Subscriber { private lateinit var subscription: Subscription override fun onComplete() { - expect(3) + expectUnreached() } override fun onSubscribe(s: Subscription?) { @@ -73,7 +74,7 @@ class FlowAsPublisherTest : TestBase() { expectUnreached() } }) - finish(4) + finish(3) } @Test @@ -149,4 +150,19 @@ class FlowAsPublisherTest : TestBase() { } finish(5) } + + @Test + fun testFlowWithTimeout() = runTest { + val publisher = flow { + expect(2) + withTimeout(1) { delay(Long.MAX_VALUE) } + }.asPublisher() + try { + expect(1) + publisher.awaitFirstOrNull() + } catch (e: CancellationException) { + expect(3) + } + finish(4) + } } diff --git a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt index 72479b5c6a..efe7ec7e45 100644 --- a/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/IntegrationTest.kt @@ -5,12 +5,14 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.* +import kotlinx.coroutines.exceptions.* import org.junit.Test import org.junit.runner.* import org.junit.runners.* import org.reactivestreams.* import java.lang.IllegalStateException import java.lang.RuntimeException +import kotlin.contracts.* import kotlin.coroutines.* import kotlin.test.* @@ -218,32 +220,35 @@ class IntegrationTest( }.let { assertTrue(it.message?.contains("onSubscribe") ?: false) } } - private suspend inline fun assertCallsExceptionHandlerWith( - crossinline operation: suspend () -> Unit): E - { - val caughtExceptions = mutableListOf() - val exceptionHandler = object: AbstractCoroutineContextElement(CoroutineExceptionHandler), - CoroutineExceptionHandler - { - override fun handleException(context: CoroutineContext, exception: Throwable) { - caughtExceptions += exception - } + @Test + fun testPublishWithTimeout() = runTest { + val publisher = publish { + expect(2) + withTimeout(1) { delay(100) } } - return withContext(exceptionHandler) { - operation() - caughtExceptions.single().let { - assertTrue(it is E) - it - } + try { + expect(1) + publisher.awaitFirstOrNull() + } catch (e: CancellationException) { + expect(3) } + finish(4) } - private suspend fun checkNumbers(n: Int, pub: Publisher) { - var last = 0 - pub.collect { - assertEquals(++last, it) +} + +@OptIn(ExperimentalContracts::class) +internal suspend inline fun assertCallsExceptionHandlerWith( + crossinline operation: suspend (CoroutineExceptionHandler) -> Unit): E { + contract { + callsInPlace(operation, InvocationKind.EXACTLY_ONCE) + } + val handler = CapturingHandler() + return withContext(handler) { + operation(handler) + handler.getException().let { + assertTrue(it is E, it.toString()) + it } - assertEquals(n, last) } - -} +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt index 9e3c07b6b7..095b724d40 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublishTest.kt @@ -5,6 +5,7 @@ package kotlinx.coroutines.reactive import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* import org.junit.Test import org.reactivestreams.* import kotlin.test.* @@ -121,44 +122,110 @@ class PublishTest : TestBase() { finish(7) } + /** Tests that, as soon as `ProducerScope.close` is called, `isClosedForSend` starts returning `true`. */ @Test - fun testOnNextError() = runTest { + fun testChannelClosing() = runTest { expect(1) - val publisher = publish(currentDispatcher()) { + val publisher = publish(Dispatchers.Unconfined) { + expect(3) + close() + assert(isClosedForSend) expect(4) - try { - send("OK") - } catch(e: Throwable) { - expect(6) - assert(e is TestException) - } } - expect(2) + try { + expect(2) + publisher.awaitFirstOrNull() + } catch (e: CancellationException) { + expect(5) + } + finish(6) + } + + @Test + fun testOnNextError() = runTest { val latch = CompletableDeferred() - publisher.subscribe(object : Subscriber { - override fun onComplete() { - expectUnreached() + expect(1) + assertCallsExceptionHandlerWith { exceptionHandler -> + val publisher = publish(currentDispatcher() + exceptionHandler) { + expect(4) + try { + send("OK") + } catch (e: Throwable) { + expect(6) + assert(e is TestException) + assert(isClosedForSend) + latch.complete(Unit) + } } + expect(2) + publisher.subscribe(object : Subscriber { + override fun onComplete() { + expectUnreached() + } - override fun onSubscribe(s: Subscription) { - expect(3) - s.request(1) - } + override fun onSubscribe(s: Subscription) { + expect(3) + s.request(1) + } - override fun onNext(t: String) { - expect(5) - assertEquals("OK", t) - throw TestException() - } + override fun onNext(t: String) { + expect(5) + assertEquals("OK", t) + throw TestException() + } - override fun onError(t: Throwable) { - expect(7) - assert(t is TestException) - latch.complete(Unit) + override fun onError(t: Throwable) { + expectUnreached() + } + }) + latch.await() + } + finish(7) + } + + /** Tests the behavior when a call to `onNext` fails after the channel is already closed. */ + @Test + fun testOnNextErrorAfterCancellation() = runTest { + assertCallsExceptionHandlerWith { handler -> + var producerScope: ProducerScope? = null + CompletableDeferred() + expect(1) + var job: Job? = null + val publisher = publish(handler + Dispatchers.Unconfined) { + producerScope = this + expect(4) + job = launch { + delay(Long.MAX_VALUE) + } } - }) - latch.await() - finish(8) + expect(2) + publisher.subscribe(object: Subscriber { + override fun onSubscribe(s: Subscription) { + expect(3) + s.request(Long.MAX_VALUE) + } + override fun onNext(t: Int) { + expect(6) + assertEquals(1, t) + job!!.cancel() + throw TestException() + } + override fun onError(t: Throwable?) { + /* Correct changes to the implementation could lead to us entering or not entering this method, but + it only matters that if we do, it is the "correct" exception that was validly used to cancel the + coroutine that gets passed here and not `TestException`. */ + assertTrue(t is CancellationException) + } + override fun onComplete() { expectUnreached() } + }) + expect(5) + val result: ChannelResult = producerScope!!.trySend(1) + val e = result.exceptionOrNull()!! + assertTrue(e is CancellationException, "The actual error: $e") + assertTrue(producerScope!!.isClosedForSend) + assertTrue(result.isFailure) + } + finish(7) } @Test @@ -182,4 +249,39 @@ class PublishTest : TestBase() { fun testIllegalArgumentException() { assertFailsWith { publish(Job()) { } } } + + /** Tests that `trySend` doesn't throw in `publish`. */ + @Test + fun testTrySendNotThrowing() = runTest { + var producerScope: ProducerScope? = null + expect(1) + val publisher = publish(Dispatchers.Unconfined) { + producerScope = this + expect(3) + delay(Long.MAX_VALUE) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + expect(2) + publisher.awaitFirstOrNull() + expectUnreached() + } + job.cancel() + expect(4) + val result = producerScope!!.trySend(1) + assertTrue(result.isFailure) + finish(5) + } + + /** Tests that all methods on `publish` fail without closing the channel when attempting to emit `null`. */ + @Test + fun testEmittingNull() = runTest { + val publisher = publish { + assertFailsWith { send(null) } + assertFailsWith { trySend(null) } + @Suppress("DEPRECATION") + assertFailsWith { offer(null) } + send("OK") + } + assertEquals("OK", publisher.awaitFirstOrNull()) + } } \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherRequestStressTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherRequestStressTest.kt index 736a66404f..9b069dcaec 100644 --- a/reactive/kotlinx-coroutines-reactive/test/PublisherRequestStressTest.kt +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherRequestStressTest.kt @@ -29,13 +29,14 @@ import kotlin.random.* */ @Suppress("ReactiveStreamsSubscriberImplementation") class PublisherRequestStressTest : TestBase() { + private val testDurationSec = 3 * stressTestMultiplier // Original code in Amazon SDK uses 4 and 16 as low/high watermarks. - // There constants were chosen so that problem reproduces asap with particular this code. + // These constants were chosen so that problem reproduces asap with particular this code. private val minDemand = 8L private val maxDemand = 16L - + private val nEmitThreads = 4 private val emitThreadNo = AtomicInteger() @@ -47,7 +48,7 @@ class PublisherRequestStressTest : TestBase() { private val reqPool = Executors.newSingleThreadExecutor { r -> Thread(r, "PublisherRequestStressTest-req") } - + private val nextValue = AtomicLong(0) @After @@ -64,7 +65,6 @@ class PublisherRequestStressTest : TestBase() { fun testRequestStress() { val expectedValue = AtomicLong(0) val requestedTill = AtomicLong(0) - val completionLatch = CountDownLatch(1) val callingOnNext = AtomicInteger() val publisher = mtFlow().asPublisher() @@ -74,7 +74,7 @@ class PublisherRequestStressTest : TestBase() { private var demand = 0L // only updated from reqPool override fun onComplete() { - completionLatch.countDown() + expectUnreached() } override fun onSubscribe(sub: Subscription) { @@ -123,7 +123,9 @@ class PublisherRequestStressTest : TestBase() { } if (!error) { subscription.cancel() - completionLatch.await() + runBlocking { + (subscription as AbstractCoroutine<*>).join() + } } } diff --git a/reactive/kotlinx-coroutines-reactor/src/Flux.kt b/reactive/kotlinx-coroutines-reactor/src/Flux.kt index b7143288ee..806f5bd5bc 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Flux.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Flux.kt @@ -55,12 +55,13 @@ private fun reactorPublish( coroutine.start(CoroutineStart.DEFAULT, coroutine, block) } -private val REACTOR_HANDLER: (Throwable, CoroutineContext) -> Unit = { e, ctx -> - if (e !is CancellationException) { +private val REACTOR_HANDLER: (Throwable, CoroutineContext) -> Unit = { cause, ctx -> + if (cause !is CancellationException) { try { - Operators.onOperatorError(e, ctx[ReactorContext]?.context ?: Context.empty()) + Operators.onOperatorError(cause, ctx[ReactorContext]?.context ?: Context.empty()) } catch (e: Throwable) { - handleCoroutineException(ctx, e) + cause.addSuppressed(e) + handleCoroutineException(ctx, cause) } } } diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index 307ec2278c..6e7b95ba6e 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -13,6 +13,7 @@ import reactor.core.* import reactor.core.publisher.* import kotlin.coroutines.* import kotlin.internal.* +import kotlinx.coroutines.internal.* /** * Creates a cold [mono][Mono] that runs a given [block] in a coroutine and emits its result. @@ -59,13 +60,15 @@ private class MonoCoroutine( override fun onCancelled(cause: Throwable, handled: Boolean) { /** Cancellation exceptions that were caused by [dispose], that is, came from downstream, are not errors. */ - if (getCancellationException() !== cause || !disposed) { + val unwrappedCause = unwrap(cause) + if (getCancellationException() !== unwrappedCause || !disposed) { try { /** If [sink] turns out to already be in a terminal state, this exception will be passed through the * [Hooks.onOperatorError] hook, which is the way to signal undeliverable exceptions in Reactor. */ sink.error(cause) } catch (e: Throwable) { // In case of improper error implementation or fatal exceptions + cause.addSuppressed(e) handleCoroutineException(context, cause) } } diff --git a/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt b/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt index 31f5f5d979..d059eb6622 100644 --- a/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/FluxTest.kt @@ -5,9 +5,9 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.* -import org.junit.* import org.junit.Test import kotlin.test.* @@ -141,4 +141,39 @@ class FluxTest : TestBase() { .collect { } } } + + /** Tests that `trySend` doesn't throw in `flux`. */ + @Test + fun testTrySendNotThrowing() = runTest { + var producerScope: ProducerScope? = null + expect(1) + val flux = flux(Dispatchers.Unconfined) { + producerScope = this + expect(3) + delay(Long.MAX_VALUE) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + expect(2) + flux.awaitFirstOrNull() + expectUnreached() + } + job.cancel() + expect(4) + val result = producerScope!!.trySend(1) + assertTrue(result.isFailure) + finish(5) + } + + /** Tests that all methods on `flux` fail without closing the channel when attempting to emit `null`. */ + @Test + fun testEmittingNull() = runTest { + val flux = flux { + assertFailsWith { send(null) } + assertFailsWith { trySend(null) } + @Suppress("DEPRECATION") + assertFailsWith { offer(null) } + send("OK") + } + assertEquals("OK", flux.awaitFirstOrNull()) + } } \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-rx2/README.md b/reactive/kotlinx-coroutines-rx2/README.md index 40fe122f89..1ae2c8a04c 100644 --- a/reactive/kotlinx-coroutines-rx2/README.md +++ b/reactive/kotlinx-coroutines-rx2/README.md @@ -35,7 +35,6 @@ Suspending extension functions and suspending iteration: | [ObservableSource.awaitFirstOrNull][io.reactivex.ObservableSource.awaitFirstOrNull] | Awaits for the first value from the given observable or null | [ObservableSource.awaitLast][io.reactivex.ObservableSource.awaitFirst] | Awaits for the last value from the given observable | [ObservableSource.awaitSingle][io.reactivex.ObservableSource.awaitSingle] | Awaits for the single value from the given observable -| [ObservableSource.openSubscription][io.reactivex.ObservableSource.openSubscription] | Subscribes to observable and returns [ReceiveChannel] Note that `Flowable` is a subclass of [Reactive Streams](https://www.reactive-streams.org) `Publisher` and extensions for it are covered by @@ -47,7 +46,6 @@ Conversion functions: | -------- | --------------- | [Job.asCompletable][kotlinx.coroutines.Job.asCompletable] | Converts job to hot completable | [Deferred.asSingle][kotlinx.coroutines.Deferred.asSingle] | Converts deferred value to hot single -| [ReceiveChannel.asObservable][kotlinx.coroutines.channels.ReceiveChannel.asObservable] | Converts streaming channel to hot observable | [Scheduler.asCoroutineDispatcher][io.reactivex.Scheduler.asCoroutineDispatcher] | Converts scheduler to [CoroutineDispatcher] @@ -86,10 +84,8 @@ Conversion functions: [io.reactivex.ObservableSource.awaitFirstOrElse]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-first-or-else.html [io.reactivex.ObservableSource.awaitFirstOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-first-or-null.html [io.reactivex.ObservableSource.awaitSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/await-single.html -[io.reactivex.ObservableSource.openSubscription]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-observable-source/open-subscription.html [kotlinx.coroutines.Job.asCompletable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/kotlinx.coroutines.-job/as-completable.html [kotlinx.coroutines.Deferred.asSingle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/kotlinx.coroutines.-deferred/as-single.html -[kotlinx.coroutines.channels.ReceiveChannel.asObservable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/kotlinx.coroutines.channels.-receive-channel/as-observable.html [io.reactivex.Scheduler.asCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-rx2/kotlinx.coroutines.rx2/io.reactivex.-scheduler/as-coroutine-dispatcher.html diff --git a/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt b/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt index 0fe43f1cee..3e39033ed7 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxCancellable.kt @@ -20,6 +20,7 @@ internal fun handleUndeliverableException(cause: Throwable, context: CoroutineCo try { RxJavaPlugins.onError(cause) } catch (e: Throwable) { + cause.addSuppressed(e) handleCoroutineException(context, cause) } } diff --git a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt index eee84b1bb6..3f6c27a693 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxCompletable.kt @@ -50,12 +50,13 @@ private class RxCompletableCoroutine( override fun onCancelled(cause: Throwable, handled: Boolean) { try { - if (!subscriber.tryOnError(cause)) { - handleUndeliverableException(cause, context) + if (subscriber.tryOnError(cause)) { + return } } catch (e: Throwable) { - handleUndeliverableException(e, context) + cause.addSuppressed(e) } + handleUndeliverableException(cause, context) } } diff --git a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt index 9682373a79..aa531c6ecf 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxMaybe.kt @@ -51,12 +51,13 @@ private class RxMaybeCoroutine( override fun onCancelled(cause: Throwable, handled: Boolean) { try { - if (!subscriber.tryOnError(cause)) { - handleUndeliverableException(cause, context) + if (subscriber.tryOnError(cause)) { + return } } catch (e: Throwable) { - handleUndeliverableException(e, context) + cause.addSuppressed(e) } + handleUndeliverableException(cause, context) } } diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index 09c5dc1d9f..c096c0d254 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -9,6 +9,7 @@ import io.reactivex.exceptions.* import kotlinx.atomicfu.* import kotlinx.coroutines.* import kotlinx.coroutines.channels.* +import kotlinx.coroutines.internal.* import kotlinx.coroutines.selects.* import kotlinx.coroutines.sync.* import kotlin.coroutines.* @@ -60,38 +61,29 @@ private class RxObservableCoroutine( ) : AbstractCoroutine(parentContext, false, true), ProducerScope, SelectClause2> { override val channel: SendChannel get() = this - // Mutex is locked when while subscriber.onXXX is being invoked + // Mutex is locked while subscriber.onXXX is being invoked private val mutex = Mutex() private val _signal = atomic(OPEN) - override val isClosedForSend: Boolean get() = isCompleted + override val isClosedForSend: Boolean get() = !isActive override fun close(cause: Throwable?): Boolean = cancelCoroutine(cause) override fun invokeOnClose(handler: (Throwable?) -> Unit) = throw UnsupportedOperationException("RxObservableCoroutine doesn't support invokeOnClose") - override fun offer(element: T): Boolean { - if (!mutex.tryLock()) return false - doLockedNext(element) - return true - } - - override fun trySend(element: T): ChannelResult { - if (!mutex.tryLock()) return ChannelResult.failure() - doLockedNext(element) - return ChannelResult.success(Unit) - } + override fun trySend(element: T): ChannelResult = + if (!mutex.tryLock()) { + ChannelResult.failure() + } else { + when (val throwable = doLockedNext(element)) { + null -> ChannelResult.success(Unit) + else -> ChannelResult.closed(throwable) + } + } public override suspend fun send(element: T) { - // fast-path -- try send without suspension - if (trySend(element).isSuccess) return - // slow-path does suspend - return sendSuspend(element) - } - - private suspend fun sendSuspend(element: T) { mutex.lock() - doLockedNext(element) + doLockedNext(element)?.let { throw it } } override val onSend: SelectClause2> @@ -99,30 +91,39 @@ private class RxObservableCoroutine( // registerSelectSend @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") - override fun registerSelectClause2(select: SelectInstance, element: T, block: suspend (SendChannel) -> R) { + override fun registerSelectClause2( + select: SelectInstance, + element: T, + block: suspend (SendChannel) -> R + ) { mutex.onLock.registerSelectClause2(select, null) { - doLockedNext(element) + doLockedNext(element)?.let { throw it } block(this) } } // assert: mutex.isLocked() - private fun doLockedNext(elem: T) { + private fun doLockedNext(elem: T): Throwable? { // check if already closed for send if (!isActive) { doLockedSignalCompleted(completionCause, completionCauseHandled) - throw getCancellationException() + return getCancellationException() } // notify subscriber try { subscriber.onNext(elem) } catch (e: Throwable) { - // If onNext fails with exception, then we cancel coroutine (with this exception) and then rethrow it - // to abort the corresponding send/offer invocation. From the standpoint of coroutines machinery, - // this failure is essentially equivalent to a failure of a child coroutine. - cancelCoroutine(e) - mutex.unlock() - throw e + val cause = UndeliverableException(e) + val causeDelivered = close(cause) + unlockAndCheckCompleted() + return if (causeDelivered) { + // `cause` is the reason this channel is closed + cause + } else { + // Someone else closed the channel during `onNext`. We report `cause` as an undeliverable exception. + handleUndeliverableException(cause, context) + getCancellationException() + } } /* * There is no sense to check for `isActive` before doing `unlock`, because cancellation/completion might @@ -131,6 +132,7 @@ private class RxObservableCoroutine( * We have to recheck `isCompleted` after `unlock` anyway. */ unlockAndCheckCompleted() + return null } private fun unlockAndCheckCompleted() { @@ -144,33 +146,31 @@ private class RxObservableCoroutine( private fun doLockedSignalCompleted(cause: Throwable?, handled: Boolean) { // cancellation failures try { - if (_signal.value >= CLOSED) { - _signal.value = SIGNALLED // we'll signal onError/onCompleted (that the final state -- no CAS needed) + if (_signal.value == SIGNALLED) + return + _signal.value = SIGNALLED // we'll signal onError/onCompleted (that the final state -- no CAS needed) + @Suppress("INVISIBLE_MEMBER") + val unwrappedCause = cause?.let { unwrap(it) } + if (unwrappedCause == null) { try { - if (cause != null && cause !is CancellationException) { - /* - * Reactive frameworks have two types of exceptions: regular and fatal. - * Regular are passed to onError. - * Fatal can be passed to onError, but even the standard implementations **can just swallow it** (e.g. see #1297). - * Such behaviour is inconsistent, leads to silent failures and we can't possibly know whether - * the cause will be handled by onError (and moreover, it depends on whether a fatal exception was - * thrown by subscriber or upstream). - * To make behaviour consistent and least surprising, we always handle fatal exceptions - * by coroutines machinery, anyway, they should not be present in regular program flow, - * thus our goal here is just to expose it as soon as possible. - */ - subscriber.tryOnError(cause) - if (!handled && cause.isFatal()) { - handleUndeliverableException(cause, context) - } - } - else { - subscriber.onComplete() - } - } catch (e: Throwable) { - // Unhandled exception (cannot handle in other way, since we are already complete) + subscriber.onComplete() + } catch (e: Exception) { handleUndeliverableException(e, context) } + } else if (unwrappedCause is UndeliverableException && !handled) { + /** Such exceptions are not reported to `onError`, as, according to the reactive specifications, + * exceptions thrown from the Subscriber methods must be treated as if the Subscriber was already + * cancelled. */ + handleUndeliverableException(cause, context) + } else if (unwrappedCause !== getCancellationException() || !subscriber.isDisposed) { + try { + /** If the subscriber is already in a terminal state, the error will be signalled to + * `RxJavaPlugins.onError`. */ + subscriber.onError(cause) + } catch (e: Exception) { + cause.addSuppressed(e) + handleUndeliverableException(cause, context) + } } } finally { mutex.unlock() @@ -192,13 +192,6 @@ private class RxObservableCoroutine( } } -internal fun Throwable.isFatal() = try { - Exceptions.throwIfFatal(this) // Rx-consistent behaviour without hardcode - false -} catch (e: Throwable) { - true -} - @Deprecated( message = "CoroutineScope.rxObservable is deprecated in favour of top-level rxObservable", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt index 49a9bb8dd6..c7ad606eb6 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxSingle.kt @@ -50,12 +50,13 @@ private class RxSingleCoroutine( override fun onCancelled(cause: Throwable, handled: Boolean) { try { - if (!subscriber.tryOnError(cause)) { - handleUndeliverableException(cause, context) + if (subscriber.tryOnError(cause)) { + return } } catch (e: Throwable) { - handleUndeliverableException(e, context) + cause.addSuppressed(e) } + handleUndeliverableException(cause, context) } } diff --git a/reactive/kotlinx-coroutines-rx2/test/FlowableExceptionHandlingTest.kt b/reactive/kotlinx-coroutines-rx2/test/FlowableExceptionHandlingTest.kt index 05b7ee92b6..316439293f 100644 --- a/reactive/kotlinx-coroutines-rx2/test/FlowableExceptionHandlingTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/FlowableExceptionHandlingTest.kt @@ -38,16 +38,16 @@ class FlowableExceptionHandlingTest : TestBase() { } @Test - fun testFatalException() = withExceptionHandler(handler(3)) { + fun testFatalException() = withExceptionHandler({ expectUnreached() }) { rxFlowable(Dispatchers.Unconfined) { expect(1) throw LinkageError() }.subscribe({ expectUnreached() }, { - expect(2) // Fatal exception is reported to both onError and CEH + expect(2) // Fatal exceptions are not treated as special }) - finish(4) + finish(3) } @Test @@ -66,7 +66,7 @@ class FlowableExceptionHandlingTest : TestBase() { } @Test - fun testFatalExceptionAsynchronous() = withExceptionHandler(handler(3)) { + fun testFatalExceptionAsynchronous() = withExceptionHandler({ expectUnreached() }) { rxFlowable(Dispatchers.Unconfined) { expect(1) throw LinkageError() @@ -77,19 +77,19 @@ class FlowableExceptionHandlingTest : TestBase() { }, { expect(2) }) - finish(4) + finish(3) } @Test - fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(4)) { + fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(3)) { rxFlowable(Dispatchers.Unconfined) { expect(1) send(Unit) }.subscribe({ expect(2) throw LinkageError() - }, { expect(3) }) // Fatal exception is reported to both onError and CEH - finish(5) + }, { expectUnreached() }) // Fatal exception is rethrown from `onNext` => the subscription is thought to be cancelled + finish(4) } @Test diff --git a/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt index 4bf3e4536e..8a6362ad99 100644 --- a/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/IntegrationTest.kt @@ -7,6 +7,7 @@ package kotlinx.coroutines.rx2 import io.reactivex.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* +import kotlinx.coroutines.reactive.* import org.junit.Test import org.junit.runner.* import org.junit.runners.* @@ -124,6 +125,21 @@ class IntegrationTest( finish(3) } + @Test + fun testObservableWithTimeout() = runTest { + val observable = rxObservable { + expect(2) + withTimeout(1) { delay(100) } + } + try { + expect(1) + observable.awaitFirstOrNull() + } catch (e: CancellationException) { + expect(3) + } + finish(4) + } + private suspend fun checkNumbers(n: Int, observable: Observable) { var last = 0 observable.collect { diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableExceptionHandlingTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableExceptionHandlingTest.kt index d6cdd3ca24..fb3d0f69fc 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableExceptionHandlingTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableExceptionHandlingTest.kt @@ -8,6 +8,7 @@ import io.reactivex.exceptions.* import kotlinx.coroutines.* import org.junit.* import org.junit.Test +import java.util.concurrent.* import kotlin.test.* class ObservableExceptionHandlingTest : TestBase() { @@ -18,7 +19,7 @@ class ObservableExceptionHandlingTest : TestBase() { } private inline fun handler(expect: Int) = { t: Throwable -> - assertTrue(t is UndeliverableException && t.cause is T) + assertTrue(t is UndeliverableException && t.cause is T, "$t") expect(expect) } @@ -38,8 +39,8 @@ class ObservableExceptionHandlingTest : TestBase() { } @Test - fun testFatalException() = withExceptionHandler(handler(3)) { - rxObservable(Dispatchers.Unconfined) { + fun testFatalException() = withExceptionHandler({ expectUnreached() }) { + rxObservable(Dispatchers.Unconfined + cehUnreached()) { expect(1) throw LinkageError() }.subscribe({ @@ -47,7 +48,7 @@ class ObservableExceptionHandlingTest : TestBase() { }, { expect(2) }) - finish(4) + finish(3) } @Test @@ -66,7 +67,7 @@ class ObservableExceptionHandlingTest : TestBase() { } @Test - fun testFatalExceptionAsynchronous() = withExceptionHandler(handler(3)) { + fun testFatalExceptionAsynchronous() = withExceptionHandler({ expectUnreached() }) { rxObservable(Dispatchers.Unconfined) { expect(1) throw LinkageError() @@ -75,20 +76,28 @@ class ObservableExceptionHandlingTest : TestBase() { .subscribe({ expectUnreached() }, { - expect(2) // Fatal exception is not reported in onError + expect(2) // Fatal exceptions are not treated in a special manner }) - finish(4) + finish(3) } @Test - fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(4)) { + fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(3)) { + val latch = CountDownLatch(1) rxObservable(Dispatchers.Unconfined) { expect(1) - send(Unit) + val result = trySend(Unit) + val exception = result.exceptionOrNull() + assertTrue(exception is UndeliverableException) + assertTrue(exception.cause is LinkageError) + assertTrue(isClosedForSend) + expect(4) + latch.countDown() }.subscribe({ expect(2) throw LinkageError() - }, { expect(3) }) // Unreached because fatal errors are rethrown + }, { expectUnreached() }) // Unreached because RxJava bubbles up fatal exceptions, causing `onNext` to throw. + latch.await() finish(5) } @@ -100,7 +109,7 @@ class ObservableExceptionHandlingTest : TestBase() { }.subscribe({ expect(2) throw TestException() - }, { expect(3) }) // not reported to onError because came from the subscribe itself + }, { expect(3) }) finish(4) } @@ -119,7 +128,7 @@ class ObservableExceptionHandlingTest : TestBase() { } @Test - fun testAsynchronousFatalExceptionFromSubscribe() = withExceptionHandler(handler(4)) { + fun testAsynchronousFatalExceptionFromSubscribe() = withExceptionHandler(handler(3)) { rxObservable(Dispatchers.Unconfined) { expect(1) send(Unit) @@ -128,7 +137,7 @@ class ObservableExceptionHandlingTest : TestBase() { .subscribe({ expect(2) throw LinkageError() - }, { expect(3) }) - finish(5) + }, { expectUnreached() }) // Unreached because RxJava bubbles up fatal exceptions, causing `onNext` to throw. + finish(4) } } diff --git a/reactive/kotlinx-coroutines-rx3/src/RxCancellable.kt b/reactive/kotlinx-coroutines-rx3/src/RxCancellable.kt index 2995159850..1017b112f1 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxCancellable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxCancellable.kt @@ -20,6 +20,7 @@ internal fun handleUndeliverableException(cause: Throwable, context: CoroutineCo try { RxJavaPlugins.onError(cause) } catch (e: Throwable) { + cause.addSuppressed(e) handleCoroutineException(context, cause) } } diff --git a/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt b/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt index 88137675d8..47cc6ad3ab 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxCompletable.kt @@ -50,11 +50,12 @@ private class RxCompletableCoroutine( override fun onCancelled(cause: Throwable, handled: Boolean) { try { - if (!subscriber.tryOnError(cause)) { - handleUndeliverableException(cause, context) + if (subscriber.tryOnError(cause)) { + return } } catch (e: Throwable) { - handleUndeliverableException(e, context) + cause.addSuppressed(e) } + handleUndeliverableException(cause, context) } } diff --git a/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt index 1c10266470..12d0197bf2 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt @@ -51,11 +51,12 @@ private class RxMaybeCoroutine( override fun onCancelled(cause: Throwable, handled: Boolean) { try { - if (!subscriber.tryOnError(cause)) { - handleUndeliverableException(cause, context) + if (subscriber.tryOnError(cause)) { + return } } catch (e: Throwable) { - handleUndeliverableException(e, context) + cause.addSuppressed(e) } + handleUndeliverableException(cause, context) } } diff --git a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt index 55794f9adf..5c810c498d 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.channels.* import kotlinx.coroutines.selects.* import kotlinx.coroutines.sync.* import kotlin.coroutines.* +import kotlinx.coroutines.internal.* /** * Creates cold [observable][Observable] that will run a given [block] in a coroutine. @@ -54,38 +55,35 @@ private const val OPEN = 0 // open channel, still working private const val CLOSED = -1 // closed, but have not signalled onCompleted/onError yet private const val SIGNALLED = -2 // already signalled subscriber onCompleted/onError -private class RxObservableCoroutine( +private class RxObservableCoroutine( parentContext: CoroutineContext, private val subscriber: ObservableEmitter ) : AbstractCoroutine(parentContext, false, true), ProducerScope, SelectClause2> { override val channel: SendChannel get() = this - // Mutex is locked when while subscriber.onXXX is being invoked + // Mutex is locked while subscriber.onXXX is being invoked private val mutex = Mutex() private val _signal = atomic(OPEN) - override val isClosedForSend: Boolean get() = isCompleted + override val isClosedForSend: Boolean get() = !isActive override fun close(cause: Throwable?): Boolean = cancelCoroutine(cause) override fun invokeOnClose(handler: (Throwable?) -> Unit) = throw UnsupportedOperationException("RxObservableCoroutine doesn't support invokeOnClose") - override fun trySend(element: T): ChannelResult { - if (!mutex.tryLock()) return ChannelResult.failure() - doLockedNext(element) - return ChannelResult.success(Unit) - } + override fun trySend(element: T): ChannelResult = + if (!mutex.tryLock()) { + ChannelResult.failure() + } else { + when (val throwable = doLockedNext(element)) { + null -> ChannelResult.success(Unit) + else -> ChannelResult.closed(throwable) + } + } public override suspend fun send(element: T) { - // fast-path -- try send without suspension - if (trySend(element).isSuccess) return - // slow-path does suspend - return sendSuspend(element) - } - - private suspend fun sendSuspend(element: T) { mutex.lock() - doLockedNext(element) + doLockedNext(element)?.let { throw it } } override val onSend: SelectClause2> @@ -93,30 +91,39 @@ private class RxObservableCoroutine( // registerSelectSend @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") - override fun registerSelectClause2(select: SelectInstance, element: T, block: suspend (SendChannel) -> R) { + override fun registerSelectClause2( + select: SelectInstance, + element: T, + block: suspend (SendChannel) -> R + ) { mutex.onLock.registerSelectClause2(select, null) { - doLockedNext(element) + doLockedNext(element)?.let { throw it } block(this) } } // assert: mutex.isLocked() - private fun doLockedNext(elem: T) { + private fun doLockedNext(elem: T): Throwable? { // check if already closed for send if (!isActive) { doLockedSignalCompleted(completionCause, completionCauseHandled) - throw getCancellationException() + return getCancellationException() } // notify subscriber try { subscriber.onNext(elem) } catch (e: Throwable) { - // If onNext fails with exception, then we cancel coroutine (with this exception) and then rethrow it - // to abort the corresponding send/offer invocation. From the standpoint of coroutines machinery, - // this failure is essentially equivalent to a failure of a child coroutine. - cancelCoroutine(e) - mutex.unlock() - throw e + val cause = UndeliverableException(e) + val causeDelivered = close(cause) + unlockAndCheckCompleted() + return if (causeDelivered) { + // `cause` is the reason this channel is closed + cause + } else { + // Someone else closed the channel during `onNext`. We report `cause` as an undeliverable exception. + handleUndeliverableException(cause, context) + getCancellationException() + } } /* * There is no sense to check for `isActive` before doing `unlock`, because cancellation/completion might @@ -125,6 +132,7 @@ private class RxObservableCoroutine( * We have to recheck `isCompleted` after `unlock` anyway. */ unlockAndCheckCompleted() + return null } private fun unlockAndCheckCompleted() { @@ -138,33 +146,31 @@ private class RxObservableCoroutine( private fun doLockedSignalCompleted(cause: Throwable?, handled: Boolean) { // cancellation failures try { - if (_signal.value >= CLOSED) { - _signal.value = SIGNALLED // we'll signal onError/onCompleted (that the final state -- no CAS needed) + if (_signal.value == SIGNALLED) + return + _signal.value = SIGNALLED // we'll signal onError/onCompleted (that the final state -- no CAS needed) + @Suppress("INVISIBLE_MEMBER") + val unwrappedCause = cause?.let { unwrap(it) } + if (unwrappedCause == null) { try { - if (cause != null && cause !is CancellationException) { - /* - * Reactive frameworks have two types of exceptions: regular and fatal. - * Regular are passed to onError. - * Fatal can be passed to onError, but even the standard implementations **can just swallow it** (e.g. see #1297). - * Such behaviour is inconsistent, leads to silent failures and we can't possibly know whether - * the cause will be handled by onError (and moreover, it depends on whether a fatal exception was - * thrown by subscriber or upstream). - * To make behaviour consistent and least surprising, we always handle fatal exceptions - * by coroutines machinery, anyway, they should not be present in regular program flow, - * thus our goal here is just to expose it as soon as possible. - */ - subscriber.tryOnError(cause) - if (!handled && cause.isFatal()) { - handleUndeliverableException(cause, context) - } - } - else { - subscriber.onComplete() - } - } catch (e: Throwable) { - // Unhandled exception (cannot handle in other way, since we are already complete) + subscriber.onComplete() + } catch (e: Exception) { handleUndeliverableException(e, context) } + } else if (unwrappedCause is UndeliverableException && !handled) { + /** Such exceptions are not reported to `onError`, as, according to the reactive specifications, + * exceptions thrown from the Subscriber methods must be treated as if the Subscriber was already + * cancelled. */ + handleUndeliverableException(cause, context) + } else if (unwrappedCause !== getCancellationException() || !subscriber.isDisposed) { + try { + /** If the subscriber is already in a terminal state, the error will be signalled to + * `RxJavaPlugins.onError`. */ + subscriber.onError(cause) + } catch (e: Exception) { + cause.addSuppressed(e) + handleUndeliverableException(cause, context) + } } } finally { mutex.unlock() @@ -186,9 +192,3 @@ private class RxObservableCoroutine( } } -internal fun Throwable.isFatal() = try { - Exceptions.throwIfFatal(this) // Rx-consistent behaviour without hardcode - false -} catch (e: Throwable) { - true -} diff --git a/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt b/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt index fb6020eab3..e7678f0d10 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxSingle.kt @@ -50,11 +50,12 @@ private class RxSingleCoroutine( override fun onCancelled(cause: Throwable, handled: Boolean) { try { - if (!subscriber.tryOnError(cause)) { - handleUndeliverableException(cause, context) + if (subscriber.tryOnError(cause)) { + return } } catch (e: Throwable) { - handleUndeliverableException(e, context) + cause.addSuppressed(e) } + handleUndeliverableException(cause, context) } } diff --git a/reactive/kotlinx-coroutines-rx3/test/FlowableExceptionHandlingTest.kt b/reactive/kotlinx-coroutines-rx3/test/FlowableExceptionHandlingTest.kt index 8cbd7ee89f..126cb81826 100644 --- a/reactive/kotlinx-coroutines-rx3/test/FlowableExceptionHandlingTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/FlowableExceptionHandlingTest.kt @@ -38,16 +38,16 @@ class FlowableExceptionHandlingTest : TestBase() { } @Test - fun testFatalException() = withExceptionHandler(handler(3)) { + fun testFatalException() = withExceptionHandler({ expectUnreached() }) { rxFlowable(Dispatchers.Unconfined) { expect(1) throw LinkageError() }.subscribe({ expectUnreached() }, { - expect(2) // Fatal exception is reported to both onError and CEH + expect(2) // Fatal exceptions are not treated as special }) - finish(4) + finish(3) } @Test @@ -66,7 +66,7 @@ class FlowableExceptionHandlingTest : TestBase() { } @Test - fun testFatalExceptionAsynchronous() = withExceptionHandler(handler(3)) { + fun testFatalExceptionAsynchronous() = withExceptionHandler({ expectUnreached() }) { rxFlowable(Dispatchers.Unconfined) { expect(1) throw LinkageError() @@ -77,19 +77,19 @@ class FlowableExceptionHandlingTest : TestBase() { }, { expect(2) }) - finish(4) + finish(3) } @Test - fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(4)) { + fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(3)) { rxFlowable(Dispatchers.Unconfined) { expect(1) send(Unit) }.subscribe({ expect(2) throw LinkageError() - }, { expect(3) }) // Fatal exception is reported to both onError and CEH - finish(5) + }, { expectUnreached() }) // Fatal exception is rethrown from `onNext` => the subscription is thought to be cancelled + finish(4) } @Test diff --git a/reactive/kotlinx-coroutines-rx3/test/IntegrationTest.kt b/reactive/kotlinx-coroutines-rx3/test/IntegrationTest.kt index 395672cee9..1302124f50 100644 --- a/reactive/kotlinx-coroutines-rx3/test/IntegrationTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/IntegrationTest.kt @@ -125,6 +125,21 @@ class IntegrationTest( finish(3) } + @Test + fun testObservableWithTimeout() = runTest { + val observable = rxObservable { + expect(2) + withTimeout(1) { delay(100) } + } + try { + expect(1) + observable.awaitFirstOrNull() + } catch (e: CancellationException) { + expect(3) + } + finish(4) + } + private suspend fun checkNumbers(n: Int, observable: Observable) { var last = 0 observable.collect { diff --git a/reactive/kotlinx-coroutines-rx3/test/ObservableExceptionHandlingTest.kt b/reactive/kotlinx-coroutines-rx3/test/ObservableExceptionHandlingTest.kt index 1183b2ae21..5ddb36ed07 100644 --- a/reactive/kotlinx-coroutines-rx3/test/ObservableExceptionHandlingTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/ObservableExceptionHandlingTest.kt @@ -8,6 +8,7 @@ import io.reactivex.rxjava3.exceptions.* import kotlinx.coroutines.* import org.junit.* import org.junit.Test +import java.util.concurrent.* import kotlin.test.* class ObservableExceptionHandlingTest : TestBase() { @@ -18,7 +19,7 @@ class ObservableExceptionHandlingTest : TestBase() { } private inline fun handler(expect: Int) = { t: Throwable -> - assertTrue(t is UndeliverableException && t.cause is T) + assertTrue(t is UndeliverableException && t.cause is T, "$t") expect(expect) } @@ -38,8 +39,8 @@ class ObservableExceptionHandlingTest : TestBase() { } @Test - fun testFatalException() = withExceptionHandler(handler(3)) { - rxObservable(Dispatchers.Unconfined) { + fun testFatalException() = withExceptionHandler({ expectUnreached() }) { + rxObservable(Dispatchers.Unconfined + cehUnreached()) { expect(1) throw LinkageError() }.subscribe({ @@ -47,7 +48,7 @@ class ObservableExceptionHandlingTest : TestBase() { }, { expect(2) }) - finish(4) + finish(3) } @Test @@ -66,7 +67,7 @@ class ObservableExceptionHandlingTest : TestBase() { } @Test - fun testFatalExceptionAsynchronous() = withExceptionHandler(handler(3)) { + fun testFatalExceptionAsynchronous() = withExceptionHandler({ expectUnreached() }) { rxObservable(Dispatchers.Unconfined) { expect(1) throw LinkageError() @@ -75,20 +76,28 @@ class ObservableExceptionHandlingTest : TestBase() { .subscribe({ expectUnreached() }, { - expect(2) // Fatal exception is not reported in onError + expect(2) // Fatal exceptions are not treated in a special manner }) - finish(4) + finish(3) } @Test - fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(4)) { + fun testFatalExceptionFromSubscribe() = withExceptionHandler(handler(3)) { + val latch = CountDownLatch(1) rxObservable(Dispatchers.Unconfined) { expect(1) - send(Unit) + val result = trySend(Unit) + val exception = result.exceptionOrNull() + assertTrue(exception is UndeliverableException) + assertTrue(exception.cause is LinkageError) + assertTrue(isClosedForSend) + expect(4) + latch.countDown() }.subscribe({ expect(2) throw LinkageError() - }, { expect(3) }) // Unreached because fatal errors are rethrown + }, { expectUnreached() }) // Unreached because RxJava bubbles up fatal exceptions, causing `onNext` to throw. + latch.await() finish(5) } @@ -100,7 +109,7 @@ class ObservableExceptionHandlingTest : TestBase() { }.subscribe({ expect(2) throw TestException() - }, { expect(3) }) // not reported to onError because came from the subscribe itself + }, { expect(3) }) finish(4) } @@ -119,7 +128,7 @@ class ObservableExceptionHandlingTest : TestBase() { } @Test - fun testAsynchronousFatalExceptionFromSubscribe() = withExceptionHandler(handler(4)) { + fun testAsynchronousFatalExceptionFromSubscribe() = withExceptionHandler(handler(3)) { rxObservable(Dispatchers.Unconfined) { expect(1) send(Unit) @@ -128,7 +137,7 @@ class ObservableExceptionHandlingTest : TestBase() { .subscribe({ expect(2) throw LinkageError() - }, { expect(3) }) - finish(5) + }, { expectUnreached() }) // Unreached because RxJava bubbles up fatal exceptions, causing `onNext` to throw. + finish(4) } } From 6a42a77090c75545ef8e47e717283d447624b5c0 Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Tue, 20 Apr 2021 13:52:02 +0300 Subject: [PATCH 38/55] Mark GlobalScope as delicate API and rewrite coroutine basics doc without it (#2637) Also, remove the tutorial variant of the basic coroutine introduction as it essentially duplicates the content of the basic coroutine introduction, albeit with "how to create a project" additional information, which does not seem to be appropriate in this section at all. Fixes #2122 Co-authored-by: Vsevolod Tolstopyatov Co-authored-by: Andrey Polyakov --- docs/images/new-mvn-project-jvm.png | Bin 378558 -> 0 bytes docs/topics/composing-suspending-functions.md | 22 +- .../coroutine-context-and-dispatchers.md | 26 +- docs/topics/coroutines-basic-jvm.md | 262 ------------- docs/topics/coroutines-basics.md | 369 +++++++----------- docs/topics/coroutines-guide.md | 1 - docs/topics/exception-handling.md | 13 +- .../api/kotlinx-coroutines-core.api | 3 + .../common/src/Annotations.kt | 16 + .../common/src/CoroutineScope.kt | 96 ++++- .../jvm/test/guide/example-basic-01.kt | 7 +- .../jvm/test/guide/example-basic-02.kt | 18 +- .../jvm/test/guide/example-basic-03.kt | 11 +- .../jvm/test/guide/example-basic-04.kt | 18 +- .../jvm/test/guide/example-basic-05.kt | 8 +- .../jvm/test/guide/example-basic-06.kt | 18 +- .../jvm/test/guide/example-basic-07.kt | 19 - .../jvm/test/guide/example-basic-08.kt | 17 - .../jvm/test/guide/example-basic-09.kt | 18 - .../jvm/test/guide/example-compose-04.kt | 2 + .../jvm/test/guide/example-context-06.kt | 6 +- .../jvm/test/guide/example-exceptions-01.kt | 1 + .../jvm/test/guide/example-exceptions-02.kt | 1 + .../jvm/test/guide/example-exceptions-04.kt | 1 + .../jvm/test/guide/example-exceptions-05.kt | 1 + .../jvm/test/guide/example-exceptions-06.kt | 1 + .../jvm/test/guide/test/BasicsGuideTest.kt | 46 +-- .../test/guide/test/DispatcherGuideTest.kt | 2 +- 28 files changed, 347 insertions(+), 656 deletions(-) delete mode 100644 docs/images/new-mvn-project-jvm.png delete mode 100644 docs/topics/coroutines-basic-jvm.md delete mode 100644 kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt delete mode 100644 kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt delete mode 100644 kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt diff --git a/docs/images/new-mvn-project-jvm.png b/docs/images/new-mvn-project-jvm.png deleted file mode 100644 index 2154a3df3d6d8271c1bfddb4b2195b3f79b39c06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 378558 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 YfQXVl;} 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

{s_iKxJ4RX9_#-ujd;4N); z#q0Q6R~2g3`-OdNQu0_kE$y}!Xw+J9*^~O4sNIkM>2>_;%H2Wqg*iuzhN<@NC!6M9 zVf2)qW4JfjM#{oN71{g+Ze^I^6-C9Q5?{Jn3U+1o_s5L^n{D{

=o2p@Lqf2%63^ ztbbvhDfY>*wra$^$j}b<+L7vt-E39Bp>Oak{@VSEP;9&7)-(&S?Vi&Z|Td%yV!{y@ms0U&|{1HZkVv{ zB?qW+6=SKka{CoYMY4&ab&T1JR=)}yb&HazkCuK4YI$L7Pb875tE|Z+EktnB9}A*v zxL{3tl1QTbe7$TYi`e^#z#ClJ__&viukvX4TP(a-sjb%FvO^3ripdv7yhl4&#?AK2 zgY$t_Pyua2{Bx^c)|VEHs{dal=U*THhhf1lM4+>Y?SjpMlCgcchaHXurCyBBo<_|% zY@IJgs_6dmmr8818T@Hp{1mi-Ev}JrA1&GxmT!20P*d_O9Kezehd2*gJ^MIICxK(~ z%edu)+vL{~oocxAurnhu$RdyKbLY%-fBl7nOJ7cTcC~DmR<-E!{y4fV9mmz8fQTKf z)$@C{NvTtP1T#&Qp+UTrc>`U;GRoW&D%|`U3r zaef#8fVr-&guVm)2pAiZOo~w3LPFd?-0lgO|ItCO;K}Hli)Q{IPCi_w;i$dvGl2YH z8Jb^`!tDKA2^Yk-cCT@!`x5$4moFFi`00YY-Fe5{)~=eB%IPweu2Op<)Ldxjlq?bIk!=T+Mpx8St744 zL4R^xBXD0=)T9&R#(Q!-OGgU5Xy4J*`0#d>f`W2SlbIM}q|q1q`wr^+;hzSaIlDjn zgWW=g5ut|RI=a0|ZH>W^e|NQ@e*dc0BUVDj$G@IC%lP27D`nKc`2S^zFINV?>kHUK zZ5j<1Yq#fH_4?p&sCr?xgk*4y+{COw&}-CF|^Xj<|};O71Iayot*xS~wSkh(Sh{7y3?)Z^I@Gc3CQ; zFIUq005)lr49o0}SM*s-S}0BP8Bj_1VYroitZ~Nw6$bg=?%lrvBwymi4vM-(NQ-EW zxr|77@QgNvgg%8LJvow~9Jwy$@Rx5`sbpP^cl_%vm0^Hhu)qm_Gbw*aEL@Z^zFSYB zzX8Q-t%lSi>fcGq_AQv?+baw#H#mgcUPhBVq>B1?qyPW79t9)N#6CjWZ&l({eQ!@t z1FaJl8Kl83PE&_t6Pb?2pb|SuKcUV7OFvV?66evwShBR

trrX$qd)u|6}lq%YBm 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%0DHv? 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 J74J;G{2#V#Td4p5 diff --git a/docs/topics/composing-suspending-functions.md b/docs/topics/composing-suspending-functions.md index 0af60d00a9..9c1a26a910 100644 --- a/docs/topics/composing-suspending-functions.md +++ b/docs/topics/composing-suspending-functions.md @@ -24,7 +24,7 @@ suspend fun doSomethingUsefulTwo(): Int { What do we do if we need them to be invoked _sequentially_ — first `doSomethingUsefulOne` _and then_ `doSomethingUsefulTwo`, and compute the sum of their results? -In practice we do this if we use the result of the first function to make a decision on whether we need +In practice, we do this if we use the result of the first function to make a decision on whether we need to invoke the second one or to decide on how to invoke it. We use a normal sequential invocation, because the code in the coroutine, just like in the regular @@ -190,18 +190,26 @@ standard `lazy` function in cases when computation of the value involves suspend ## Async-style functions We can define async-style functions that invoke `doSomethingUsefulOne` and `doSomethingUsefulTwo` -_asynchronously_ using the [async] coroutine builder with an explicit [GlobalScope] reference. +_asynchronously_ using the [async] coroutine builder using a [GlobalScope] reference to +opt-out of the structured concurrency. We name such functions with the "...Async" suffix to highlight the fact that they only start asynchronous computation and one needs to use the resulting deferred value to get the result. +> [GlobalScope] is a delicate API that can backfire in non-trivial ways, one of which will be explained +> below, so you must explicitly opt-in into using `GlobalScope` with `@OptIn(DelicateCoroutinesApi::class)`. +> +{type="note"} + ```kotlin // The result type of somethingUsefulOneAsync is Deferred +@OptIn(DelicateCoroutinesApi::class) fun somethingUsefulOneAsync() = GlobalScope.async { doSomethingUsefulOne() } // The result type of somethingUsefulTwoAsync is Deferred +@OptIn(DelicateCoroutinesApi::class) fun somethingUsefulTwoAsync() = GlobalScope.async { doSomethingUsefulTwo() } @@ -236,10 +244,12 @@ fun main() { } //sampleEnd +@OptIn(DelicateCoroutinesApi::class) fun somethingUsefulOneAsync() = GlobalScope.async { doSomethingUsefulOne() } +@OptIn(DelicateCoroutinesApi::class) fun somethingUsefulTwoAsync() = GlobalScope.async { doSomethingUsefulTwo() } @@ -272,9 +282,9 @@ Completed in 1085 ms {type="note"} Consider what happens if between the `val one = somethingUsefulOneAsync()` line and `one.await()` expression there is some logic -error in the code and the program throws an exception and the operation that was being performed by the program aborts. +error in the code, and the program throws an exception, and the operation that was being performed by the program aborts. Normally, a global error-handler could catch this exception, log and report the error for developers, but the program -could otherwise continue doing other operations. But here we have `somethingUsefulOneAsync` still running in the background, +could otherwise continue doing other operations. However, here we have `somethingUsefulOneAsync` still running in the background, even though the operation that initiated it was aborted. This problem does not happen with structured concurrency, as shown in the section below. @@ -293,7 +303,7 @@ suspend fun concurrentSum(): Int = coroutineScope { } ``` -This way, if something goes wrong inside the code of the `concurrentSum` function and it throws an exception, +This way, if something goes wrong inside the code of the `concurrentSum` function, and it throws an exception, all the coroutines that were launched in its scope will be cancelled. @@ -403,4 +413,4 @@ Computation failed with ArithmeticException [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html - \ No newline at end of file + diff --git a/docs/topics/coroutine-context-and-dispatchers.md b/docs/topics/coroutine-context-and-dispatchers.md index 9aae1a7a4b..402db103b4 100644 --- a/docs/topics/coroutine-context-and-dispatchers.md +++ b/docs/topics/coroutine-context-and-dispatchers.md @@ -65,9 +65,8 @@ context of the main `runBlocking` coroutine which runs in the `main` thread. [Dispatchers.Unconfined] is a special dispatcher that also appears to run in the `main` thread, but it is, in fact, a different mechanism that is explained later. -The default dispatcher that is used when coroutines are launched in [GlobalScope] -is represented by [Dispatchers.Default] and uses a shared background pool of threads, -so `launch(Dispatchers.Default) { ... }` uses the same dispatcher as `GlobalScope.launch { ... }`. +The default dispatcher that is used when no other dispatcher is explicitly specified in the scope. +It is represented by [Dispatchers.Default] and uses a shared background pool of threads. [newSingleThreadContext] creates a thread for the coroutine to run. A dedicated thread is a very expensive resource. @@ -303,8 +302,14 @@ the [Job] of the new coroutine becomes a _child_ of the parent coroutine's job. When the parent coroutine is cancelled, all its children are recursively cancelled, too. -However, when [GlobalScope] is used to launch a coroutine, there is no parent for the job of the new coroutine. -It is therefore not tied to the scope it was launched from and operates independently. +However, this parent-child relation can be explicitly overriden in one of two ways: + +1. When a different scope is explicitly specified when launching a coroutine (for example, `GlobalScope.launch`), + then it does not inherit a `Job` from the parent scope. +2. When a different `Job` object is passed as the context for the new coroutine (as show in the example below), + then it overrides the `Job` of the parent scope. + +In both cases, the launched coroutine is not tied to the scope it was launched from and operates independently. ```kotlin import kotlinx.coroutines.* @@ -313,9 +318,9 @@ fun main() = runBlocking { //sampleStart // launch a coroutine to process some kind of incoming request val request = launch { - // it spawns two other jobs, one with GlobalScope - GlobalScope.launch { - println("job1: I run in GlobalScope and execute independently!") + // it spawns two other jobs + launch(Job()) { + println("job1: I run in my own Job and execute independently!") delay(1000) println("job1: I am not affected by cancellation of the request") } @@ -343,7 +348,7 @@ fun main() = runBlocking { The output of this code is: ```text -job1: I run in GlobalScope and execute independently! +job1: I run in my own Job and execute independently! job2: I am a child of the request coroutine job1: I am not affected by cancellation of the request main: Who has survived request cancellation? @@ -659,7 +664,6 @@ that should be implemented. [async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html -[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html [Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html [newSingleThreadContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/new-single-thread-context.html [ExecutorCoroutineDispatcher.close]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-executor-coroutine-dispatcher/close.html @@ -678,4 +682,4 @@ that should be implemented. [ensurePresent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/java.lang.-thread-local/ensure-present.html [ThreadContextElement]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/index.html - \ No newline at end of file + diff --git a/docs/topics/coroutines-basic-jvm.md b/docs/topics/coroutines-basic-jvm.md deleted file mode 100644 index a6ca3ba5f3..0000000000 --- a/docs/topics/coroutines-basic-jvm.md +++ /dev/null @@ -1,262 +0,0 @@ -[//]: # (title: Create a basic coroutine – tutorial) - -Kotlin 1.1 introduced coroutines, a new way of writing asynchronous, non-blocking code (and much more). In this tutorial you will go through some basics of using Kotlin coroutines with the help of the `kotlinx.coroutines` library, which is a collection of helpers and wrappers for existing Java libraries. - -## Set up a project - -### Gradle - -In IntelliJ IDEA go to **File** \| **New** \| **Project**.: - -![Create a new project](new-gradle-project-jvm.png) - -Then follow the wizard steps. You'll have a `build.gradle` file created with Kotlin configured according to [this document](gradle.md). -Make sure it's configured for Kotlin 1.3 or higher. - -Since we'll be using the [`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines), let's add its recent version to our dependencies: - - - -```groovy -dependencies { - ... - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%' -} -``` - -```kotlin -dependencies { - ... - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%") -} -``` - - -This library is published to the [Maven Central repository](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core), so add the following: - -```groovy -repositories { - mavenCentral() -} -``` - -That's it, we are good to go and write code under `src/main/kotlin`. - -### Maven - -In IntelliJ IDEA go to **File** \| **New** \| **Project** and check the **Create from archetype** box: - -![Create a new project](new-mvn-project-jvm.png) - -Then follow the wizard steps. You'll have a `pom.xml` file created with Kotlin configured according to [this document](maven.md). -Make sure it's configured for Kotlin 1.3 or higher. - -```xml - - org.jetbrains.kotlin - kotlin-maven-plugin - ... - -``` - -Since we'll be using the [`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines), let's add its recent version to our dependencies: - -```xml - - ... - - org.jetbrains.kotlinx - kotlinx-coroutines-core - %coroutinesVersion% - - -``` - -This library is published to the [Maven Central repository](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core), which Maven will download from by default. - -That's it, we are good to go and write code under `src/main/kotlin`. - -## My first coroutine - -One can think of a coroutine as a light-weight thread. Like threads, coroutines can run in parallel, wait for each other and communicate. -The biggest difference is that coroutines are very cheap, almost free: we can create thousands of them, and pay very little in terms of performance. -True threads, on the other hand, are expensive to start and keep around. A thousand threads can be a serious challenge for a modern machine. - -So, how do we start a coroutine? Let's use the `launch {}` function: - -```kotlin -launch { - ... -} -``` - -This starts a new coroutine. By default, coroutines are run on a shared pool of threads. -Threads still exist in a program based on coroutines, but one thread can run many coroutines, so there's no need for -too many threads. - -Let's look at a full program that uses `launch`: - -```kotlin -import kotlinx.coroutines.* - -fun main(args: Array) { -//sampleStart - println("Start") - - // Start a coroutine - GlobalScope.launch { - delay(1000) - println("Hello") - } - - Thread.sleep(2000) // wait for 2 seconds - println("Stop") -//sampleEnd -} -``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -Here we start a coroutine that waits for 1 second and prints `Hello`. - -We are using the `delay()` function that's like `Thread.sleep()`, but better: it _doesn't block a thread_, but only suspends the coroutine itself. -The thread is returned to the pool while the coroutine is waiting, and when the waiting is done, the coroutine resumes on a free thread in the pool. - -The main thread (that runs the `main()` function) must wait until our coroutine completes, otherwise the program ends before `Hello` is printed. - -_Exercise: try removing the `sleep()` from the program above and see the result._ - - If we try to use the same non-blocking `delay()` function directly inside `main()`, we'll get a compiler error: - -> Suspend functions are only allowed to be called from a coroutine or another suspend function. -> -{type="note"} - -This is because we are not inside any coroutine. We can use delay if we wrap it into `runBlocking {}` that starts a coroutine and waits until it's done: - -```kotlin -runBlocking { - delay(2000) -} -``` - -So, first the resulting program prints `Start`, then it runs a coroutine through `launch {}`, then it runs another one through `runBlocking {}` and blocks until it's done, then prints `Stop`. Meanwhile the first coroutine completes and prints `Hello`. Just like threads, we told you :) - -## Let's run a lot of them - -Now, let's make sure that coroutines are really cheaper than threads. How about starting a million of them? Let's try starting a million threads first: - -```kotlin -val c = AtomicLong() - -for (i in 1..1_000_000L) - thread(start = true) { - c.addAndGet(i) - } - -println(c.get()) -``` - -This runs a 1'000'000 threads each of which adds to a common counter. My patience runs out before this program completes on my machine (definitely over a minute). - -Let's try the same with coroutines: - -```kotlin -val c = AtomicLong() - -for (i in 1..1_000_000L) - GlobalScope.launch { - c.addAndGet(i) - } - -println(c.get()) -``` - -This example completes in less than a second for me, but it prints some arbitrary number, because some coroutines don't finish before `main()` prints the result. Let's fix that. - -We could use the same means of synchronization that are applicable to threads (a `CountDownLatch` is what crosses my mind in this case), but let's take a safer and cleaner path. - -## Async: returning a value from a coroutine - -Another way of starting a coroutine is `async {}`. It is like `launch {}`, but returns an instance of `Deferred`, which has an `await()` function that returns the result of the coroutine. `Deferred` is a very basic [future](https://en.wikipedia.org/wiki/Futures_and_promises) (fully-fledged JDK futures are also supported, but here we'll confine ourselves to `Deferred` for now). - -Let's create a million coroutines again, keeping their `Deferred` objects. Now there's no need in the atomic counter, as we can just return the numbers to be added from our coroutines: - -```kotlin -val deferred = (1..1_000_000).map { n -> - GlobalScope.async { - n - } -} -``` - -All these have already started, all we need is collect the results: - -```kotlin -val sum = deferred.sumOf { it.await().toLong() } -``` - -We simply take every coroutine and await its result here, then all results are added together by the standard library function `sumOf()`. But the compiler rightfully complains: - -> Suspend functions are only allowed to be called from a coroutine or another suspend function. -> -{type="note"} - -`await()` can not be called outside a coroutine, because it needs to suspend until the computation finishes, and only coroutines can suspend in a non-blocking way. So, let's put this inside a coroutine: - -```kotlin -runBlocking { - val sum = deferred.sumOf { it.await().toLong() } - println("Sum: $sum") -} -``` - -Now it prints something sensible: `500000500000`, because all coroutines complete. - -Let's also make sure that our coroutines actually run in parallel. If we add a 1-second `delay()` to each of the `async`'s, the resulting program won't run for 1'000'000 seconds (over 11,5 days): - -```kotlin -val deferred = (1..1_000_000).map { n -> - GlobalScope.async { - delay(1000) - n - } -} -``` - -This takes about 10 seconds on my machine, so yes, coroutines do run in parallel. - -## Suspending functions - -Now, let's say we want to extract our _workload_ (which is "wait 1 second and return a number") into a separate function: - -```kotlin -fun workload(n: Int): Int { - delay(1000) - return n -} -``` - -A familiar error pops up: - -> Suspend functions are only allowed to be called from a coroutine or another suspend function. -> -{type="note"} - -Let's dig a little into what it means. The biggest merit of coroutines is that they can _suspend_ without blocking a thread. The compiler has to emit some special code to make this possible, so we have to mark functions that _may suspend_ explicitly in the code. We use the `suspend` modifier for it: - -```kotlin -suspend fun workload(n: Int): Int { - delay(1000) - return n -} -``` - -Now when we call `workload()` from a coroutine, the compiler knows that it may suspend and will prepare accordingly: - -```kotlin -GlobalScope.async { - workload(n) -} -``` - -Our `workload()` function can be called from a coroutine (or another suspending function), but _cannot_ be called from outside a coroutine. Naturally, `delay()` and `await()` that we used above are themselves declared as `suspend`, and this is why we had to put them inside `runBlocking {}`, `launch {}` or `async {}`. diff --git a/docs/topics/coroutines-basics.md b/docs/topics/coroutines-basics.md index c1c17581c0..ab8b427685 100644 --- a/docs/topics/coroutines-basics.md +++ b/docs/topics/coroutines-basics.md @@ -6,19 +6,27 @@ This section covers basic coroutine concepts. ## Your first coroutine -Run the following code: +A _coroutine_ is an instance of suspendable computation. It is conceptually similar to a thread, in the sense that it +takes a block of code to run that works concurrently with the rest of the code. +However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. + +Coroutines can be thought of as light-weight threads, but there is a number +of important differences that make their real-life usage very different from threads. + +Run the following code to get to your first working coroutine: ```kotlin import kotlinx.coroutines.* -fun main() { - GlobalScope.launch { // launch a new coroutine in background and continue +//sampleStart +fun main() = runBlocking { // this: CoroutineScope + launch { // launch a new coroutine and continue delay(1000L) // non-blocking delay for 1 second (default time unit is ms) println("World!") // print after delay } - println("Hello,") // main thread continues while coroutine is delayed - Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive + println("Hello") // main coroutine continues while a previous one is delayed } +//sampleEnd ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} @@ -29,49 +37,71 @@ fun main() { You will see the following result: ```text -Hello, +Hello World! ``` -Essentially, coroutines are light-weight threads. -They are launched with [launch] _coroutine builder_ in a context of some [CoroutineScope]. -Here we are launching a new coroutine in the [GlobalScope], meaning that the lifetime of the new -coroutine is limited only by the lifetime of the whole application. +Let's dissect what this code does. -You can achieve the same result by replacing -`GlobalScope.launch { ... }` with `thread { ... }`, and `delay(...)` with `Thread.sleep(...)`. -Try it (don't forget to import `kotlin.concurrent.thread`). +[launch] is a _coroutine builder_. It launches a new coroutine concurrently with +the rest of the code, which continues to work independently. That's why `Hello` has been printed first. -If you start by replacing `GlobalScope.launch` with `thread`, the compiler produces the following error: +[delay] is a special _suspending function_. It _suspends_ the coroutine for a specific time. Suspending a coroutine +does not _block_ the underlying thread, but allows other coroutines to run and use the underlying thread for +their code. + +[runBlocking] is also a coroutine builder that bridges the non-coroutine world of a regular `fun main()` and +the code with coroutines inside of `runBlocking { ... }` curly braces. This is highlighted in an IDE by +`this: CoroutineScope` hint right after the `runBlocking` opening curly brace. + +If you remove or forget `runBlocking` in this code, you'll get an error on the [launch] call, since `launch` +is declared only in the [CoroutineScope]: ```Plain Text -Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function +Unresolved reference: launch ``` -That is because [delay] is a special _suspending function_ that does not block a thread, but _suspends_ the -coroutine, and it can be only used from a coroutine. +The name of `runBlocking` means that the thread that runs it (in this case — the main thread) gets _blocked_ for +the duration of the call, until all the coroutines inside `runBlocking { ... }` complete their execution. You will +often see `runBlocking` used like that at the very top-level of the application and quite rarely inside the real code, +as threads are expensive resources and blocking them is inefficient and is often not desired. + +### Structured concurrency + +Coroutines follow a principle of +**structured concurrency** which means that new coroutines can be only launched in a specific [CoroutineScope] +which delimits the lifetime of the coroutine. The above example shows that [runBlocking] establishes the corresponding +scope and that is why the previous example waits until `World!` is printed after a second's delay and only then exits. + +In the real application, you will be launching a lot of coroutines. Structured concurrency ensures that they are not +lost and do not leak. An outer scope cannot complete until all its children coroutines complete. +Structured concurrency also ensures that any errors in the code are properly reported and are never lost. -## Bridging blocking and non-blocking worlds +## Extract function refactoring -The first example mixes _non-blocking_ `delay(...)` and _blocking_ `Thread.sleep(...)` in the same code. -It is easy to lose track of which one is blocking and which one is not. -Let's be explicit about blocking using the [runBlocking] coroutine builder: +Let's extract the block of code inside `launch { ... }` into a separate function. When you +perform "Extract function" refactoring on this code, you get a new function with the `suspend` modifier. +This is your first _suspending function_. Suspending functions can be used inside coroutines +just like regular functions, but their additional feature is that they can, in turn, +use other suspending functions (like `delay` in this example) to _suspend_ execution of a coroutine. ```kotlin import kotlinx.coroutines.* -fun main() { - GlobalScope.launch { // launch a new coroutine in background and continue - delay(1000L) - println("World!") - } - println("Hello,") // main thread continues here immediately - runBlocking { // but this expression blocks the main thread - delay(2000L) // ... while we delay for 2 seconds to keep JVM alive - } +//sampleStart +fun main() = runBlocking { // this: CoroutineScope + launch { doWorld() } + println("Hello") } + +// this is your first suspending function +suspend fun doWorld() { + delay(1000L) + println("World!") +} +//sampleEnd ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} @@ -80,27 +110,39 @@ fun main() { {type="note"} -The result is the same, but this code uses only non-blocking [delay]. -The main thread invoking `runBlocking` _blocks_ until the coroutine inside `runBlocking` completes. +## Scope builder + +In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the +[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete. + +[runBlocking] and [coroutineScope][_coroutineScope] builders may look similar because they both wait for their body and all its children to complete. +The main difference is that the [runBlocking] method _blocks_ the current thread for waiting, +while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages. +Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function. -This example can be also rewritten in a more idiomatic way, using `runBlocking` to wrap -the execution of the main function: +You can use `coroutineScope` from any suspending function. +For example, you can move the concurrent printing of `Hello` and `World` into a `suspend fun doWorld()` function: ```kotlin import kotlinx.coroutines.* -fun main() = runBlocking { // start main coroutine - GlobalScope.launch { // launch a new coroutine in background and continue +//sampleStart +fun main() = runBlocking { + doWorld() +} + +suspend fun doWorld() = coroutineScope { // this: CoroutineScope + launch { delay(1000L) println("World!") } - println("Hello,") // main coroutine continues here immediately - delay(2000L) // delaying for 2 seconds to keep JVM alive + println("Hello") } +//sampleEnd ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} @@ -108,49 +150,41 @@ fun main() = runBlocking { // start main coroutine > {type="note"} +This code also prints: + -Here `runBlocking { ... }` works as an adaptor that is used to start the top-level main coroutine. -We explicitly specify its `Unit` return type, because a well-formed `main` function in Kotlin has to return `Unit`. - -This is also a way to write unit tests for suspending functions: - - - -```kotlin -class MyTest { - @Test - fun testMySuspendingFunction() = runBlocking { - // here we can use suspending functions using any assertion style that we like - } -} -``` - - +## Scope builder and concurrency -## Waiting for a job - -Delaying for a time while another coroutine is working is not a good approach. Let's explicitly -wait (in a non-blocking way) until the background [Job] that we have launched is complete: +A [coroutineScope][_coroutineScope] builder can be used inside any suspending function to perform multiple concurrent operations. +Let's launch two concurrent coroutines inside a `doWorld` suspending function: ```kotlin import kotlinx.coroutines.* -fun main() = runBlocking { //sampleStart - val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job +// Sequentially executes doWorld followed by "Hello" +fun main() = runBlocking { + doWorld() + println("Done") +} + +// Concurrently executes both sections +suspend fun doWorld() = coroutineScope { // this: CoroutineScope + launch { + delay(2000L) + println("World 2") + } + launch { delay(1000L) - println("World!") + println("World 1") } - println("Hello,") - job.join() // wait until child coroutine completes -//sampleEnd + println("Hello") } +//sampleEnd ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} @@ -158,42 +192,39 @@ fun main() = runBlocking { > {type="note"} - - -Now the result is still the same, but the code of the main coroutine is not tied to the duration of -the background job in any way. Much better. +Both pieces of code inside `launch { ... }` blocks execute _concurrently_, with +`World 1` printed first, after a second from start, and `World 2` printed next, after two seconds from start. +A [coroutineScope][_coroutineScope] in `doWorld` completes only after both are complete, so `doWorld` returns and +allows `Done` string to be printed only after that: -## Structured concurrency +```text +Hello +World 1 +World 2 +Done +``` -There is still something to be desired for practical usage of coroutines. -When we use `GlobalScope.launch`, we create a top-level coroutine. Even though it is light-weight, it still -consumes some memory resources while it runs. If we forget to keep a reference to the newly launched -coroutine, it still runs. What if the code in the coroutine hangs (for example, we erroneously -delay for too long), what if we launched too many coroutines and ran out of memory? -Having to manually keep references to all the launched coroutines and [join][Job.join] them is error-prone. + -There is a better solution. We can use structured concurrency in our code. -Instead of launching coroutines in the [GlobalScope], just like we usually do with threads (threads are always global), -we can launch coroutines in the specific scope of the operation we are performing. +## An explicit job -In our example, we have a `main` function that is turned into a coroutine using the [runBlocking] coroutine builder. -Every coroutine builder, including `runBlocking`, adds an instance of [CoroutineScope] to the scope of its code block. -We can launch coroutines in this scope without having to `join` them explicitly, because -an outer coroutine (`runBlocking` in our example) does not complete until all the coroutines launched -in its scope complete. Thus, we can make our example simpler: +A [launch] coroutine builder returns a [Job] object that is a handle to the launched coroutine and can be +used to explicitly wait for its completion. For example, you can wait for completion of the child coroutine +and then print "Done" string: ```kotlin import kotlinx.coroutines.* -fun main() = runBlocking { // this: CoroutineScope - launch { // launch a new coroutine in the scope of runBlocking +fun main() = runBlocking { +//sampleStart + val job = launch { // launch a new coroutine and keep a reference to its Job delay(1000L) println("World!") } - println("Hello,") + println("Hello") + job.join() // wait until child coroutine completes + println("Done") +//sampleEnd } ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} @@ -202,101 +233,15 @@ fun main() = runBlocking { // this: CoroutineScope > {type="note"} - - -## Scope builder - -In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the -[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete. - -[runBlocking] and [coroutineScope][_coroutineScope] may look similar because they both wait for their body and all its children to complete. -The main difference is that the [runBlocking] method _blocks_ the current thread for waiting, -while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages. -Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function. - -It can be demonstrated by the following example: - -```kotlin -import kotlinx.coroutines.* - -fun main() = runBlocking { // this: CoroutineScope - launch { - delay(200L) - println("Task from runBlocking") - } - - coroutineScope { // Creates a coroutine scope - launch { - delay(500L) - println("Task from nested launch") - } - - delay(100L) - println("Task from coroutine scope") // This line will be printed before the nested launch - } - - println("Coroutine scope is over") // This line is not printed until the nested launch completes -} -``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt). -> -{type="note"} - - - -Note that right after the "Task from coroutine scope" message (while waiting for nested launch) - "Task from runBlocking" is executed and printed — even though the [coroutineScope][_coroutineScope] is not completed yet. - -## Extract function refactoring - -Let's extract the block of code inside `launch { ... }` into a separate function. When you -perform "Extract function" refactoring on this code, you get a new function with the `suspend` modifier. -This is your first _suspending function_. Suspending functions can be used inside coroutines -just like regular functions, but their additional feature is that they can, in turn, -use other suspending functions (like `delay` in this example) to _suspend_ execution of a coroutine. - -```kotlin -import kotlinx.coroutines.* - -fun main() = runBlocking { - launch { doWorld() } - println("Hello,") -} - -// this is your first suspending function -suspend fun doWorld() { - delay(1000L) - println("World!") -} -``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt). -> -{type="note"} +This code produces: - +Done +``` -But what if the extracted function contains a coroutine builder which is invoked on the current scope? -In this case, the `suspend` modifier on the extracted function is not enough. Making `doWorld` an extension -method on `CoroutineScope` is one of the solutions, but it may not always be applicable as it does not make the API clearer. -The idiomatic solution is to have either an explicit `CoroutineScope` as a field in a class containing the target function -or an implicit one when the outer class implements `CoroutineScope`. -As a last resort, [CoroutineScope(coroutineContext)][CoroutineScope()] can be used, but such an approach is structurally unsafe -because you no longer have control on the scope of execution of this method. Only private APIs can use this builder. + ## Coroutines ARE light-weight @@ -305,6 +250,7 @@ Run the following code: ```kotlin import kotlinx.coroutines.* +//sampleStart fun main() = runBlocking { repeat(100_000) { // launch a lot of coroutines launch { @@ -313,9 +259,10 @@ fun main() = runBlocking { } } } +//sampleEnd ``` -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt). +> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt). > {type="note"} @@ -323,57 +270,17 @@ fun main() = runBlocking { It launches 100K coroutines and, after 5 seconds, each coroutine prints a dot. -Now, try that with threads. What would happen? (Most likely your code will produce some sort of out-of-memory error) - -## Global coroutines are like daemon threads - -The following code launches a long-running coroutine in [GlobalScope] that prints "I'm sleeping" twice a second and then -returns from the main function after some delay: - -```kotlin -import kotlinx.coroutines.* - -fun main() = runBlocking { -//sampleStart - GlobalScope.launch { - repeat(1000) { i -> - println("I'm sleeping $i ...") - delay(500L) - } - } - delay(1300L) // just quit after delay -//sampleEnd -} -``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt). -> -{type="note"} - -You can run and see that it prints three lines and terminates: - -```text -I'm sleeping 0 ... -I'm sleeping 1 ... -I'm sleeping 2 ... -``` - - - -Active coroutines that were launched in [GlobalScope] do not keep the process alive. They are like daemon threads. +Now, try that with threads (remove `runBlocking`, replace `launch` with `thread`, and replace `delay` with `Thread.sleep`). +What would happen? (Most likely your code will produce some sort of out-of-memory error) [launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html -[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html -[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html [delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html [runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html -[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html -[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html +[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html [_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html -[CoroutineScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html +[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html diff --git a/docs/topics/coroutines-guide.md b/docs/topics/coroutines-guide.md index 3e9fa90490..050376ce1b 100644 --- a/docs/topics/coroutines-guide.md +++ b/docs/topics/coroutines-guide.md @@ -17,7 +17,6 @@ In order to use coroutines as well as follow the examples in this guide, you nee ## Table of contents * [Coroutines basics](coroutines-basics.md) -* [Tutorial: Create a basic coroutine](coroutines-basic-jvm.md) * [Hands-on: Intro to coroutines and channels](https://play.kotlinlang.org/hands-on/Introduction%20to%20Coroutines%20and%20Channels) * [Cancellation and timeouts](cancellation-and-timeouts.md) * [Composing suspending functions](composing-suspending-functions.md) diff --git a/docs/topics/exception-handling.md b/docs/topics/exception-handling.md index fbd11313cc..4cff42f357 100644 --- a/docs/topics/exception-handling.md +++ b/docs/topics/exception-handling.md @@ -19,9 +19,16 @@ exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.r It can be demonstrated by a simple example that creates root coroutines using the [GlobalScope]: +> [GlobalScope] is a delicate API that can backfire in non-trivial ways. Creating a root coroutine for the +> whole application is one of the rare legitimate uses for `GlobalScope`, so you must explicitly opt-in into +> using `GlobalScope` with `@OptIn(DelicateCoroutinesApi::class)`. +> +{type="note"} + ```kotlin import kotlinx.coroutines.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { val job = GlobalScope.launch { // root coroutine with launch println("Throwing exception from launch") @@ -90,6 +97,7 @@ so its `CoroutineExceptionHandler` has no effect either. ```kotlin import kotlinx.coroutines.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { //sampleStart val handler = CoroutineExceptionHandler { _, exception -> @@ -184,6 +192,7 @@ which is demonstrated by the following example. ```kotlin import kotlinx.coroutines.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { //sampleStart val handler = CoroutineExceptionHandler { _, exception -> @@ -242,6 +251,7 @@ import kotlinx.coroutines.exceptions.* import kotlinx.coroutines.* import java.io.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}") @@ -292,6 +302,7 @@ Cancellation exceptions are transparent and are unwrapped by default: import kotlinx.coroutines.* import java.io.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { //sampleStart val handler = CoroutineExceptionHandler { _, exception -> @@ -510,4 +521,4 @@ The scope is completed [produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html [ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html - \ No newline at end of file + diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index dcbf62b565..fdb70200f4 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -270,6 +270,9 @@ public final class kotlinx/coroutines/DelayKt { public static final fun delay-VtjQ1oo (JLkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public abstract interface annotation class kotlinx/coroutines/DelicateCoroutinesApi : java/lang/annotation/Annotation { +} + public final class kotlinx/coroutines/Dispatchers { public static final field INSTANCE Lkotlinx/coroutines/Dispatchers; public static final fun getDefault ()Lkotlinx/coroutines/CoroutineDispatcher; diff --git a/kotlinx-coroutines-core/common/src/Annotations.kt b/kotlinx-coroutines-core/common/src/Annotations.kt index 1ade389d37..724cc8cb87 100644 --- a/kotlinx-coroutines-core/common/src/Annotations.kt +++ b/kotlinx-coroutines-core/common/src/Annotations.kt @@ -6,6 +6,22 @@ package kotlinx.coroutines import kotlinx.coroutines.flow.* +/** + * Marks declarations in the coroutines that are **delicate** — + * they have limited use-case and shall be used with care in general code. + * Any use of a delicate declaration has to be carefully reviewed to make sure it is + * properly used and does not create problems like memory and resource leaks. + * Carefully read documentation of any declaration marked as `DelicateCoroutinesApi`. + */ +@MustBeDocumented +@Retention(value = AnnotationRetention.BINARY) +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "This is a delicate API and its use requires care." + + " Make sure you fully read and understand documentation of the declaration that is marked as a delicate API." +) +public annotation class DelicateCoroutinesApi + /** * Marks declarations that are still **experimental** in coroutines API, which means that the design of the * corresponding declarations has open issues which may (or may not) lead to their changes in the future. diff --git a/kotlinx-coroutines-core/common/src/CoroutineScope.kt b/kotlinx-coroutines-core/common/src/CoroutineScope.kt index e7c243a42d..df2ee615dc 100644 --- a/kotlinx-coroutines-core/common/src/CoroutineScope.kt +++ b/kotlinx-coroutines-core/common/src/CoroutineScope.kt @@ -16,7 +16,10 @@ import kotlin.coroutines.intrinsics.* * is an extension on [CoroutineScope] and inherits its [coroutineContext][CoroutineScope.coroutineContext] * to automatically propagate all its elements and cancellation. * - * The best ways to obtain a standalone instance of the scope are [CoroutineScope()] and [MainScope()] factory functions. + * The best ways to obtain a standalone instance of the scope are [CoroutineScope()] and [MainScope()] factory functions, + * taking care to cancel these coroutine scopes when they are no longer needed (see section on custom usage below for + * explanation and example). + * * Additional context elements can be appended to the scope using the [plus][CoroutineScope.plus] operator. * * ### Convention for structured concurrency @@ -38,12 +41,23 @@ import kotlin.coroutines.intrinsics.* * * ### Custom usage * - * [CoroutineScope] should be implemented or declared as a property on entities with a well-defined lifecycle that are - * responsible for launching children coroutines, for example: + * `CoroutineScope` should be declared as a property on entities with a well-defined lifecycle that are + * responsible for launching children coroutines. The corresponding instance of `CoroutineScope` shall be created + * with either `CoroutineScope()` or `MainScope()` functions. The difference between them is only in the + * [CoroutineDispatcher]: + * + * * `CoroutineScope()` uses [Dispatchers.Default] for its coroutines. + * * `MainScope()` uses [Dispatchers.Main] for its coroutines. + * + * **The key part of custom usage of `CustomScope` is cancelling it and the end of the lifecycle.** + * The [CoroutineScope.cancel] extension function shall be used when the entity that was launching coroutines + * is no longer needed. It cancels all the coroutines that might still be running on behalf of it. + * + * For example: * * ``` * class MyUIClass { - * val scope = MainScope() // the scope of MyUIClass + * val scope = MainScope() // the scope of MyUIClass, uses Dispatchers.Main * * fun destroy() { // destroys an instance of MyUIClass * scope.cancel() // cancels all coroutines launched in this scope @@ -124,25 +138,81 @@ public val CoroutineScope.isActive: Boolean /** * A global [CoroutineScope] not bound to any job. - * * Global scope is used to launch top-level coroutines which are operating on the whole application lifetime * and are not cancelled prematurely. - * Another use of the global scope is operators running in [Dispatchers.Unconfined], which don't have any job associated with them. * - * Application code usually should use an application-defined [CoroutineScope]. Using - * [async][CoroutineScope.async] or [launch][CoroutineScope.launch] - * on the instance of [GlobalScope] is highly discouraged. + * Active coroutines launched in `GlobalScope` do not keep the process alive. They are like daemon threads. + * + * This is a **delicate** API. It is easy to accidentally create resource or memory leaks when + * `GlobalScope` is used. A coroutine launched in `GlobalScope` is not subject to the principle of structured + * concurrency, so if it hangs or gets delayed due to a problem (e.g. due to a slow network), it will stay working + * and consuming resources. For example, consider the following code: + * + * ``` + * fun loadConfiguration() { + * GlobalScope.launch { + * val config = fetchConfigFromServer() // network request + * updateConfiguration(config) + * } + * } + * ``` + * + * A call to `loadConfiguration` creates a coroutine in the `GlobalScope` that works in background without any + * provision to cancel it or to wait for its completion. If a network is slow, it keeps waiting in background, + * consuming resources. Repeated calls to `loadConfiguration` will consume more and more resources. + * + * ### Possible replacements + * + * In may cases uses of `GlobalScope` should be removed, marking the containing operation with `suspend`, for example: + * + * ``` + * suspend fun loadConfiguration() { + * val config = fetchConfigFromServer() // network request + * updateConfiguration(config) + * } + * ``` + * + * In cases when `GlobalScope.launch` was used to launch multiple concurrent operations, the corresponding + * operations shall be grouped with [coroutineScope] instead: + * + * ``` + * // concurrently load configuration and data + * suspend fun loadConfigurationAndData() { + * coroutinesScope { + * launch { loadConfiguration() } + * launch { loadData() } + * } + * } + * ``` + * + * In top-level code, when launching a concurrent operation operation from a non-suspending context, an appropriately + * confined instance of [CoroutineScope] shall be used instead of a `GlobalScope`. See docs on [CoroutineScope] for + * details. + * + * ### GlobalScope vs custom scope + * + * Do not replace `GlobalScope.launch { ... }` with `CoroutineScope().launch { ... }` constructor function call. + * The latter has the same pitfalls as `GlobalScope`. See [CoroutineScope] documentation on the intended usage of + * `CoroutineScope()` constructor function. + * + * ### Legitimate use-cases * - * Usage of this interface may look like this: + * There are limited circumstances under which `GlobalScope` can be legitimately and safely used, such as top-level background + * processes that must stay active for the whole duration of the application's lifetime. Because of that, any use + * of `GlobalScope` requires an explicit opt-in with `@OptIn(DelicateCoroutinesApi::class)`, like this: * * ``` - * fun ReceiveChannel.sqrt(): ReceiveChannel = GlobalScope.produce(Dispatchers.Unconfined) { - * for (number in this) { - * send(Math.sqrt(number)) + * // A global coroutine to log statistics every second, must be always active + * @OptIn(DelicateCoroutinesApi::class) + * val globalScopeReporter = GlobalScope.launch { + * while (true) { + * delay(1000) + * logStatistics() * } * } * ``` */ +@DelicateCoroutinesApi public object GlobalScope : CoroutineScope { /** * Returns [EmptyCoroutineContext]. diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt index f04b100a8a..529f881730 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt @@ -7,11 +7,10 @@ package kotlinx.coroutines.guide.exampleBasic01 import kotlinx.coroutines.* -fun main() { - GlobalScope.launch { // launch a new coroutine in background and continue +fun main() = runBlocking { // this: CoroutineScope + launch { // launch a new coroutine and continue delay(1000L) // non-blocking delay for 1 second (default time unit is ms) println("World!") // print after delay } - println("Hello,") // main thread continues while coroutine is delayed - Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive + println("Hello") // main coroutine continues while a previous one is delayed } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt index bfece26bc9..6bf2af4c31 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt @@ -7,13 +7,13 @@ package kotlinx.coroutines.guide.exampleBasic02 import kotlinx.coroutines.* -fun main() { - GlobalScope.launch { // launch a new coroutine in background and continue - delay(1000L) - println("World!") - } - println("Hello,") // main thread continues here immediately - runBlocking { // but this expression blocks the main thread - delay(2000L) // ... while we delay for 2 seconds to keep JVM alive - } +fun main() = runBlocking { // this: CoroutineScope + launch { doWorld() } + println("Hello") +} + +// this is your first suspending function +suspend fun doWorld() { + delay(1000L) + println("World!") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt index 8541f60472..67b6894ada 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt @@ -7,11 +7,14 @@ package kotlinx.coroutines.guide.exampleBasic03 import kotlinx.coroutines.* -fun main() = runBlocking { // start main coroutine - GlobalScope.launch { // launch a new coroutine in background and continue +fun main() = runBlocking { + doWorld() +} + +suspend fun doWorld() = coroutineScope { // this: CoroutineScope + launch { delay(1000L) println("World!") } - println("Hello,") // main coroutine continues here immediately - delay(2000L) // delaying for 2 seconds to keep JVM alive + println("Hello") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt index 69f827715d..3531b22083 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt @@ -7,11 +7,21 @@ package kotlinx.coroutines.guide.exampleBasic04 import kotlinx.coroutines.* +// Sequentially executes doWorld followed by "Hello" fun main() = runBlocking { - val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job + doWorld() + println("Done") +} + +// Concurrently executes both sections +suspend fun doWorld() = coroutineScope { // this: CoroutineScope + launch { + delay(2000L) + println("World 2") + } + launch { delay(1000L) - println("World!") + println("World 1") } - println("Hello,") - job.join() // wait until child coroutine completes + println("Hello") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt index 9d530b5f2b..193f2cc3aa 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt @@ -7,10 +7,12 @@ package kotlinx.coroutines.guide.exampleBasic05 import kotlinx.coroutines.* -fun main() = runBlocking { // this: CoroutineScope - launch { // launch a new coroutine in the scope of runBlocking +fun main() = runBlocking { + val job = launch { // launch a new coroutine and keep a reference to its Job delay(1000L) println("World!") } - println("Hello,") + println("Hello") + job.join() // wait until child coroutine completes + println("Done") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt index b53d3b8962..24b890a0ad 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt @@ -7,21 +7,11 @@ package kotlinx.coroutines.guide.exampleBasic06 import kotlinx.coroutines.* -fun main() = runBlocking { // this: CoroutineScope - launch { - delay(200L) - println("Task from runBlocking") - } - - coroutineScope { // Creates a coroutine scope +fun main() = runBlocking { + repeat(100_000) { // launch a lot of coroutines launch { - delay(500L) - println("Task from nested launch") + delay(5000L) + print(".") } - - delay(100L) - println("Task from coroutine scope") // This line will be printed before the nested launch } - - println("Coroutine scope is over") // This line is not printed until the nested launch completes } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt deleted file mode 100644 index cd854ce89a..0000000000 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -// This file was automatically generated from coroutines-basics.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exampleBasic07 - -import kotlinx.coroutines.* - -fun main() = runBlocking { - launch { doWorld() } - println("Hello,") -} - -// this is your first suspending function -suspend fun doWorld() { - delay(1000L) - println("World!") -} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt deleted file mode 100644 index 0a346e0be8..0000000000 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-08.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -// This file was automatically generated from coroutines-basics.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exampleBasic08 - -import kotlinx.coroutines.* - -fun main() = runBlocking { - repeat(100_000) { // launch a lot of coroutines - launch { - delay(5000L) - print(".") - } - } -} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt deleted file mode 100644 index c9783ee50f..0000000000 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-09.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -// This file was automatically generated from coroutines-basics.md by Knit tool. Do not edit. -package kotlinx.coroutines.guide.exampleBasic09 - -import kotlinx.coroutines.* - -fun main() = runBlocking { - GlobalScope.launch { - repeat(1000) { i -> - println("I'm sleeping $i ...") - delay(500L) - } - } - delay(1300L) // just quit after delay -} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt index 312dc72b55..35536a7d14 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt @@ -23,10 +23,12 @@ fun main() { println("Completed in $time ms") } +@OptIn(DelicateCoroutinesApi::class) fun somethingUsefulOneAsync() = GlobalScope.async { doSomethingUsefulOne() } +@OptIn(DelicateCoroutinesApi::class) fun somethingUsefulTwoAsync() = GlobalScope.async { doSomethingUsefulTwo() } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt index e23eaf2542..c6ad4516da 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt @@ -10,9 +10,9 @@ import kotlinx.coroutines.* fun main() = runBlocking { // launch a coroutine to process some kind of incoming request val request = launch { - // it spawns two other jobs, one with GlobalScope - GlobalScope.launch { - println("job1: I run in GlobalScope and execute independently!") + // it spawns two other jobs + launch(Job()) { + println("job1: I run in my own Job and execute independently!") delay(1000) println("job1: I am not affected by cancellation of the request") } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt index e08ddd0811..24cbabe094 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt @@ -7,6 +7,7 @@ package kotlinx.coroutines.guide.exampleExceptions01 import kotlinx.coroutines.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { val job = GlobalScope.launch { // root coroutine with launch println("Throwing exception from launch") diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt index 67fdaa7177..c3ab68a53a 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt @@ -7,6 +7,7 @@ package kotlinx.coroutines.guide.exampleExceptions02 import kotlinx.coroutines.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler got $exception") diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt index 9c9b43d22e..b966c1eab4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt @@ -7,6 +7,7 @@ package kotlinx.coroutines.guide.exampleExceptions04 import kotlinx.coroutines.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler got $exception") diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt index 04f9385f06..5f1f3d89a9 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.exceptions.* import kotlinx.coroutines.* import java.io.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}") diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt index 5a5b276bc3..bc9f77b936 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt @@ -8,6 +8,7 @@ package kotlinx.coroutines.guide.exampleExceptions06 import kotlinx.coroutines.* import java.io.* +@OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler got $exception") diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt index 765cd0b9df..7e54fb1d26 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/BasicsGuideTest.kt @@ -12,7 +12,7 @@ class BasicsGuideTest { @Test fun testExampleBasic01() { test("ExampleBasic01") { kotlinx.coroutines.guide.exampleBasic01.main() }.verifyLines( - "Hello,", + "Hello", "World!" ) } @@ -20,7 +20,7 @@ class BasicsGuideTest { @Test fun testExampleBasic02() { test("ExampleBasic02") { kotlinx.coroutines.guide.exampleBasic02.main() }.verifyLines( - "Hello,", + "Hello", "World!" ) } @@ -28,7 +28,7 @@ class BasicsGuideTest { @Test fun testExampleBasic03() { test("ExampleBasic03") { kotlinx.coroutines.guide.exampleBasic03.main() }.verifyLines( - "Hello,", + "Hello", "World!" ) } @@ -36,50 +36,26 @@ class BasicsGuideTest { @Test fun testExampleBasic04() { test("ExampleBasic04") { kotlinx.coroutines.guide.exampleBasic04.main() }.verifyLines( - "Hello,", - "World!" + "Hello", + "World 1", + "World 2", + "Done" ) } @Test fun testExampleBasic05() { test("ExampleBasic05") { kotlinx.coroutines.guide.exampleBasic05.main() }.verifyLines( - "Hello,", - "World!" + "Hello", + "World!", + "Done" ) } @Test fun testExampleBasic06() { - test("ExampleBasic06") { kotlinx.coroutines.guide.exampleBasic06.main() }.verifyLines( - "Task from coroutine scope", - "Task from runBlocking", - "Task from nested launch", - "Coroutine scope is over" - ) - } - - @Test - fun testExampleBasic07() { - test("ExampleBasic07") { kotlinx.coroutines.guide.exampleBasic07.main() }.verifyLines( - "Hello,", - "World!" - ) - } - - @Test - fun testExampleBasic08() { - test("ExampleBasic08") { kotlinx.coroutines.guide.exampleBasic08.main() }.also { lines -> + test("ExampleBasic06") { kotlinx.coroutines.guide.exampleBasic06.main() }.also { lines -> check(lines.size == 1 && lines[0] == ".".repeat(100_000)) } } - - @Test - fun testExampleBasic09() { - test("ExampleBasic09") { kotlinx.coroutines.guide.exampleBasic09.main() }.verifyLines( - "I'm sleeping 0 ...", - "I'm sleeping 1 ...", - "I'm sleeping 2 ..." - ) - } } diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt index d6f1c21dc0..1a84fb9427 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/DispatcherGuideTest.kt @@ -57,7 +57,7 @@ class DispatcherGuideTest { @Test fun testExampleContext06() { test("ExampleContext06") { kotlinx.coroutines.guide.exampleContext06.main() }.verifyLines( - "job1: I run in GlobalScope and execute independently!", + "job1: I run in my own Job and execute independently!", "job2: I am a child of the request coroutine", "job1: I am not affected by cancellation of the request", "main: Who has survived request cancellation?" From 998f67bac740ed27e32658d9d8a6f05ab7396c4b Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 20 Apr 2021 16:47:40 +0300 Subject: [PATCH 39/55] Properly implement replacement for Flow.concatWith(flow) to avoid deadlocks when the downstream throws, but other flow has no elements in it (#2659) Fixes KT-46013 --- kotlinx-coroutines-core/common/src/flow/Migration.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/flow/Migration.kt b/kotlinx-coroutines-core/common/src/flow/Migration.kt index 57749523d4..6278081a5d 100644 --- a/kotlinx-coroutines-core/common/src/flow/Migration.kt +++ b/kotlinx-coroutines-core/common/src/flow/Migration.kt @@ -344,13 +344,13 @@ public fun Flow.concatWith(value: T): Flow = noImpl() /** * Flow analogue of `concatWith` is [onCompletion]. - * Use `onCompletion { emitAll(other) }`. + * Use `onCompletion { if (it == null) emitAll(other) }`. * @suppress */ @Deprecated( level = DeprecationLevel.ERROR, - message = "Flow analogue of 'concatWith' is 'onCompletion'. Use 'onCompletion { emitAll(other) }'", - replaceWith = ReplaceWith("onCompletion { emitAll(other) }") + message = "Flow analogue of 'concatWith' is 'onCompletion'. Use 'onCompletion { if (it == null) emitAll(other) }'", + replaceWith = ReplaceWith("onCompletion { if (it == null) emitAll(other) }") ) public fun Flow.concatWith(other: Flow): Flow = noImpl() @@ -404,7 +404,7 @@ public fun Flow.combineLatest( * @suppress */ @Deprecated( - level = DeprecationLevel.WARNING, // since 1.3.0, error in 1.4.0 + level = DeprecationLevel.ERROR, // since 1.3.0, error in 1.5.0 message = "Use 'onStart { delay(timeMillis) }'", replaceWith = ReplaceWith("onStart { delay(timeMillis) }") ) @@ -416,7 +416,7 @@ public fun Flow.delayFlow(timeMillis: Long): Flow = onStart { delay(ti * @suppress */ @Deprecated( - level = DeprecationLevel.WARNING, // since 1.3.0, error in 1.4.0 + level = DeprecationLevel.ERROR, // since 1.3.0, error in 1.5.0 message = "Use 'onEach { delay(timeMillis) }'", replaceWith = ReplaceWith("onEach { delay(timeMillis) }") ) @@ -430,7 +430,7 @@ public fun Flow.delayEach(timeMillis: Long): Flow = onEach { delay(tim public fun Flow.switchMap(transform: suspend (value: T) -> Flow): Flow = flatMapLatest(transform) @Deprecated( - level = DeprecationLevel.WARNING, // Since 1.3.8, was experimental when deprecated + level = DeprecationLevel.ERROR, // Warning since 1.3.8, was experimental when deprecated, ERROR since 1.5.0 message = "'scanReduce' was renamed to 'runningReduce' to be consistent with Kotlin standard library", replaceWith = ReplaceWith("runningReduce(operation)") ) From c752d64ca8a69dfb544abc761376e7253755e92f Mon Sep 17 00:00:00 2001 From: Roman Elizarov Date: Tue, 20 Apr 2021 16:53:46 +0300 Subject: [PATCH 40/55] Shared overflow doc (#2560) * Clarify docs about the restriction on MutableSharedFlow function's onBufferOverflow parameter. * Clarify docs about MutableSharedFlow function without subscribers but with onBufferOverflow Co-authored-by: Zach Klippenstein --- .../common/src/flow/SharedFlow.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt index 25118d75a1..9bcf088e95 100644 --- a/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt +++ b/kotlinx-coroutines-core/common/src/flow/SharedFlow.kt @@ -68,6 +68,13 @@ import kotlin.native.concurrent.* * the `onBufferOverflow` parameter, which is equal to one of the entries of the [BufferOverflow] enum. When a strategy other * than [SUSPENDED][BufferOverflow.SUSPEND] is configured, emissions to the shared flow never suspend. * + * **Buffer overflow condition can happen only when there is at least one subscriber that is not ready to accept + * the new value.** In the absence of subscribers only the most recent `replay` values are stored and the buffer + * overflow behavior is never triggered and has no effect. In particular, in the absence of subscribers emitter never + * suspends despite [BufferOverflow.SUSPEND] option and [BufferOverflow.DROP_LATEST] option does not have effect either. + * Essentially, the behavior in the absence of subscribers is always similar to [BufferOverflow.DROP_OLDEST], + * but the buffer is just of `replay` size (without any `extraBufferCapacity`). + * * ### Unbuffered shared flow * * A default implementation of a shared flow that is created with `MutableSharedFlow()` constructor function @@ -221,9 +228,12 @@ public interface MutableSharedFlow : SharedFlow, FlowCollector { * @param replay the number of values replayed to new subscribers (cannot be negative, defaults to zero). * @param extraBufferCapacity the number of values buffered in addition to `replay`. * [emit][MutableSharedFlow.emit] does not suspend while there is a buffer space remaining (optional, cannot be negative, defaults to zero). - * @param onBufferOverflow configures an action on buffer overflow (optional, defaults to - * [suspending][BufferOverflow.SUSPEND] attempts to [emit][MutableSharedFlow.emit] a value, - * supported only when `replay > 0` or `extraBufferCapacity > 0`). + * @param onBufferOverflow configures an [emit][MutableSharedFlow.emit] action on buffer overflow. Optional, defaults to + * [suspending][BufferOverflow.SUSPEND] attempts to emit a value. + * Values other than [BufferOverflow.SUSPEND] are supported only when `replay > 0` or `extraBufferCapacity > 0`. + * **Buffer overflow can happen only when there is at least one subscriber that is not ready to accept + * the new value.** In the absence of subscribers only the most recent [replay] values are stored and + * the buffer overflow behavior is never triggered and has no effect. */ @Suppress("FunctionName", "UNCHECKED_CAST") public fun MutableSharedFlow( From cefb84f6d5fe9a350f0e2fce90dcd8b81f380618 Mon Sep 17 00:00:00 2001 From: Andrey Polyakov Date: Tue, 20 Apr 2021 19:51:02 +0300 Subject: [PATCH 41/55] Update coroutines-guide.md link (#2661) --- docs/topics/coroutines-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/coroutines-guide.md b/docs/topics/coroutines-guide.md index 050376ce1b..3d857991a1 100644 --- a/docs/topics/coroutines-guide.md +++ b/docs/topics/coroutines-guide.md @@ -32,5 +32,5 @@ In order to use coroutines as well as follow the examples in this guide, you nee ## Additional references * [Guide to UI programming with coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md) -* [Coroutines design document (KEEP)](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md) +* [Coroutines design document (KEEP)](https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md) * [Full kotlinx.coroutines API reference](https://kotlin.github.io/kotlinx.coroutines) From 71df60e732ff91103af76636b97bcd7705a6df7d Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Wed, 21 Apr 2021 10:30:33 +0300 Subject: [PATCH 42/55] Deprecate awaitSingleOr*, specialize some await* functions for Mono and Maybe (#2628) * Deprecated `awaitSingleOr*` on arbitrary Publishers * Added specialized `awaitSingle` and `awaitSingleOrNull` methods on `Maybe` and `Mono` * Deprecated `Maybe.await()` in favor of `Maybe.awaitSingleOrNull()` * Added specializations of most of the `await*` methods for `Mono` and deprecated them, as the only useful methods on `Mono` are `awaitSingle` and `awaitSingleOrNull` * Reworded some documentation for `await*` methods Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/2591 Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/1587 --- reactive/kotlinx-coroutines-jdk9/src/Await.kt | 72 ++++---- .../kotlinx-coroutines-jdk9/test/AwaitTest.kt | 43 +++++ .../kotlinx-coroutines-reactive/src/Await.kt | 145 +++++++++++------ .../test/AwaitTest.kt | 43 +++++ .../api/kotlinx-coroutines-reactor.api | 7 + .../kotlinx-coroutines-reactor/src/Mono.kt | 154 +++++++++++++++++- .../src/ReactorContext.kt | 4 +- .../test/FlowAsFluxTest.kt | 4 +- .../test/MonoTest.kt | 59 ++++++- .../test/ReactorContextTest.kt | 12 +- .../api/kotlinx-coroutines-rx2.api | 2 + .../kotlinx-coroutines-rx2/src/RxAwait.kt | 140 ++++++++++------ .../test/CompletableTest.kt | 30 +++- .../kotlinx-coroutines-rx2/test/MaybeTest.kt | 46 ++++-- .../test/ObservableSingleTest.kt | 30 +++- .../kotlinx-coroutines-rx2/test/SingleTest.kt | 28 +++- .../api/kotlinx-coroutines-rx3.api | 2 + .../kotlinx-coroutines-rx3/src/RxAwait.kt | 141 ++++++++++------ .../test/CompletableTest.kt | 30 +++- .../kotlinx-coroutines-rx3/test/MaybeTest.kt | 46 ++++-- .../test/ObservableSingleTest.kt | 30 +++- .../kotlinx-coroutines-rx3/test/SingleTest.kt | 28 +++- 22 files changed, 871 insertions(+), 225 deletions(-) create mode 100644 reactive/kotlinx-coroutines-jdk9/test/AwaitTest.kt create mode 100644 reactive/kotlinx-coroutines-reactive/test/AwaitTest.kt diff --git a/reactive/kotlinx-coroutines-jdk9/src/Await.kt b/reactive/kotlinx-coroutines-jdk9/src/Await.kt index 4febf4079c..dfe6ec52f2 100644 --- a/reactive/kotlinx-coroutines-jdk9/src/Await.kt +++ b/reactive/kotlinx-coroutines-jdk9/src/Await.kt @@ -4,78 +4,82 @@ package kotlinx.coroutines.jdk9 +import kotlinx.coroutines.* import java.util.concurrent.* import org.reactivestreams.FlowAdapters import kotlinx.coroutines.reactive.* /** - * Awaits for the first value from the given publisher without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the first value from the given publisher without blocking the thread and returns the resulting value, or, if + * the publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. * - * @throws NoSuchElementException if publisher does not emit any value + * @throws NoSuchElementException if the publisher does not emit any value */ -public suspend fun Flow.Publisher.awaitFirst(): T = FlowAdapters.toPublisher(this).awaitFirst() +public suspend fun Flow.Publisher.awaitFirst(): T = + FlowAdapters.toPublisher(this).awaitFirst() /** - * Awaits for the first value from the given observable or the [default] value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given publisher, or returns the [default] value if none is emitted, without blocking + * the thread, and returns the resulting value, or, if this publisher has produced an error, throws the corresponding + * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. */ public suspend fun Flow.Publisher.awaitFirstOrDefault(default: T): T = - FlowAdapters.toPublisher(this).awaitFirstOrDefault(default) + FlowAdapters.toPublisher(this).awaitFirstOrDefault(default) /** - * Awaits for the first value from the given observable or `null` value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given publisher, or returns `null` if none is emitted, without blocking the thread, + * and returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. */ public suspend fun Flow.Publisher.awaitFirstOrNull(): T? = - FlowAdapters.toPublisher(this).awaitFirstOrNull() + FlowAdapters.toPublisher(this).awaitFirstOrNull() /** - * Awaits for the first value from the given observable or call [defaultValue] to get a value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given publisher, or calls [defaultValue] to get a value if none is emitted, without + * blocking the thread, and returns the resulting value, or, if this publisher has produced an error, throws the + * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. */ public suspend fun Flow.Publisher.awaitFirstOrElse(defaultValue: () -> T): T = - FlowAdapters.toPublisher(this).awaitFirstOrElse(defaultValue) + FlowAdapters.toPublisher(this).awaitFirstOrElse(defaultValue) /** - * Awaits for the last value from the given publisher without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the last value from the given publisher without blocking the thread and + * returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. * - * @throws NoSuchElementException if publisher does not emit any value + * @throws NoSuchElementException if the publisher does not emit any value */ public suspend fun Flow.Publisher.awaitLast(): T = - FlowAdapters.toPublisher(this).awaitLast() + FlowAdapters.toPublisher(this).awaitLast() /** - * Awaits for the single value from the given publisher without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the single value from the given publisher without blocking the thread and returns the resulting value, or, + * if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Flow.Subscription] and resumes with [CancellationException]. * - * @throws NoSuchElementException if publisher does not emit any value - * @throws IllegalArgumentException if publisher emits more than one value + * @throws NoSuchElementException if the publisher does not emit any value + * @throws IllegalArgumentException if the publisher emits more than one value */ public suspend fun Flow.Publisher.awaitSingle(): T = - FlowAdapters.toPublisher(this).awaitSingle() + FlowAdapters.toPublisher(this).awaitSingle() diff --git a/reactive/kotlinx-coroutines-jdk9/test/AwaitTest.kt b/reactive/kotlinx-coroutines-jdk9/test/AwaitTest.kt new file mode 100644 index 0000000000..5a95d098fd --- /dev/null +++ b/reactive/kotlinx-coroutines-jdk9/test/AwaitTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.jdk9 + +import kotlinx.coroutines.* +import org.junit.* +import java.util.concurrent.Flow as JFlow + +class AwaitTest: TestBase() { + + /** Tests that calls to [awaitFirst] (and, thus, to the rest of these functions) throw [CancellationException] and + * unsubscribe from the publisher when their [Job] is cancelled. */ + @Test + fun testAwaitCancellation() = runTest { + expect(1) + val publisher = JFlow.Publisher { s -> + s.onSubscribe(object : JFlow.Subscription { + override fun request(n: Long) { + expect(3) + } + + override fun cancel() { + expect(5) + } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + publisher.awaitFirst() + } catch (e: CancellationException) { + expect(6) + throw e + } + } + expect(4) + job.cancelAndJoin() + finish(7) + } + +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactive/src/Await.kt b/reactive/kotlinx-coroutines-reactive/src/Await.kt index 9af134cb94..067f5e8031 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Await.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Await.kt @@ -9,114 +9,161 @@ import org.reactivestreams.Publisher import org.reactivestreams.Subscriber import org.reactivestreams.Subscription import java.lang.IllegalStateException -import java.util.* import kotlin.coroutines.* /** - * Awaits for the first value from the given publisher without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the first value from the given publisher without blocking the thread and returns the resulting value, or, if + * the publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. * - * @throws NoSuchElementException if publisher does not emit any value + * @throws NoSuchElementException if the publisher does not emit any value */ public suspend fun Publisher.awaitFirst(): T = awaitOne(Mode.FIRST) /** - * Awaits for the first value from the given observable or the [default] value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given publisher, or returns the [default] value if none is emitted, without blocking + * the thread, and returns the resulting value, or, if this publisher has produced an error, throws the corresponding + * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. */ public suspend fun Publisher.awaitFirstOrDefault(default: T): T = awaitOne(Mode.FIRST_OR_DEFAULT, default) /** - * Awaits for the first value from the given observable or `null` value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given publisher, or returns `null` if none is emitted, without blocking the thread, + * and returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. */ public suspend fun Publisher.awaitFirstOrNull(): T? = awaitOne(Mode.FIRST_OR_DEFAULT) /** - * Awaits for the first value from the given observable or call [defaultValue] to get a value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given publisher, or calls [defaultValue] to get a value if none is emitted, without + * blocking the thread, and returns the resulting value, or, if this publisher has produced an error, throws the + * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. */ public suspend fun Publisher.awaitFirstOrElse(defaultValue: () -> T): T = awaitOne(Mode.FIRST_OR_DEFAULT) ?: defaultValue() /** - * Awaits for the last value from the given publisher without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the last value from the given publisher without blocking the thread and + * returns the resulting value, or, if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. * - * @throws NoSuchElementException if publisher does not emit any value + * @throws NoSuchElementException if the publisher does not emit any value */ public suspend fun Publisher.awaitLast(): T = awaitOne(Mode.LAST) /** - * Awaits for the single value from the given publisher without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the single value from the given publisher without blocking the thread and returns the resulting value, or, + * if this publisher has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. * - * @throws NoSuchElementException if publisher does not emit any value - * @throws IllegalArgumentException if publisher emits more than one value + * @throws NoSuchElementException if the publisher does not emit any value + * @throws IllegalArgumentException if the publisher emits more than one value */ public suspend fun Publisher.awaitSingle(): T = awaitOne(Mode.SINGLE) /** - * Awaits for the single value from the given publisher or the [default] value if none is emitted without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the single value from the given publisher, or returns the [default] value if none is emitted, without + * blocking the thread, and returns the resulting value, or, if this publisher has produced an error, throws the + * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. * - * @throws NoSuchElementException if publisher does not emit any value - * @throws IllegalArgumentException if publisher emits more than one value + * ### Deprecation + * + * This method is deprecated because the conventions established in Kotlin mandate that an operation with the name + * `awaitSingleOrDefault` returns the default value instead of throwing in case there is an error; however, this would + * also mean that this method would return the default value if there are *too many* values. This could be confusing to + * those who expect this function to validate that there is a single element or none at all emitted, and cases where + * there are no elements are indistinguishable from those where there are too many, though these cases have different + * meaning. + * + * @throws NoSuchElementException if the publisher does not emit any value + * @throws IllegalArgumentException if the publisher emits more than one value */ +@Deprecated( + message = "Deprecated without a replacement due to its name incorrectly conveying the behavior. " + + "Please consider using awaitFirstOrDefault().", + level = DeprecationLevel.WARNING +) // Warning since 1.5, error in 1.6, hidden in 1.7 public suspend fun Publisher.awaitSingleOrDefault(default: T): T = awaitOne(Mode.SINGLE_OR_DEFAULT, default) /** - * Awaits for the single value from the given publisher or `null` value if none is emitted without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the single value from the given publisher without blocking the thread and returns the resulting value, or, if + * this publisher has produced an error, throws the corresponding exception. If more than one value or none were + * produced by the publisher, `null` is returned. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. + * + * ### Deprecation * - * @throws NoSuchElementException if publisher does not emit any value - * @throws IllegalArgumentException if publisher emits more than one value + * This method is deprecated because the conventions established in Kotlin mandate that an operation with the name + * `awaitSingleOrNull` returns `null` instead of throwing in case there is an error; however, this would + * also mean that this method would return `null` if there are *too many* values. This could be confusing to + * those who expect this function to validate that there is a single element or none at all emitted, and cases where + * there are no elements are indistinguishable from those where there are too many, though these cases have different + * meaning. + * + * @throws IllegalArgumentException if the publisher emits more than one value */ -public suspend fun Publisher.awaitSingleOrNull(): T = awaitOne(Mode.SINGLE_OR_DEFAULT) +@Deprecated( + message = "Deprecated without a replacement due to its name incorrectly conveying the behavior. " + + "There is a specialized version for Reactor's Mono, please use that where applicable. " + + "Alternatively, please consider using awaitFirstOrNull().", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull()", "kotlinx.coroutines.reactor") +) // Warning since 1.5, error in 1.6, hidden in 1.7 +public suspend fun Publisher.awaitSingleOrNull(): T? = awaitOne(Mode.SINGLE_OR_DEFAULT) /** - * Awaits for the single value from the given publisher or call [defaultValue] to get a value if none is emitted without blocking a thread and - * returns the resulting value or throws the corresponding exception if this publisher had produced error. + * Awaits the single value from the given publisher, or calls [defaultValue] to get a value if none is emitted, without + * blocking the thread, and returns the resulting value, or, if this publisher has produced an error, throws the + * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. + * + * ### Deprecation + * + * This method is deprecated because the conventions established in Kotlin mandate that an operation with the name + * `awaitSingleOrElse` returns the calculated value instead of throwing in case there is an error; however, this would + * also mean that this method would return the calculated value if there are *too many* values. This could be confusing + * to those who expect this function to validate that there is a single element or none at all emitted, and cases where + * there are no elements are indistinguishable from those where there are too many, though these cases have different + * meaning. * - * @throws NoSuchElementException if publisher does not emit any value - * @throws IllegalArgumentException if publisher emits more than one value + * @throws IllegalArgumentException if the publisher emits more than one value */ -public suspend fun Publisher.awaitSingleOrElse(defaultValue: () -> T): T = awaitOne(Mode.SINGLE_OR_DEFAULT) ?: defaultValue() +@Deprecated( + message = "Deprecated without a replacement due to its name incorrectly conveying the behavior. " + + "Please consider using awaitFirstOrElse().", + level = DeprecationLevel.WARNING +) // Warning since 1.5, error in 1.6, hidden in 1.7 +public suspend fun Publisher.awaitSingleOrElse(defaultValue: () -> T): T = + awaitOne(Mode.SINGLE_OR_DEFAULT) ?: defaultValue() // ------------------------ private ------------------------ diff --git a/reactive/kotlinx-coroutines-reactive/test/AwaitTest.kt b/reactive/kotlinx-coroutines-reactive/test/AwaitTest.kt new file mode 100644 index 0000000000..6749423f80 --- /dev/null +++ b/reactive/kotlinx-coroutines-reactive/test/AwaitTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.reactive + +import kotlinx.coroutines.* +import org.junit.* +import org.reactivestreams.* + +class AwaitTest: TestBase() { + + /** Tests that calls to [awaitFirst] (and, thus, to the rest of these functions) throw [CancellationException] and + * unsubscribe from the publisher when their [Job] is cancelled. */ + @Test + fun testAwaitCancellation() = runTest { + expect(1) + val publisher = Publisher { s -> + s.onSubscribe(object: Subscription { + override fun request(n: Long) { + expect(3) + } + + override fun cancel() { + expect(5) + } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + publisher.awaitFirst() + } catch (e: CancellationException) { + expect(6) + throw e + } + } + expect(4) + job.cancelAndJoin() + finish(7) + } + +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api b/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api index 3a1c8b7d31..0a10aa12a9 100644 --- a/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api +++ b/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api @@ -17,6 +17,13 @@ public final class kotlinx/coroutines/reactor/FluxKt { } public final class kotlinx/coroutines/reactor/MonoKt { + public static final fun awaitFirst (Lreactor/core/publisher/Mono;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitFirstOrDefault (Lreactor/core/publisher/Mono;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitFirstOrElse (Lreactor/core/publisher/Mono;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitFirstOrNull (Lreactor/core/publisher/Mono;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitLast (Lreactor/core/publisher/Mono;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitSingle (Lreactor/core/publisher/Mono;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitSingleOrNull (Lreactor/core/publisher/Mono;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun mono (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Mono; public static final synthetic fun mono (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)Lreactor/core/publisher/Mono; public static synthetic fun mono$default (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lreactor/core/publisher/Mono; diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index 6e7b95ba6e..6a4a38f379 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -12,7 +12,6 @@ import org.reactivestreams.* import reactor.core.* import reactor.core.publisher.* import kotlin.coroutines.* -import kotlin.internal.* import kotlinx.coroutines.internal.* /** @@ -35,6 +34,50 @@ public fun mono( return monoInternal(GlobalScope, context, block) } +/** + * Awaits the single value from the given [Mono] without blocking the thread and returns the resulting value, or, if + * this publisher has produced an error, throws the corresponding exception. If the Mono completed without a value, + * `null` is returned. + * + * This suspending function is cancellable. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. + */ +public suspend fun Mono.awaitSingleOrNull(): T? = suspendCancellableCoroutine { cont -> + injectCoroutineContext(cont.context).subscribe(object : Subscriber { + private var seenValue = false + + override fun onSubscribe(s: Subscription) { + cont.invokeOnCancellation { s.cancel() } + s.request(Long.MAX_VALUE) + } + + override fun onComplete() { + if (!seenValue) cont.resume(null) + } + + override fun onNext(t: T) { + seenValue = true + cont.resume(t) + } + + override fun onError(error: Throwable) { cont.resumeWithException(error) } + }) +} + +/** + * Awaits the single value from the given [Mono] without blocking the thread and returns the resulting value, or, + * if this Mono has produced an error, throws the corresponding exception. + * + * This suspending function is cancellable. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately cancels its [Subscription] and resumes with [CancellationException]. + * + * @throws NoSuchElementException if the Mono does not emit any value + */ +// TODO: consider using https://github.com/Kotlin/kotlinx.coroutines/issues/2607 once that lands +public suspend fun Mono.awaitSingle(): T = awaitSingleOrNull() ?: throw NoSuchElementException() + private fun monoInternal( scope: CoroutineScope, // support for legacy mono in scope context: CoroutineContext, @@ -92,3 +135,112 @@ public fun CoroutineScope.mono( block: suspend CoroutineScope.() -> T? ): Mono = monoInternal(this, context, block) +/** + * This is a lint function that was added already deprecated in order to guard against confusing usages on [Mono]. + * On [Publisher] instances other than [Mono], this function is not deprecated. + * + * Both [awaitFirst] and [awaitSingle] await the first value, or throw [NoSuchElementException] if there is none, but + * the name [Mono.awaitSingle] better reflects the semantics of [Mono]. + * + * For example, consider this code: + * ``` + * myDbClient.findById(uniqueId).awaitFirst() // findById returns a `Mono` + * ``` + * It looks like more than one value could be returned from `findById` and [awaitFirst] discards the extra elements, + * when in fact, at most a single value can be present. + */ +@Deprecated( + message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + + "Please use awaitSingle() instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingle()") +) // Warning since 1.5, error in 1.6 +public suspend fun Mono.awaitFirst(): T = awaitSingle() + +/** + * This is a lint function that was added already deprecated in order to guard against confusing usages on [Mono]. + * On [Publisher] instances other than [Mono], this function is not deprecated. + * + * Both [awaitFirstOrDefault] and [awaitSingleOrNull] await the first value, or return some special value if there + * is none, but the name [Mono.awaitSingleOrNull] better reflects the semantics of [Mono]. + * + * For example, consider this code: + * ``` + * myDbClient.findById(uniqueId).awaitFirstOrDefault(default) // findById returns a `Mono` + * ``` + * It looks like more than one value could be returned from `findById` and [awaitFirstOrDefault] discards the extra + * elements, when in fact, at most a single value can be present. + */ +@Deprecated( + message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + + "Please use awaitSingleOrNull() instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull() ?: default") +) // Warning since 1.5, error in 1.6 +public suspend fun Mono.awaitFirstOrDefault(default: T): T = awaitSingleOrNull() ?: default + +/** + * This is a lint function that was added already deprecated in order to guard against confusing usages on [Mono]. + * On [Publisher] instances other than [Mono], this function is not deprecated. + * + * Both [awaitFirstOrNull] and [awaitSingleOrNull] await the first value, or return some special value if there + * is none, but the name [Mono.awaitSingleOrNull] better reflects the semantics of [Mono]. + * + * For example, consider this code: + * ``` + * myDbClient.findById(uniqueId).awaitFirstOrNull() // findById returns a `Mono` + * ``` + * It looks like more than one value could be returned from `findById` and [awaitFirstOrNull] discards the extra + * elements, when in fact, at most a single value can be present. + */ +@Deprecated( + message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + + "Please use awaitSingleOrNull() instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull()") +) // Warning since 1.5, error in 1.6 +public suspend fun Mono.awaitFirstOrNull(): T? = awaitSingleOrNull() + +/** + * This is a lint function that was added already deprecated in order to guard against confusing usages on [Mono]. + * On [Publisher] instances other than [Mono], this function is not deprecated. + * + * Both [awaitFirstOrElse] and [awaitSingleOrNull] await the first value, or return some special value if there + * is none, but the name [Mono.awaitSingleOrNull] better reflects the semantics of [Mono]. + * + * For example, consider this code: + * ``` + * myDbClient.findById(uniqueId).awaitFirstOrElse(defaultValue) // findById returns a `Mono` + * ``` + * It looks like more than one value could be returned from `findById` and [awaitFirstOrElse] discards the extra + * elements, when in fact, at most a single value can be present. + */ +@Deprecated( + message = "Mono produces at most one value, so the semantics of dropping the remaining elements are not useful. " + + "Please use awaitSingleOrNull() instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull() ?: defaultValue()") +) // Warning since 1.5, error in 1.6 +public suspend fun Mono.awaitFirstOrElse(defaultValue: () -> T): T = awaitSingleOrNull() ?: defaultValue() + +/** + * This is a lint function that was added already deprecated in order to guard against confusing usages on [Mono]. + * On [Publisher] instances other than [Mono], this function is not deprecated. + * + * Both [awaitLast] and [awaitSingle] await the single value, or throw [NoSuchElementException] if there is none, but + * the name [Mono.awaitSingle] better reflects the semantics of [Mono]. + * + * For example, consider this code: + * ``` + * myDbClient.findById(uniqueId).awaitLast() // findById returns a `Mono` + * ``` + * It looks like more than one value could be returned from `findById` and [awaitLast] discards the initial elements, + * when in fact, at most a single value can be present. + */ +@Deprecated( + message = "Mono produces at most one value, so the last element is the same as the first. " + + "Please use awaitSingle() instead.", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingle()") +) // Warning since 1.5, error in 1.6 +public suspend fun Mono.awaitLast(): T = awaitSingle() \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt index be4b2c7d45..333f056d97 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt @@ -16,8 +16,8 @@ import kotlinx.coroutines.reactive.* * * This context element is implicitly propagated through subscriber's context by all Reactive integrations, such as [mono], [flux], * [Publisher.asFlow][asFlow], [Flow.asPublisher][asPublisher] and [Flow.asFlux][asFlux]. - * Functions that subscribe to the reactive stream (e.g. [Publisher.awaitFirst][awaitFirst]) also propagate [ReactorContext] to the - * subscriber's [Context]. + * Functions that subscribe to the reactive stream (e.g. [Publisher.awaitFirst][kotlinx.coroutines.reactive.awaitFirst]) + * also propagate the [ReactorContext] to the subscriber's [Context]. ** * ### Examples of Reactive context integration. * diff --git a/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt b/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt index dbe97b17d8..d8807385f0 100644 --- a/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/FlowAsFluxTest.kt @@ -14,8 +14,8 @@ import kotlin.test.* class FlowAsFluxTest : TestBase() { @Test fun testFlowAsFluxContextPropagation() { - val flux = flow { - (1..4).forEach { i -> emit(createMono(i).awaitFirst()) } + val flux = flow { + (1..4).forEach { i -> emit(createMono(i).awaitSingle()) } } .asFlux() .contextWrite(Context.of(1, "1")) diff --git a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt index a98c514f19..421295d115 100644 --- a/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/MonoTest.kt @@ -13,7 +13,6 @@ import org.junit.Test import org.reactivestreams.* import reactor.core.publisher.* import reactor.util.context.* -import java.time.* import java.time.Duration.* import java.util.function.* import kotlin.test.* @@ -115,6 +114,52 @@ class MonoTest : TestBase() { @Test fun testMonoAwait() = runBlocking { assertEquals("OK", Mono.just("O").awaitSingle() + "K") + assertEquals("OK", Mono.just("O").awaitSingleOrNull() + "K") + assertFailsWith{ Mono.empty().awaitSingle() } + assertNull(Mono.empty().awaitSingleOrNull()) + } + + /** Tests that the versions of the await methods specialized for Mono for deprecation behave correctly and we don't + * break any code by introducing them. */ + @Test + @Suppress("DEPRECATION") + fun testDeprecatedAwaitMethods() = runBlocking { + val filledMono = mono { "OK" } + assertEquals("OK", filledMono.awaitFirst()) + assertEquals("OK", filledMono.awaitFirstOrDefault("!")) + assertEquals("OK", filledMono.awaitFirstOrNull()) + assertEquals("OK", filledMono.awaitFirstOrElse { "ELSE" }) + assertEquals("OK", filledMono.awaitLast()) + assertEquals("OK", filledMono.awaitSingleOrDefault("!")) + assertEquals("OK", filledMono.awaitSingleOrElse { "ELSE" }) + val emptyMono = mono { null } + assertFailsWith { emptyMono.awaitFirst() } + assertEquals("OK", emptyMono.awaitFirstOrDefault("OK")) + assertNull(emptyMono.awaitFirstOrNull()) + assertEquals("ELSE", emptyMono.awaitFirstOrElse { "ELSE" }) + assertFailsWith { emptyMono.awaitLast() } + assertEquals("OK", emptyMono.awaitSingleOrDefault("OK")) + assertEquals("ELSE", emptyMono.awaitSingleOrElse { "ELSE" }) + } + + /** Tests that calls to [awaitSingleOrNull] (and, thus, to the rest of such functions) throw [CancellationException] + * and unsubscribe from the publisher when their [Job] is cancelled. */ + @Test + fun testAwaitCancellation() = runTest { + expect(1) + val mono = mono { delay(Long.MAX_VALUE) }.doOnSubscribe { expect(3) }.doOnCancel { expect(5) } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + mono.awaitSingleOrNull() + } catch (e: CancellationException) { + expect(6) + throw e + } + } + expect(4) + job.cancelAndJoin() + finish(7) } @Test @@ -264,7 +309,7 @@ class MonoTest : TestBase() { .interval(ofMillis(1)) .switchMap { mono(coroutineContext) { - timeBomb().awaitFirst() + timeBomb().awaitSingle() } } .onErrorReturn({ @@ -275,14 +320,14 @@ class MonoTest : TestBase() { finish(2) } - private fun timeBomb() = Mono.delay(Duration.ofMillis(1)).doOnSuccess { throw Exception("something went wrong") } + private fun timeBomb() = Mono.delay(ofMillis(1)).doOnSuccess { throw Exception("something went wrong") } @Test fun testLeakedException() = runBlocking { // Test exception is not reported to global handler val flow = mono { throw TestException() }.toFlux().asFlow() repeat(10000) { - combine(flow, flow) { _, _ -> Unit } + combine(flow, flow) { _, _ -> } .catch {} .collect { } } @@ -373,13 +418,13 @@ class MonoTest : TestBase() { Hooks.resetOnOperatorError("testDownstreamCancellationDoesNotThrow") } - /** Run the given [Publisher], cancel it, wait for the cancellation handler to finish, and return only then. + /** Run the given [Mono], cancel it, wait for the cancellation handler to finish, and return only then. * * Will not work in the general case, but here, when the publisher uses [Dispatchers.Unconfined], this seems to * ensure that the cancellation handler will have nowhere to execute but serially with the cancellation. */ - private suspend fun Publisher.awaitCancelAndJoin() = coroutineScope { + private suspend fun Mono.awaitCancelAndJoin() = coroutineScope { async(start = CoroutineStart.UNDISPATCHED) { - awaitFirstOrNull() + awaitSingleOrNull() }.cancelAndJoin() } } diff --git a/reactive/kotlinx-coroutines-reactor/test/ReactorContextTest.kt b/reactive/kotlinx-coroutines-reactor/test/ReactorContextTest.kt index aff29241c9..577238be1d 100644 --- a/reactive/kotlinx-coroutines-reactor/test/ReactorContextTest.kt +++ b/reactive/kotlinx-coroutines-reactor/test/ReactorContextTest.kt @@ -20,7 +20,7 @@ class ReactorContextTest : TestBase() { } } .contextWrite(Context.of(2, "2", 3, "3", 4, "4", 5, "5")) .contextWrite { ctx -> ctx.put(6, "6") } - assertEquals(mono.awaitFirst(), "1234567") + assertEquals(mono.awaitSingle(), "1234567") } @Test @@ -43,22 +43,18 @@ class ReactorContextTest : TestBase() { (1..3).forEach { append(ctx.getOrDefault(it, "noValue")) } } } .contextWrite(Context.of(2, "2")) - .awaitFirst() + .awaitSingle() assertEquals(result, "123") } @Test fun testMonoAwaitContextPropagation() = runBlocking(Context.of(7, "7").asCoroutineContext()) { - assertEquals(createMono().awaitFirst(), "7") - assertEquals(createMono().awaitFirstOrDefault("noValue"), "7") - assertEquals(createMono().awaitFirstOrNull(), "7") - assertEquals(createMono().awaitFirstOrElse { "noValue" }, "7") - assertEquals(createMono().awaitLast(), "7") assertEquals(createMono().awaitSingle(), "7") + assertEquals(createMono().awaitSingleOrNull(), "7") } @Test - fun testFluxAwaitContextPropagation() = runBlocking( + fun testFluxAwaitContextPropagation() = runBlocking( Context.of(1, "1", 2, "2", 3, "3").asCoroutineContext() ) { assertEquals(createFlux().awaitFirst(), "1") diff --git a/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api b/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api index eb5e2fb4ad..c27ef4d796 100644 --- a/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api +++ b/reactive/kotlinx-coroutines-rx2/api/kotlinx-coroutines-rx2.api @@ -8,7 +8,9 @@ public final class kotlinx/coroutines/rx2/RxAwaitKt { public static final fun awaitFirstOrNull (Lio/reactivex/ObservableSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun awaitLast (Lio/reactivex/ObservableSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun awaitOrDefault (Lio/reactivex/MaybeSource;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitSingle (Lio/reactivex/MaybeSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun awaitSingle (Lio/reactivex/ObservableSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitSingleOrNull (Lio/reactivex/MaybeSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlinx/coroutines/rx2/RxChannelKt { diff --git a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt index 6e162c9a4d..d7b8ee26f6 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxAwait.kt @@ -15,11 +15,12 @@ import kotlin.coroutines.* // ------------------------ CompletableSource ------------------------ /** - * Awaits for completion of this completable without blocking a thread. - * Returns `Unit` or throws the corresponding exception if this completable had produced error. + * Awaits for completion of this completable without blocking the thread. + * Returns `Unit`, or throws the corresponding exception if this completable produces an error. * * This suspending function is cancellable. If the [Job] of the invoking coroutine is cancelled or completed while this - * suspending function is suspended, this function immediately resumes with [CancellationException]. + * suspending function is suspended, this function immediately resumes with [CancellationException] and disposes of its + * subscription. */ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine { cont -> subscribe(object : CompletableObserver { @@ -31,6 +32,37 @@ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine // ------------------------ MaybeSource ------------------------ +/** + * Awaits for completion of the [MaybeSource] without blocking the thread. + * Returns the resulting value, or `null` if no value is produced, or throws the corresponding exception if this + * [MaybeSource] produces an error. + * + * This suspending function is cancellable. + * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * function immediately resumes with [CancellationException] and disposes of its subscription. + */ +@Suppress("UNCHECKED_CAST") +public suspend fun MaybeSource.awaitSingleOrNull(): T? = suspendCancellableCoroutine { cont -> + subscribe(object : MaybeObserver { + override fun onSubscribe(d: Disposable) { cont.disposeOnCancellation(d) } + override fun onComplete() { cont.resume(null) } + override fun onSuccess(t: T) { cont.resume(t) } + override fun onError(error: Throwable) { cont.resumeWithException(error) } + }) +} + +/** + * Awaits for completion of the [MaybeSource] without blocking the thread. + * Returns the resulting value, or throws if either no value is produced or this [MaybeSource] produces an error. + * + * This suspending function is cancellable. + * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * function immediately resumes with [CancellationException] and disposes of its subscription. + * + * @throws NoSuchElementException if no elements were produced by this [MaybeSource]. + */ +public suspend fun MaybeSource.awaitSingle(): T = awaitSingleOrNull() ?: throw NoSuchElementException() + /** * Awaits for completion of the maybe without blocking a thread. * Returns the resulting value, null if no value was produced or throws the corresponding exception if this @@ -39,9 +71,18 @@ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine * This suspending function is cancellable. * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function * immediately resumes with [CancellationException]. + * + * ### Deprecation + * + * Deprecated in favor of [awaitSingleOrNull] in order to reflect that `null` can be returned to denote the absence of + * a value, as opposed to throwing in such case. */ -@Suppress("UNCHECKED_CAST") -public suspend fun MaybeSource.await(): T? = (this as MaybeSource).awaitOrDefault(null) +@Deprecated( + message = "Deprecated in favor of awaitSingleOrNull()", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull()") +) // Warning since 1.5, error in 1.6, hidden in 1.7 +public suspend fun MaybeSource.await(): T? = awaitSingleOrNull() /** * Awaits for completion of the maybe without blocking a thread. @@ -51,25 +92,28 @@ public suspend fun MaybeSource.await(): T? = (this as MaybeSource).aw * This suspending function is cancellable. * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function * immediately resumes with [CancellationException]. + * + * ### Deprecation + * + * Deprecated in favor of [awaitSingleOrNull] for naming consistency (see the deprecation of [MaybeSource.await] for + * details). */ -public suspend fun MaybeSource.awaitOrDefault(default: T): T = suspendCancellableCoroutine { cont -> - subscribe(object : MaybeObserver { - override fun onSubscribe(d: Disposable) { cont.disposeOnCancellation(d) } - override fun onComplete() { cont.resume(default) } - override fun onSuccess(t: T) { cont.resume(t) } - override fun onError(error: Throwable) { cont.resumeWithException(error) } - }) -} +@Deprecated( + message = "Deprecated in favor of awaitSingleOrNull()", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull() ?: default") +) // Warning since 1.5, error in 1.6, hidden in 1.7 +public suspend fun MaybeSource.awaitOrDefault(default: T): T = awaitSingleOrNull() ?: default // ------------------------ SingleSource ------------------------ /** - * Awaits for completion of the single value without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this single had produced error. + * Awaits for completion of the single value response without blocking the thread. + * Returns the resulting value, or throws the corresponding exception if this response produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun SingleSource.await(): T = suspendCancellableCoroutine { cont -> subscribe(object : SingleObserver { @@ -82,69 +126,73 @@ public suspend fun SingleSource.await(): T = suspendCancellableCoroutine // ------------------------ ObservableSource ------------------------ /** - * Awaits for the first value from the given observable without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable] without blocking the thread and returns the resulting value, or, + * if the observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. * - * @throws NoSuchElementException if observable does not emit any value + * @throws NoSuchElementException if the observable does not emit any value */ public suspend fun ObservableSource.awaitFirst(): T = awaitOne(Mode.FIRST) /** - * Awaits for the first value from the given observable or the [default] value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable], or returns the [default] value if none is emitted, without + * blocking the thread, and returns the resulting value, or, if this observable has produced an error, throws the + * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrDefault(default: T): T = awaitOne(Mode.FIRST_OR_DEFAULT, default) /** - * Awaits for the first value from the given observable or `null` value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable], or returns `null` if none is emitted, without blocking the + * thread, and returns the resulting value, or, if this observable has produced an error, throws the corresponding + * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrNull(): T? = awaitOne(Mode.FIRST_OR_DEFAULT) /** - * Awaits for the first value from the given observable or call [defaultValue] to get a value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable], or calls [defaultValue] to get a value if none is emitted, + * without blocking the thread, and returns the resulting value, or, if this observable has produced an error, throws + * the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ -public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: () -> T): T = awaitOne(Mode.FIRST_OR_DEFAULT) ?: defaultValue() +public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: () -> T): T = + awaitOne(Mode.FIRST_OR_DEFAULT) ?: defaultValue() /** - * Awaits for the last value from the given observable without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the last value from the given [Observable] without blocking the thread and + * returns the resulting value, or, if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. * - * @throws NoSuchElementException if observable does not emit any value + * @throws NoSuchElementException if the observable does not emit any value */ public suspend fun ObservableSource.awaitLast(): T = awaitOne(Mode.LAST) /** - * Awaits for the single value from the given observable without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the single value from the given observable without blocking the thread and returns the resulting value, or, + * if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. * - * @throws NoSuchElementException if observable does not emit any value - * @throws IllegalArgumentException if observable emits more than one value + * @throws NoSuchElementException if the observable does not emit any value + * @throws IllegalArgumentException if the observable emits more than one value */ public suspend fun ObservableSource.awaitSingle(): T = awaitOne(Mode.SINGLE) diff --git a/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt b/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt index 298b32bf32..16f0005ba1 100644 --- a/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/CompletableTest.kt @@ -98,6 +98,31 @@ class CompletableTest : TestBase() { } } + /** Tests that calls to [await] throw [CancellationException] and dispose of the subscription when their [Job] is + * cancelled. */ + @Test + fun testAwaitCancellation() = runTest { + expect(1) + val completable = CompletableSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + completable.await() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) + } + @Test fun testSuppressedException() = runTest { val completable = rxCompletable(currentDispatcher()) { @@ -119,7 +144,7 @@ class CompletableTest : TestBase() { } @Test - fun testUnhandledException() = runTest() { + fun testUnhandledException() = runTest { expect(1) var disposable: Disposable? = null val handler = { e: Throwable -> @@ -165,8 +190,7 @@ class CompletableTest : TestBase() { withExceptionHandler(handler) { rxCompletable(Dispatchers.Unconfined) { expect(1) - 42 - }.subscribe({ throw LinkageError() }) + }.subscribe { throw LinkageError() } finish(3) } } diff --git a/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt b/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt index 08427dcf3d..292f6187fa 100644 --- a/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt @@ -7,13 +7,12 @@ package kotlinx.coroutines.rx2 import io.reactivex.* import io.reactivex.disposables.* import io.reactivex.exceptions.* -import io.reactivex.functions.* import io.reactivex.internal.functions.Functions.* import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import org.junit.* import org.junit.Test import java.util.concurrent.* -import java.util.concurrent.CancellationException import kotlin.test.* class MaybeTest : TestBase() { @@ -47,7 +46,7 @@ class MaybeTest : TestBase() { null } expect(2) - maybe.subscribe (emptyConsumer(), ON_ERROR_MISSING, Action { + maybe.subscribe (emptyConsumer(), ON_ERROR_MISSING, { expect(5) }) expect(3) @@ -112,18 +111,45 @@ class MaybeTest : TestBase() { @Test fun testMaybeAwait() = runBlocking { - assertEquals("OK", Maybe.just("O").await() + "K") + assertEquals("OK", Maybe.just("O").awaitSingleOrNull() + "K") + assertEquals("OK", Maybe.just("O").awaitSingle() + "K") } @Test - fun testMaybeAwaitForNull() = runBlocking { - assertNull(Maybe.empty().await()) + fun testMaybeAwaitForNull(): Unit = runBlocking { + assertNull(Maybe.empty().awaitSingleOrNull()) + assertFailsWith { Maybe.empty().awaitSingle() } + } + + /** Tests that calls to [awaitSingleOrNull] throw [CancellationException] and dispose of the subscription when their + * [Job] is cancelled. */ + @Test + fun testMaybeAwaitCancellation() = runTest { + expect(1) + val maybe = MaybeSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + maybe.awaitSingleOrNull() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) } @Test fun testMaybeEmitAndAwait() { val maybe = rxMaybe { - Maybe.just("O").await() + "K" + Maybe.just("O").awaitSingleOrNull() + "K" } checkMaybeValue(maybe) { @@ -205,7 +231,7 @@ class MaybeTest : TestBase() { @Test fun testCancelledConsumer() = runTest { expect(1) - val maybe = rxMaybe(currentDispatcher()) { + val maybe = rxMaybe(currentDispatcher()) { expect(4) try { delay(Long.MAX_VALUE) @@ -241,7 +267,7 @@ class MaybeTest : TestBase() { } } try { - maybe.await() + maybe.awaitSingleOrNull() expectUnreached() } catch (e: TestException) { assertTrue(e.suppressed[0] is TestException2) @@ -301,7 +327,7 @@ class MaybeTest : TestBase() { rxMaybe(Dispatchers.Unconfined) { expect(1) 42 - }.subscribe({ throw LinkageError() }) + }.subscribe { throw LinkageError() } finish(3) } } diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt index 4454190f8f..e246407a17 100644 --- a/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableSingleTest.kt @@ -5,7 +5,9 @@ package kotlinx.coroutines.rx2 import io.reactivex.* +import io.reactivex.disposables.* import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import org.junit.* import org.junit.Test import java.util.concurrent.* @@ -101,7 +103,7 @@ class ObservableSingleTest : TestBase() { @Test fun testAwaitFirstOrNull() { - val observable = rxObservable { + val observable = rxObservable { send(Observable.empty().awaitFirstOrNull() ?: "OK") } @@ -154,6 +156,32 @@ class ObservableSingleTest : TestBase() { } } + /** Tests that calls to [awaitFirst] (and, thus, the other methods) throw [CancellationException] and dispose of + * the subscription when their [Job] is cancelled. */ + @Test + fun testAwaitCancellation() = runTest { + expect(1) + val observable = ObservableSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + observable.awaitFirst() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) + } + + @Test fun testExceptionFromObservable() { val observable = rxObservable { diff --git a/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt b/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt index c66188a183..b359d963b8 100644 --- a/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/SingleTest.kt @@ -9,6 +9,7 @@ import io.reactivex.disposables.* import io.reactivex.exceptions.* import io.reactivex.functions.* import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import org.junit.* import org.junit.Test import java.util.concurrent.* @@ -98,6 +99,31 @@ class SingleTest : TestBase() { assertEquals("OK", Single.just("O").await() + "K") } + /** Tests that calls to [await] throw [CancellationException] and dispose of the subscription when their + * [Job] is cancelled. */ + @Test + fun testSingleAwaitCancellation() = runTest { + expect(1) + val single = SingleSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + single.await() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) + } + @Test fun testSingleEmitAndAwait() { val single = rxSingle { @@ -221,7 +247,7 @@ class SingleTest : TestBase() { fun testFatalExceptionInSingle() = runTest { rxSingle(Dispatchers.Unconfined) { throw LinkageError() - }.subscribe({ _, e -> assertTrue(e is LinkageError); expect(1) }) + }.subscribe { _, e -> assertTrue(e is LinkageError); expect(1) } finish(2) } diff --git a/reactive/kotlinx-coroutines-rx3/api/kotlinx-coroutines-rx3.api b/reactive/kotlinx-coroutines-rx3/api/kotlinx-coroutines-rx3.api index 6d2dd63d2c..f6f3f1d06d 100644 --- a/reactive/kotlinx-coroutines-rx3/api/kotlinx-coroutines-rx3.api +++ b/reactive/kotlinx-coroutines-rx3/api/kotlinx-coroutines-rx3.api @@ -8,7 +8,9 @@ public final class kotlinx/coroutines/rx3/RxAwaitKt { public static final fun awaitFirstOrNull (Lio/reactivex/rxjava3/core/ObservableSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun awaitLast (Lio/reactivex/rxjava3/core/ObservableSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun awaitOrDefault (Lio/reactivex/rxjava3/core/MaybeSource;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitSingle (Lio/reactivex/rxjava3/core/MaybeSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun awaitSingle (Lio/reactivex/rxjava3/core/ObservableSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitSingleOrNull (Lio/reactivex/rxjava3/core/MaybeSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlinx/coroutines/rx3/RxChannelKt { diff --git a/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt b/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt index 8ac0a10ce7..a4435b88e2 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxAwait.kt @@ -15,11 +15,12 @@ import kotlin.coroutines.* // ------------------------ CompletableSource ------------------------ /** - * Awaits for completion of this completable without blocking a thread. - * Returns `Unit` or throws the corresponding exception if this completable had produced error. + * Awaits for completion of this completable without blocking the thread. + * Returns `Unit`, or throws the corresponding exception if this completable produces an error. * * This suspending function is cancellable. If the [Job] of the invoking coroutine is cancelled or completed while this - * suspending function is suspended, this function immediately resumes with [CancellationException]. + * suspending function is suspended, this function immediately resumes with [CancellationException] and disposes of its + * subscription. */ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine { cont -> subscribe(object : CompletableObserver { @@ -31,6 +32,37 @@ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine // ------------------------ MaybeSource ------------------------ +/** + * Awaits for completion of the [MaybeSource] without blocking the thread. + * Returns the resulting value, or `null` if no value is produced, or throws the corresponding exception if this + * [MaybeSource] produces an error. + * + * This suspending function is cancellable. + * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * function immediately resumes with [CancellationException] and disposes of its subscription. + */ +@Suppress("UNCHECKED_CAST") +public suspend fun MaybeSource.awaitSingleOrNull(): T? = suspendCancellableCoroutine { cont -> + subscribe(object : MaybeObserver { + override fun onSubscribe(d: Disposable) { cont.disposeOnCancellation(d) } + override fun onComplete() { cont.resume(null) } + override fun onSuccess(t: T) { cont.resume(t) } + override fun onError(error: Throwable) { cont.resumeWithException(error) } + }) +} + +/** + * Awaits for completion of the [MaybeSource] without blocking the thread. + * Returns the resulting value, or throws if either no value is produced or this [MaybeSource] produces an error. + * + * This suspending function is cancellable. + * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this + * function immediately resumes with [CancellationException] and disposes of its subscription. + * + * @throws NoSuchElementException if no elements were produced by this [MaybeSource]. + */ +public suspend fun MaybeSource.awaitSingle(): T = awaitSingleOrNull() ?: throw NoSuchElementException() + /** * Awaits for completion of the maybe without blocking a thread. * Returns the resulting value, null if no value was produced or throws the corresponding exception if this @@ -39,9 +71,18 @@ public suspend fun CompletableSource.await(): Unit = suspendCancellableCoroutine * This suspending function is cancellable. * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function * immediately resumes with [CancellationException]. + * + * ### Deprecation + * + * Deprecated in favor of [awaitSingleOrNull] in order to reflect that `null` can be returned to denote the absence of + * a value, as opposed to throwing in such case. */ -@Suppress("UNCHECKED_CAST") -public suspend fun MaybeSource.await(): T? = (this as MaybeSource).awaitOrDefault(null) +@Deprecated( + message = "Deprecated in favor of awaitSingleOrNull()", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull()") +) // Warning since 1.5, error in 1.6, hidden in 1.7 +public suspend fun MaybeSource.await(): T? = awaitSingleOrNull() /** * Awaits for completion of the maybe without blocking a thread. @@ -51,25 +92,28 @@ public suspend fun MaybeSource.await(): T? = (this as MaybeSource).aw * This suspending function is cancellable. * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function * immediately resumes with [CancellationException]. + * + * ### Deprecation + * + * Deprecated in favor of [awaitSingleOrNull] for naming consistency (see the deprecation of [MaybeSource.await] for + * details). */ -public suspend fun MaybeSource.awaitOrDefault(default: T): T = suspendCancellableCoroutine { cont -> - subscribe(object : MaybeObserver { - override fun onSubscribe(d: Disposable) { cont.disposeOnCancellation(d) } - override fun onComplete() { cont.resume(default) } - override fun onSuccess(t: T) { cont.resume(t) } - override fun onError(error: Throwable) { cont.resumeWithException(error) } - }) -} +@Deprecated( + message = "Deprecated in favor of awaitSingleOrNull()", + level = DeprecationLevel.WARNING, + replaceWith = ReplaceWith("this.awaitSingleOrNull() ?: default") +) // Warning since 1.5, error in 1.6, hidden in 1.7 +public suspend fun MaybeSource.awaitOrDefault(default: T): T = awaitSingleOrNull() ?: default // ------------------------ SingleSource ------------------------ /** - * Awaits for completion of the single value without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this single had produced error. + * Awaits for completion of the single value response without blocking the thread. + * Returns the resulting value, or throws the corresponding exception if this response produces an error. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun SingleSource.await(): T = suspendCancellableCoroutine { cont -> subscribe(object : SingleObserver { @@ -82,69 +126,73 @@ public suspend fun SingleSource.await(): T = suspendCancellableCoroutine // ------------------------ ObservableSource ------------------------ /** - * Awaits for the first value from the given observable without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable] without blocking the thread and returns the resulting value, or, + * if the observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. * - * @throws NoSuchElementException if observable does not emit any value + * @throws NoSuchElementException if the observable does not emit any value */ public suspend fun ObservableSource.awaitFirst(): T = awaitOne(Mode.FIRST) /** - * Awaits for the first value from the given observable or the [default] value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable], or returns the [default] value if none is emitted, without + * blocking the thread, and returns the resulting value, or, if this observable has produced an error, throws the + * corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrDefault(default: T): T = awaitOne(Mode.FIRST_OR_DEFAULT, default) /** - * Awaits for the first value from the given observable or `null` value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable], or returns `null` if none is emitted, without blocking the + * thread, and returns the resulting value, or, if this observable has produced an error, throws the corresponding + * exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ public suspend fun ObservableSource.awaitFirstOrNull(): T? = awaitOne(Mode.FIRST_OR_DEFAULT) /** - * Awaits for the first value from the given observable or call [defaultValue] to get a value if none is emitted without blocking a - * thread and returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the first value from the given [Observable], or calls [defaultValue] to get a value if none is emitted, + * without blocking the thread, and returns the resulting value, or, if this observable has produced an error, throws + * the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. */ -public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: () -> T): T = awaitOne(Mode.FIRST_OR_DEFAULT) ?: defaultValue() +public suspend fun ObservableSource.awaitFirstOrElse(defaultValue: () -> T): T = + awaitOne(Mode.FIRST_OR_DEFAULT) ?: defaultValue() /** - * Awaits for the last value from the given observable without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the last value from the given [Observable] without blocking the thread and + * returns the resulting value, or, if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. * - * @throws NoSuchElementException if observable does not emit any value + * @throws NoSuchElementException if the observable does not emit any value */ public suspend fun ObservableSource.awaitLast(): T = awaitOne(Mode.LAST) /** - * Awaits for the single value from the given observable without blocking a thread. - * Returns the resulting value or throws the corresponding exception if this observable had produced error. + * Awaits the single value from the given observable without blocking the thread and returns the resulting value, or, + * if this observable has produced an error, throws the corresponding exception. * * This suspending function is cancellable. - * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function - * immediately resumes with [CancellationException]. + * If the [Job] of the current coroutine is cancelled or completed while the suspending function is waiting, this + * function immediately disposes of its subscription and resumes with [CancellationException]. * - * @throws NoSuchElementException if observable does not emit any value - * @throws IllegalArgumentException if observable emits more than one value + * @throws NoSuchElementException if the observable does not emit any value + * @throws IllegalArgumentException if the observable emits more than one value */ public suspend fun ObservableSource.awaitSingle(): T = awaitOne(Mode.SINGLE) @@ -218,3 +266,4 @@ private suspend fun ObservableSource.awaitOne( } }) } + diff --git a/reactive/kotlinx-coroutines-rx3/test/CompletableTest.kt b/reactive/kotlinx-coroutines-rx3/test/CompletableTest.kt index e5399d16d1..cfdb6d4170 100644 --- a/reactive/kotlinx-coroutines-rx3/test/CompletableTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/CompletableTest.kt @@ -98,6 +98,31 @@ class CompletableTest : TestBase() { } } + /** Tests that calls to [await] throw [CancellationException] and dispose of the subscription when their [Job] is + * cancelled. */ + @Test + fun testAwaitCancellation() = runTest { + expect(1) + val completable = CompletableSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + completable.await() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) + } + @Test fun testSuppressedException() = runTest { val completable = rxCompletable(currentDispatcher()) { @@ -119,7 +144,7 @@ class CompletableTest : TestBase() { } @Test - fun testUnhandledException() = runTest() { + fun testUnhandledException() = runTest { expect(1) var disposable: Disposable? = null val handler = { e: Throwable -> @@ -165,8 +190,7 @@ class CompletableTest : TestBase() { withExceptionHandler(handler) { rxCompletable(Dispatchers.Unconfined) { expect(1) - 42 - }.subscribe({ throw LinkageError() }) + }.subscribe { throw LinkageError() } finish(3) } } diff --git a/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt b/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt index e0cec748f8..bdb5481d80 100644 --- a/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt @@ -7,13 +7,12 @@ package kotlinx.coroutines.rx3 import io.reactivex.rxjava3.core.* import io.reactivex.rxjava3.disposables.* import io.reactivex.rxjava3.exceptions.* -import io.reactivex.rxjava3.functions.* import io.reactivex.rxjava3.internal.functions.Functions.* import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import org.junit.* import org.junit.Test import java.util.concurrent.* -import java.util.concurrent.CancellationException import kotlin.test.* class MaybeTest : TestBase() { @@ -47,7 +46,7 @@ class MaybeTest : TestBase() { null } expect(2) - maybe.subscribe (emptyConsumer(), ON_ERROR_MISSING, Action { + maybe.subscribe (emptyConsumer(), ON_ERROR_MISSING, { expect(5) }) expect(3) @@ -112,18 +111,45 @@ class MaybeTest : TestBase() { @Test fun testMaybeAwait() = runBlocking { - assertEquals("OK", Maybe.just("O").await() + "K") + assertEquals("OK", Maybe.just("O").awaitSingleOrNull() + "K") + assertEquals("OK", Maybe.just("O").awaitSingle() + "K") } @Test - fun testMaybeAwaitForNull() = runBlocking { - assertNull(Maybe.empty().await()) + fun testMaybeAwaitForNull(): Unit = runBlocking { + assertNull(Maybe.empty().awaitSingleOrNull()) + assertFailsWith { Maybe.empty().awaitSingle() } + } + + /** Tests that calls to [awaitSingleOrNull] throw [CancellationException] and dispose of the subscription when their + * [Job] is cancelled. */ + @Test + fun testMaybeAwaitCancellation() = runTest { + expect(1) + val maybe = MaybeSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + maybe.awaitSingleOrNull() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) } @Test fun testMaybeEmitAndAwait() { val maybe = rxMaybe { - Maybe.just("O").await() + "K" + Maybe.just("O").awaitSingleOrNull() + "K" } checkMaybeValue(maybe) { @@ -205,7 +231,7 @@ class MaybeTest : TestBase() { @Test fun testCancelledConsumer() = runTest { expect(1) - val maybe = rxMaybe(currentDispatcher()) { + val maybe = rxMaybe(currentDispatcher()) { expect(4) try { delay(Long.MAX_VALUE) @@ -241,7 +267,7 @@ class MaybeTest : TestBase() { } } try { - maybe.await() + maybe.awaitSingleOrNull() expectUnreached() } catch (e: TestException) { assertTrue(e.suppressed[0] is TestException2) @@ -301,7 +327,7 @@ class MaybeTest : TestBase() { rxMaybe(Dispatchers.Unconfined) { expect(1) 42 - }.subscribe({ throw LinkageError() }) + }.subscribe { throw LinkageError() } finish(3) } } diff --git a/reactive/kotlinx-coroutines-rx3/test/ObservableSingleTest.kt b/reactive/kotlinx-coroutines-rx3/test/ObservableSingleTest.kt index 2a3ce04638..692f014418 100644 --- a/reactive/kotlinx-coroutines-rx3/test/ObservableSingleTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/ObservableSingleTest.kt @@ -5,7 +5,9 @@ package kotlinx.coroutines.rx3 import io.reactivex.rxjava3.core.* +import io.reactivex.rxjava3.disposables.* import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import org.junit.* import org.junit.Test import java.util.concurrent.* @@ -101,7 +103,7 @@ class ObservableSingleTest : TestBase() { @Test fun testAwaitFirstOrNull() { - val observable = rxObservable { + val observable = rxObservable { send(Observable.empty().awaitFirstOrNull() ?: "OK") } @@ -154,6 +156,32 @@ class ObservableSingleTest : TestBase() { } } + /** Tests that calls to [awaitFirst] (and, thus, the other methods) throw [CancellationException] and dispose of + * the subscription when their [Job] is cancelled. */ + @Test + fun testAwaitCancellation() = runTest { + expect(1) + val observable = ObservableSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + observable.awaitFirst() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) + } + + @Test fun testExceptionFromObservable() { val observable = rxObservable { diff --git a/reactive/kotlinx-coroutines-rx3/test/SingleTest.kt b/reactive/kotlinx-coroutines-rx3/test/SingleTest.kt index 46bcaf84dc..3e763aa58c 100644 --- a/reactive/kotlinx-coroutines-rx3/test/SingleTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/SingleTest.kt @@ -9,6 +9,7 @@ import io.reactivex.rxjava3.disposables.* import io.reactivex.rxjava3.exceptions.* import io.reactivex.rxjava3.functions.* import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException import org.junit.* import org.junit.Test import java.util.concurrent.* @@ -98,6 +99,31 @@ class SingleTest : TestBase() { assertEquals("OK", Single.just("O").await() + "K") } + /** Tests that calls to [await] throw [CancellationException] and dispose of the subscription when their + * [Job] is cancelled. */ + @Test + fun testSingleAwaitCancellation() = runTest { + expect(1) + val single = SingleSource { s -> + s.onSubscribe(object: Disposable { + override fun dispose() { expect(4) } + override fun isDisposed(): Boolean { expectUnreached(); return false } + }) + } + val job = launch(start = CoroutineStart.UNDISPATCHED) { + try { + expect(2) + single.await() + } catch (e: CancellationException) { + expect(5) + throw e + } + } + expect(3) + job.cancelAndJoin() + finish(6) + } + @Test fun testSingleEmitAndAwait() { val single = rxSingle { @@ -221,7 +247,7 @@ class SingleTest : TestBase() { fun testFatalExceptionInSingle() = runTest { rxSingle(Dispatchers.Unconfined) { throw LinkageError() - }.subscribe({ _, e -> assertTrue(e is LinkageError); expect(1) }) + }.subscribe { _, e -> assertTrue(e is LinkageError); expect(1) } finish(2) } From e49cf79679ef742c08588ec572f55289f4474f46 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 21 Apr 2021 14:34:19 +0300 Subject: [PATCH 43/55] Mention benign data-race in getResult (#2663) Addresses #2660 --- .../common/src/CancellableContinuationImpl.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt index c310623c5d..f74155eb8f 100644 --- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt +++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt @@ -260,9 +260,13 @@ internal open class CancellableContinuationImpl( // or we got async cancellation from parent. if (trySuspend()) { /* + * Invariant: parentHandle is `null` *only* for reusable continuations. * We were neither resumed nor cancelled, time to suspend. * But first we have to install parent cancellation handle (if we didn't yet), * so CC could be properly resumed on parent cancellation. + * + * This read has benign data-race with write of 'NonDisposableHandle' + * in 'detachChildIfNotReusable'. */ if (parentHandle == null) { installParentHandle() From b400b027c90675f19b03c08d937b51e518220ca5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 21 Apr 2021 17:42:23 +0300 Subject: [PATCH 44/55] Add debugger-related manifest to root metadata artifact of kotlinx-coroutines-core (#2632) * It fixes IDEA issue that was introduced in coroutines 1.4.3 where we switched to Kotlin 1.4.30 and stopped producing root artifact with JVM classes * It simplifies manual -javaagent specification for end-users who shouldn't even be aware of kotlinx-coroutines-core-jvm.jar Fixes #2619 --- kotlinx-coroutines-core/build.gradle | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/kotlinx-coroutines-core/build.gradle b/kotlinx-coroutines-core/build.gradle index c2a57f9d9e..4ea81d3de3 100644 --- a/kotlinx-coroutines-core/build.gradle +++ b/kotlinx-coroutines-core/build.gradle @@ -208,8 +208,20 @@ jvmTest { } } -jvmJar { - manifest { +// Setup manifest for kotlinx-coroutines-core-jvm.jar +jvmJar { setupManifest(it) } + +/* + * Setup manifest for kotlinx-coroutines-core.jar + * This is convenient for users that pass -javaagent arg manually and also is a workaround #2619 and KTIJ-5659. + * This manifest contains reference to AgentPremain that belongs to + * kotlinx-coroutines-core-jvm, but our resolving machinery guarantees that + * any JVM project that depends on -core artifact also depends on -core-jvm one. + */ +metadataJar { setupManifest(it) } + +static def setupManifest(Jar jar) { + jar.manifest { attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain" attributes "Can-Retransform-Classes": "true" } From 5cd57e7287913dfd32ab0d803f53abbfb56bffcb Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 21 Apr 2021 17:42:44 +0300 Subject: [PATCH 45/55] Add ChannelResult.onClosed (#2665) * Establish clear contract on ChannelResult.isClosed * This method provides a **clear** migration from correct 'offer' usages to 'trySend' --- .../api/kotlinx-coroutines-core.api | 1 + .../common/src/channels/Channel.kt | 36 +++++++++++++++++-- .../test/channels/BasicOperationsTest.kt | 22 +++++++++++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index fdb70200f4..fa5924fda6 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -635,6 +635,7 @@ public final class kotlinx/coroutines/channels/ChannelKt { public static synthetic fun Channel$default (IILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel; public static synthetic fun Channel$default (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel; public static final fun getOrElse-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static final fun onClosed-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun onFailure-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun onSuccess-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 1812bddfa7..a0dbc6b9b4 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -144,13 +144,22 @@ public interface SendChannel { * oversee such error during code review. * * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead. * + * **NB** Automatic migration provides best-effort for the user experience, but requires removal + * or adjusting of the code that relied on the exception handling. + * The complete replacement has a more verbose form: + * ``` + * channel.trySend(element) + * .onClosed { throw it ?: ClosedSendChannelException("Channel was closed normally") } + * .isSuccess + * ``` + * * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context. */ @Deprecated( level = DeprecationLevel.WARNING, message = "Deprecated in the favour of 'trySend' method", replaceWith = ReplaceWith("trySend(element).isSuccess") - ) // Since 1.5.0 + ) // Warning since 1.5.0 public fun offer(element: E): Boolean { val result = trySend(element) if (result.isSuccess) return true @@ -297,7 +306,7 @@ public interface ReceiveChannel { @Deprecated(level = DeprecationLevel.WARNING, message = "Deprecated in the favour of 'tryReceive'", replaceWith = ReplaceWith("tryReceive().getOrNull()") - ) // Since 1.5.0 + ) // Warning since 1.5.0 public fun poll(): E? { val result = tryReceive() if (result.isSuccess) return result.getOrThrow() @@ -362,6 +371,8 @@ public interface ReceiveChannel { * E.g. when the channel is full, [Channel.trySend] returns failed result, but the channel itself is not in the failed state. * * The closed result represents an operation attempt to a closed channel and also implies that the operation has failed. + * It is guaranteed that if the result is _closed_, then the target channel is either [closed for send][Channel.isClosedForSend] + * or is [closed for receive][Channel.isClosedForReceive] depending on whether the failed operation was sending or receiving. */ @JvmInline public value class ChannelResult @@ -399,12 +410,14 @@ public value class ChannelResult /** * Returns the encapsulated value if this instance represents success or `null` if it represents failed result. */ + @Suppress("UNCHECKED_CAST") public fun getOrNull(): T? = if (holder !is Failed) holder as T else null /** * Returns the encapsulated value if this instance represents success or throws an exception if it is closed or failed. */ public fun getOrThrow(): T { + @Suppress("UNCHECKED_CAST") if (holder !is Failed) return holder as T if (holder is Closed && holder.cause != null) throw holder.cause error("Trying to call 'getOrThrow' on a failed channel result: $holder") @@ -495,6 +508,25 @@ public inline fun ChannelResult.onFailure(action: (exception: Throwable?) return this } +/** + * Performs the given [action] on the encapsulated [Throwable] exception if this instance represents [failure][ChannelResult.isFailure] + * due to channel being [closed][Channel.close]. + * The result of [ChannelResult.exceptionOrNull] is passed to the [action] parameter. + * It is guaranteed that if action is invoked, then the channel is either [closed for send][Channel.isClosedForSend] + * or is [closed for receive][Channel.isClosedForReceive] depending on the failed operation. + * + * Returns the original `ChannelResult` unchanged. + */ +@OptIn(ExperimentalContracts::class) +public inline fun ChannelResult.onClosed(action: (exception: Throwable?) -> Unit): ChannelResult { + contract { + callsInPlace(action, InvocationKind.AT_MOST_ONCE) + } + @Suppress("UNCHECKED_CAST") + if (holder is ChannelResult.Closed) action(exceptionOrNull()) + return this +} + /** * Iterator for [ReceiveChannel]. Instances of this interface are *not thread-safe* and shall not be used * from concurrent coroutines. diff --git a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt index 8962acc3dc..4538f6c680 100644 --- a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt @@ -14,6 +14,11 @@ class BasicOperationsTest : TestBase() { TestChannelKind.values().forEach { kind -> testSendReceive(kind, 20) } } + @Test + fun testTrySendToFullChannel() = runTest { + TestChannelKind.values().forEach { kind -> testTrySendToFullChannel(kind) } + } + @Test fun testTrySendAfterClose() = runTest { TestChannelKind.values().forEach { kind -> testTrySend(kind) } @@ -118,7 +123,7 @@ class BasicOperationsTest : TestBase() { assertTrue(channel.isClosedForSend) channel.trySend(2) .onSuccess { expectUnreached() } - .onFailure { + .onClosed { assertTrue { it is ClosedSendChannelException} if (!kind.isConflated) { assertEquals(42, channel.receive()) @@ -127,6 +132,21 @@ class BasicOperationsTest : TestBase() { d.await() } + private suspend fun testTrySendToFullChannel(kind: TestChannelKind) = coroutineScope { + if (kind.isConflated || kind.capacity == Int.MAX_VALUE) return@coroutineScope + val channel = kind.create() + // Make it full + repeat(11) { + channel.trySend(42) + } + channel.trySend(1) + .onSuccess { expectUnreached() } + .onFailure { assertNull(it) } + .onClosed { + expectUnreached() + } + } + /** * [ClosedSendChannelException] should not be eaten. * See [https://github.com/Kotlin/kotlinx.coroutines/issues/957] From 40f79ff8b96015e0d0365b69fe686d6558db4a28 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 21 Apr 2021 17:44:06 +0300 Subject: [PATCH 46/55] Introduce Flow.last and Flow.lastOrNull operators (#2662) Fixes #2246 --- .../api/kotlinx-coroutines-core.api | 2 + .../common/src/flow/terminal/Reduce.kt | 25 +++++++++++ .../common/test/flow/terminal/LastTest.kt | 45 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 kotlinx-coroutines-core/common/test/flow/terminal/LastTest.kt diff --git a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api index fa5924fda6..ec161ce28e 100644 --- a/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api +++ b/kotlinx-coroutines-core/api/kotlinx-coroutines-core.api @@ -965,6 +965,8 @@ public final class kotlinx/coroutines/flow/FlowKt { public static final fun fold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun forEach (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)V public static final fun getDEFAULT_CONCURRENCY ()I + public static final fun last (Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun lastOrNull (Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun launchIn (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/Job; public static final fun map (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow; public static final fun mapLatest (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow; diff --git a/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt b/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt index a937adcc8a..1794c9f41c 100644 --- a/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt +++ b/kotlinx-coroutines-core/common/src/flow/terminal/Reduce.kt @@ -144,3 +144,28 @@ public suspend fun Flow.firstOrNull(predicate: suspend (T) -> Boolean): T } return result } + +/** + * The terminal operator that returns the last element emitted by the flow. + * + * Throws [NoSuchElementException] if the flow was empty. + */ +public suspend fun Flow.last(): T { + var result: Any? = NULL + collect { + result = it + } + if (result === NULL) throw NoSuchElementException("Expected at least one element") + return result as T +} + +/** + * The terminal operator that returns the last element emitted by the flow or `null` if the flow was empty. + */ +public suspend fun Flow.lastOrNull(): T? { + var result: T? = null + collect { + result = it + } + return result +} diff --git a/kotlinx-coroutines-core/common/test/flow/terminal/LastTest.kt b/kotlinx-coroutines-core/common/test/flow/terminal/LastTest.kt new file mode 100644 index 0000000000..e7699ccc65 --- /dev/null +++ b/kotlinx-coroutines-core/common/test/flow/terminal/LastTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.flow + +import kotlinx.coroutines.* +import kotlin.test.* + +class LastTest : TestBase() { + @Test + fun testLast() = runTest { + val flow = flowOf(1, 2, 3) + assertEquals(3, flow.last()) + assertEquals(3, flow.lastOrNull()) + } + + @Test + fun testNulls() = runTest { + val flow = flowOf(1, null) + assertNull(flow.last()) + assertNull(flow.lastOrNull()) + } + + @Test + fun testNullsLastOrNull() = runTest { + val flow = flowOf(null, 1) + assertEquals(1, flow.lastOrNull()) + } + + @Test + fun testEmptyFlow() = runTest { + assertFailsWith { emptyFlow().last() } + assertNull(emptyFlow().lastOrNull()) + } + + @Test + fun testBadClass() = runTest { + val instance = BadClass() + val flow = flowOf(instance) + assertSame(instance, flow.last()) + assertSame(instance, flow.lastOrNull()) + + } +} From 99b475829f97fc98d81fbbec9ed9788cc05a0031 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 21 Apr 2021 18:17:35 +0300 Subject: [PATCH 47/55] Test invariant that IDEA evaluator depends on (#2525) Fixes #2522 --- ...jIdeaDebuggerEvaluatorCompatibilityTest.kt | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 kotlinx-coroutines-core/jvm/test/IntellijIdeaDebuggerEvaluatorCompatibilityTest.kt diff --git a/kotlinx-coroutines-core/jvm/test/IntellijIdeaDebuggerEvaluatorCompatibilityTest.kt b/kotlinx-coroutines-core/jvm/test/IntellijIdeaDebuggerEvaluatorCompatibilityTest.kt new file mode 100644 index 0000000000..6bbfdd1b4b --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/IntellijIdeaDebuggerEvaluatorCompatibilityTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines + +import org.junit.Test +import kotlin.coroutines.* +import kotlin.test.* + +class IntellijIdeaDebuggerEvaluatorCompatibilityTest { + + /* + * This test verifies that our CoroutineScope is accessible to IDEA debugger. + * + * Consider the following scenario: + * ``` + * runBlocking { // this: CoroutineScope + * println("runBlocking") + * } + * ``` + * user puts breakpoint to `println` line, opens "Evaluate" window + * and executes `launch { println("launch") }`. They (obviously) expect it to work, but + * it won't: `{}` in `runBlocking` is `SuspendLambda` and `this` is an unused implicit receiver + * that is removed by the compiler (because it's unused). + * + * But we still want to provide consistent user experience for functions with `CoroutineScope` receiver, + * for that IDEA debugger tries to retrieve the scope via `kotlin.coroutines.coroutineContext[Job] as? CoroutineScope` + * and with this test we're fixing this behaviour. + * + * Note that this behaviour is not carved in stone: IDEA fallbacks to `kotlin.coroutines.coroutineContext` for the context if necessary. + */ + + @Test + fun testScopeIsAccessible() = runBlocking { + verify() + + withContext(Job()) { + verify() + } + + coroutineScope { + verify() + } + + supervisorScope { + verify() + } + + } + + private suspend fun verify() { + val ctx = coroutineContext + assertTrue { ctx.job is CoroutineScope } + } +} From 44ebdef31eebc47544b447cfcecef0137575f0a1 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 21 Apr 2021 19:01:28 +0300 Subject: [PATCH 48/55] Use cancellation exception from standard library in Kotlin/JS and K/N (#2638) * On JVM it uses java.util.concurrent.CancellationException, same as stdlib declaration * Fully backwards compatible for JVM, Native-friendly for Obj-C interop --- .../common/test/flow/internal/FlowScopeTest.kt | 4 ++-- kotlinx-coroutines-core/js/src/Exceptions.kt | 7 +------ kotlinx-coroutines-core/native/src/Exceptions.kt | 7 +------ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/kotlinx-coroutines-core/common/test/flow/internal/FlowScopeTest.kt b/kotlinx-coroutines-core/common/test/flow/internal/FlowScopeTest.kt index d41ab8893f..e866647930 100644 --- a/kotlinx-coroutines-core/common/test/flow/internal/FlowScopeTest.kt +++ b/kotlinx-coroutines-core/common/test/flow/internal/FlowScopeTest.kt @@ -68,10 +68,10 @@ class FlowScopeTest : TestBase() { flowScope { flowScope { launch { - throw CancellationException(null) + throw CancellationException("") } } } } } -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/js/src/Exceptions.kt b/kotlinx-coroutines-core/js/src/Exceptions.kt index 7c76bc6d2c..da9979b603 100644 --- a/kotlinx-coroutines-core/js/src/Exceptions.kt +++ b/kotlinx-coroutines-core/js/src/Exceptions.kt @@ -10,12 +10,7 @@ package kotlinx.coroutines * **It is not printed to console/log by default uncaught exception handler**. * (see [CoroutineExceptionHandler]). */ -public actual open class CancellationException( - message: String?, - cause: Throwable? -) : IllegalStateException(message, cause) { - public actual constructor(message: String?) : this(message, null) -} +public actual typealias CancellationException = kotlin.coroutines.cancellation.CancellationException /** * Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled or completed diff --git a/kotlinx-coroutines-core/native/src/Exceptions.kt b/kotlinx-coroutines-core/native/src/Exceptions.kt index 7c76bc6d2c..da9979b603 100644 --- a/kotlinx-coroutines-core/native/src/Exceptions.kt +++ b/kotlinx-coroutines-core/native/src/Exceptions.kt @@ -10,12 +10,7 @@ package kotlinx.coroutines * **It is not printed to console/log by default uncaught exception handler**. * (see [CoroutineExceptionHandler]). */ -public actual open class CancellationException( - message: String?, - cause: Throwable? -) : IllegalStateException(message, cause) { - public actual constructor(message: String?) : this(message, null) -} +public actual typealias CancellationException = kotlin.coroutines.cancellation.CancellationException /** * Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled or completed From 347feedb9900a665db634dd09d929d15a1569c2d Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 22 Apr 2021 12:58:39 +0300 Subject: [PATCH 49/55] Remove dev repositories and Bintray usages where applicable (#2587) * Remove dev repositories and Bintray usages where applicable * Update our frontend example * Update lincheck * Temporary ignore MutexLincheckTest.modelCheckingTest --- README.md | 4 ---- build.gradle | 17 +++-------------- buildSrc/build.gradle.kts | 1 + buildSrc/src/main/kotlin/Publishing.kt | 14 -------------- gradle.properties | 10 ++-------- .../{publish-bintray.gradle => publish.gradle} | 14 +++----------- js/example-frontend-js/build.gradle.kts | 5 +++++ .../jvm/test/AbstractLincheckTest.kt | 6 +++--- .../jvm/test/lincheck/MutexLincheckTest.kt | 9 +++++++-- 9 files changed, 24 insertions(+), 56 deletions(-) rename gradle/{publish-bintray.gradle => publish.gradle} (84%) diff --git a/README.md b/README.md index 08417dd0f5..a961e44876 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,6 @@ suspend fun main() = coroutineScope { ## Using in your projects -The libraries are published to [kotlinx](https://bintray.com/kotlin/kotlinx/kotlinx.coroutines) bintray repository, -linked to [JCenter](https://bintray.com/bintray/jcenter?filterByPkgName=kotlinx.coroutines) and -pushed to [Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3Aorg.jetbrains.kotlinx%20a%3Akotlinx-coroutines*). - ### Maven Add dependencies (you can also add other modules that you need): diff --git a/build.gradle b/build.gradle index 08d07782a7..fc52d8cc53 100644 --- a/build.gradle +++ b/build.gradle @@ -53,16 +53,8 @@ buildscript { } repositories { - maven {url "https://kotlin.bintray.com/kotlinx"} - // Future replacement for kotlin-dev, with cache redirector - maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } - maven { - url "https://kotlin.bintray.com/kotlin-dev" - credentials { - username = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') ?: "" - password = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') ?: "" - } - } + // Leftover until we migrated to Dokka 1.4.30 + maven { url "https://kotlin.bintray.com/kotlin-dev" } maven { url "https://jetbrains.bintray.com/kotlin-native-dependencies" } maven { url "https://plugins.gradle.org/m2/" } maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } @@ -151,9 +143,6 @@ allprojects { */ google() mavenCentral() - // Future replacement for kotlin-dev, with cache redirector - maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } - maven { url "https://kotlin.bintray.com/kotlinx" } } } @@ -244,7 +233,7 @@ configure(subprojects.findAll { !unpublished.contains(it.name) }) { if (it.name != 'kotlinx-coroutines-bom') { apply from: rootProject.file('gradle/dokka.gradle') } - apply from: rootProject.file('gradle/publish-bintray.gradle') + apply from: rootProject.file('gradle/publish.gradle') } configure(subprojects.findAll { !unpublished.contains(it.name) }) { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c763d424fd..80214522b8 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -17,6 +17,7 @@ repositories { maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") } else { maven("https://plugins.gradle.org/m2") + // Leftover until we migrated to Dokka 1.4.30 maven("https://dl.bintray.com/kotlin/kotlin-dev") } maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt index 5b191bfa38..e0124cd966 100644 --- a/buildSrc/src/main/kotlin/Publishing.kt +++ b/buildSrc/src/main/kotlin/Publishing.kt @@ -62,20 +62,6 @@ fun configureMavenPublication(rh: RepositoryHandler, project: Project) { } } -fun configureBintrayPublication(rh: RepositoryHandler, project: Project) { - rh.maven { - val user = "kotlin" - val repo = "kotlinx" - val name = "kotlinx.coroutines" - url = URI("https://api.bintray.com/maven/$user/$repo/$name/;publish=0;override=0") - - credentials { - username = project.findProperty("bintrayUser") as? String ?: System.getenv("BINTRAY_USER") - password = project.findProperty("bintrayApiKey") as? String ?: System.getenv("BINTRAY_API_KEY") - } - } -} - fun signPublicationIfKeyPresent(project: Project, publication: MavenPublication) { val keyId = project.getSensitiveProperty("libs.sign.key.id") val signingKey = project.getSensitiveProperty("libs.sign.key.private") diff --git a/gradle.properties b/gradle.properties index a27edd12f4..97c6af986f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,8 +11,8 @@ kotlin_version=1.5.0-RC junit_version=4.12 atomicfu_version=0.15.2 knit_version=0.2.3 -html_version=0.6.8 -lincheck_version=2.10 +html_version=0.7.2 +lincheck_version=2.12 dokka_version=0.9.16-rdev-2-mpp-hacks byte_buddy_version=1.10.9 reactor_version=3.4.1 @@ -54,10 +54,4 @@ jekyll_version=4.0 # TODO: Remove once KT-37187 is fixed org.gradle.jvmargs=-Xmx2g -# Workaround for Bintray treating .sha512 files as artifacts -# https://github.com/gradle/gradle/issues/1.4.3 -systemProp.org.gradle.internal.publish.checksums.insecure=true - -# todo:KLUDGE: This is commented out, and the property is set conditionally in build.gradle, because IDEA doesn't work with it. -#kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.enableCompatibilityMetadataVariant=true diff --git a/gradle/publish-bintray.gradle b/gradle/publish.gradle similarity index 84% rename from gradle/publish-bintray.gradle rename to gradle/publish.gradle index e6f0423451..3a0a4224ab 100644 --- a/gradle/publish-bintray.gradle +++ b/gradle/publish.gradle @@ -4,7 +4,7 @@ import org.gradle.util.VersionNumber -// Configures publishing of Maven artifacts to Bintray +// Configures publishing of Maven artifacts to Maven Central apply plugin: 'maven' apply plugin: 'maven-publish' @@ -37,12 +37,7 @@ if (!isMultiplatform && !isBom) { publishing { repositories { - def bintrayUpload = System.getenv("libs.bintray.upload") != null - if (bintrayUpload) { - PublishingKt.configureBintrayPublication(delegate, project) - } else { - PublishingKt.configureMavenPublication(delegate, project) - } + PublishingKt.configureMavenPublication(delegate, project) } if (!isMultiplatform && !isBom) { @@ -61,10 +56,7 @@ publishing { publications.all { PublishingKt.configureMavenCentralMetadata(pom, project) - def bintrayUpload = System.getenv("libs.bintray.upload") != null - if (!bintrayUpload) { - PublishingKt.signPublicationIfKeyPresent(project, it) - } + PublishingKt.signPublicationIfKeyPresent(project, it) // add empty javadocs if (!isBom && it.name != "kotlinMultiplatform") { it.artifact(javadocJar) diff --git a/js/example-frontend-js/build.gradle.kts b/js/example-frontend-js/build.gradle.kts index e5e7a3e2a5..a78ac3dc6d 100644 --- a/js/example-frontend-js/build.gradle.kts +++ b/js/example-frontend-js/build.gradle.kts @@ -21,6 +21,11 @@ kotlin { } } +// For kotlinx-html +repositories { + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") +} + dependencies { implementation("org.jetbrains.kotlinx:kotlinx-html-js:${version("html")}") implementation(devNpm("html-webpack-plugin", "5.3.1")) diff --git a/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt index 5ba7acf994..2b4e91c02a 100644 --- a/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/AbstractLincheckTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.coroutines @@ -15,7 +15,7 @@ abstract class AbstractLincheckTest : VerifierState() { open fun StressOptions.customize(isStressTest: Boolean): StressOptions = this @Test - fun modelCheckingTest() = ModelCheckingOptions() + open fun modelCheckingTest() = ModelCheckingOptions() .iterations(if (isStressTest) 100 else 20) .invocationsPerIteration(if (isStressTest) 10_000 else 1_000) .commonConfiguration() @@ -38,4 +38,4 @@ abstract class AbstractLincheckTest : VerifierState() { .customize(isStressTest) override fun extractState(): Any = error("Not implemented") -} \ No newline at end of file +} diff --git a/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt b/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt index 6e350660ae..f7f59eef5e 100644 --- a/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt +++ b/kotlinx-coroutines-core/jvm/test/lincheck/MutexLincheckTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:Suppress("unused") package kotlinx.coroutines.lincheck @@ -9,10 +9,15 @@ import kotlinx.coroutines.sync.* import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.annotations.Operation import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.* +import org.junit.* class MutexLincheckTest : AbstractLincheckTest() { private val mutex = Mutex() + override fun modelCheckingTest() { + // Ignored via empty body as the only way + } + @Operation fun tryLock() = mutex.tryLock() @@ -29,4 +34,4 @@ class MutexLincheckTest : AbstractLincheckTest() { checkObstructionFreedom() override fun extractState() = mutex.isLocked -} \ No newline at end of file +} From 835ee18fb5229f49f31ca4b1a6bcf03141360202 Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Thu, 22 Apr 2021 14:39:51 +0300 Subject: [PATCH 50/55] Add a way to construct ReactorContext from ContextView (#2622) Fixes https://github.com/Kotlin/kotlinx.coroutines/issues/2575 --- .../api/kotlinx-coroutines-reactor.api | 4 +- .../kotlinx-coroutines-reactor/src/Convert.kt | 4 +- .../kotlinx-coroutines-reactor/src/Flux.kt | 46 +++++++++++++------ .../kotlinx-coroutines-reactor/src/Mono.kt | 2 +- .../src/ReactorContext.kt | 40 ++++++++++++---- 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api b/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api index 0a10aa12a9..b69bb334d7 100644 --- a/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api +++ b/reactive/kotlinx-coroutines-reactor/api/kotlinx-coroutines-reactor.api @@ -33,6 +33,7 @@ public final class kotlinx/coroutines/reactor/MonoKt { public final class kotlinx/coroutines/reactor/ReactorContext : kotlin/coroutines/AbstractCoroutineContextElement { public static final field Key Lkotlinx/coroutines/reactor/ReactorContext$Key; public fun (Lreactor/util/context/Context;)V + public fun (Lreactor/util/context/ContextView;)V public final fun getContext ()Lreactor/util/context/Context; } @@ -40,7 +41,8 @@ public final class kotlinx/coroutines/reactor/ReactorContext$Key : kotlin/corout } public final class kotlinx/coroutines/reactor/ReactorContextKt { - public static final fun asCoroutineContext (Lreactor/util/context/Context;)Lkotlinx/coroutines/reactor/ReactorContext; + public static final synthetic fun asCoroutineContext (Lreactor/util/context/Context;)Lkotlinx/coroutines/reactor/ReactorContext; + public static final fun asCoroutineContext (Lreactor/util/context/ContextView;)Lkotlinx/coroutines/reactor/ReactorContext; } public final class kotlinx/coroutines/reactor/ReactorFlowKt { diff --git a/reactive/kotlinx-coroutines-reactor/src/Convert.kt b/reactive/kotlinx-coroutines-reactor/src/Convert.kt index 3b08bd6639..002baa6185 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Convert.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Convert.kt @@ -41,8 +41,8 @@ public fun Deferred.asMono(context: CoroutineContext): Mono = mono(co /** * Converts a stream of elements received from the channel to the hot reactive flux. * - * Every subscriber receives values from this channel in **fan-out** fashion. If the are multiple subscribers, - * they'll receive values in round-robin way. + * Every subscriber receives values from this channel in a **fan-out** fashion. If the are multiple subscribers, + * they'll receive values in a round-robin way. * @param context -- the coroutine context from which the resulting flux is going to be signalled */ @Deprecated(message = "Deprecated in the favour of consumeAsFlow()", diff --git a/reactive/kotlinx-coroutines-reactor/src/Flux.kt b/reactive/kotlinx-coroutines-reactor/src/Flux.kt index 806f5bd5bc..df5f64f262 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Flux.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Flux.kt @@ -14,20 +14,20 @@ import reactor.util.context.* import kotlin.coroutines.* /** - * Creates cold reactive [Flux] that runs a given [block] in a coroutine. + * Creates a cold reactive [Flux] that runs the given [block] in a coroutine. * Every time the returned flux is subscribed, it starts a new coroutine in the specified [context]. - * Coroutine emits ([Subscriber.onNext]) values with `send`, completes ([Subscriber.onComplete]) - * when the coroutine completes or channel is explicitly closed and emits error ([Subscriber.onError]) - * if coroutine throws an exception or closes channel with a cause. - * Unsubscribing cancels running coroutine. + * The coroutine emits ([Subscriber.onNext]) values with [send][ProducerScope.send], completes ([Subscriber.onComplete]) + * when the coroutine completes, or, in case the coroutine throws an exception or the channel is closed, + * emits the error ([Subscriber.onError]) and closes the channel with the cause. + * Unsubscribing cancels the running coroutine. * - * Invocations of `send` are suspended appropriately when subscribers apply back-pressure and to ensure that - * `onNext` is not invoked concurrently. - * - * Method throws [IllegalArgumentException] if provided [context] contains a [Job] instance. + * Invocations of [send][ProducerScope.send] are suspended appropriately when subscribers apply back-pressure and to + * ensure that [onNext][Subscriber.onNext] is not invoked concurrently. * * **Note: This is an experimental api.** Behaviour of publishers that work as children in a parent scope with respect * to cancellation and error handling may change in the future. + * + * @throws IllegalArgumentException if the provided [context] contains a [Job] instance. */ @ExperimentalCoroutinesApi public fun flux( @@ -43,12 +43,13 @@ private fun reactorPublish( scope: CoroutineScope, context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit -): Publisher = Publisher { subscriber -> - // specification requires NPE on null subscriber - if (subscriber == null) throw NullPointerException("Subscriber cannot be null") - require(subscriber is CoreSubscriber) { "Subscriber is not an instance of CoreSubscriber, context can not be extracted." } +): Publisher = Publisher onSubscribe@{ subscriber: Subscriber? -> + if (subscriber !is CoreSubscriber) { + subscriber.reject(IllegalArgumentException("Subscriber is not an instance of CoreSubscriber, context can not be extracted.")) + return@onSubscribe + } val currentContext = subscriber.currentContext() - val reactorContext = (context[ReactorContext]?.context?.putAll(currentContext) ?: currentContext).asCoroutineContext() + val reactorContext = context.extendReactorContext(currentContext) val newContext = scope.newCoroutineContext(context + reactorContext) val coroutine = PublisherCoroutine(newContext, subscriber, REACTOR_HANDLER) subscriber.onSubscribe(coroutine) // do it first (before starting coroutine), to avoid unnecessary suspensions @@ -66,6 +67,23 @@ private val REACTOR_HANDLER: (Throwable, CoroutineContext) -> Unit = { cause, ct } } +/** The proper way to reject the subscriber, according to + * [the reactive spec](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#1.9) + */ +private fun Subscriber?.reject(t: Throwable) { + if (this == null) + throw NullPointerException("The subscriber can not be null") + onSubscribe(object: Subscription { + override fun request(n: Long) { + // intentionally left blank + } + override fun cancel() { + // intentionally left blank + } + }) + onError(t) +} + @Deprecated( message = "CoroutineScope.flux is deprecated in favour of top-level flux", level = DeprecationLevel.HIDDEN, diff --git a/reactive/kotlinx-coroutines-reactor/src/Mono.kt b/reactive/kotlinx-coroutines-reactor/src/Mono.kt index 6a4a38f379..67c1baa02d 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Mono.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Mono.kt @@ -83,7 +83,7 @@ private fun monoInternal( context: CoroutineContext, block: suspend CoroutineScope.() -> T? ): Mono = Mono.create { sink -> - val reactorContext = (context[ReactorContext]?.context?.putAll(sink.currentContext()) ?: sink.currentContext()).asCoroutineContext() + val reactorContext = context.extendReactorContext(sink.currentContext()) val newContext = scope.newCoroutineContext(context + reactorContext) val coroutine = MonoCoroutine(newContext, sink) sink.onDispose(coroutine) diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt index 333f056d97..8969662adc 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt @@ -5,19 +5,21 @@ package kotlinx.coroutines.reactor import kotlinx.coroutines.ExperimentalCoroutinesApi -import reactor.util.context.Context import kotlin.coroutines.* import kotlinx.coroutines.reactive.* +import reactor.util.context.* /** - * Wraps Reactor's [Context] into [CoroutineContext] element for seamless integration Reactor and kotlinx.coroutines. - * [Context.asCoroutineContext] is defined to add Reactor's [Context] elements as part of [CoroutineContext]. - * Coroutine context element that propagates information about Reactor's [Context] through coroutines. + * Wraps Reactor's [Context] into a [CoroutineContext] element for seamless integration between + * Reactor and kotlinx.coroutines. + * [Context.asCoroutineContext] puts Reactor's [Context] elements into a [CoroutineContext], + * which can be used to propagate the information about Reactor's [Context] through coroutines. * - * This context element is implicitly propagated through subscriber's context by all Reactive integrations, such as [mono], [flux], - * [Publisher.asFlow][asFlow], [Flow.asPublisher][asPublisher] and [Flow.asFlux][asFlux]. - * Functions that subscribe to the reactive stream (e.g. [Publisher.awaitFirst][kotlinx.coroutines.reactive.awaitFirst]) - * also propagate the [ReactorContext] to the subscriber's [Context]. + * This context element is implicitly propagated through subscribers' context by all Reactive integrations, + * such as [mono], [flux], [Publisher.asFlow][asFlow], [Flow.asPublisher][asPublisher] and [Flow.asFlux][asFlux]. + * Functions that subscribe to a reactive stream + * (e.g. [Publisher.awaitFirst][kotlinx.coroutines.reactive.awaitFirst]), too, propagate [ReactorContext] + * to the subscriber's [Context]. ** * ### Examples of Reactive context integration. * @@ -49,12 +51,30 @@ import kotlinx.coroutines.reactive.* */ @ExperimentalCoroutinesApi public class ReactorContext(public val context: Context) : AbstractCoroutineContextElement(ReactorContext) { + + // `Context.of` is zero-cost if the argument is a `Context` + public constructor(contextView: ContextView): this(Context.of(contextView)) + public companion object Key : CoroutineContext.Key } /** - * Wraps the given [Context] into [ReactorContext], so it can be added to coroutine's context + * Wraps the given [ContextView] into [ReactorContext], so it can be added to the coroutine's context + * and later used via `coroutineContext[ReactorContext]`. + */ +@ExperimentalCoroutinesApi +public fun ContextView.asCoroutineContext(): ReactorContext = ReactorContext(this) + +/** + * Wraps the given [Context] into [ReactorContext], so it can be added to the coroutine's context * and later used via `coroutineContext[ReactorContext]`. */ @ExperimentalCoroutinesApi -public fun Context.asCoroutineContext(): ReactorContext = ReactorContext(this) +@Deprecated("The more general version for ContextView should be used instead", level = DeprecationLevel.HIDDEN) +public fun Context.asCoroutineContext(): ReactorContext = readOnly().asCoroutineContext() // `readOnly()` is zero-cost. + +/** + * Updates the Reactor context in this [CoroutineContext], adding (or possibly replacing) some values. + */ +internal fun CoroutineContext.extendReactorContext(extensions: ContextView): CoroutineContext = + (this[ReactorContext]?.context?.putAll(extensions) ?: extensions).asCoroutineContext() \ No newline at end of file From 05d30189b4ff68fb702b711d31f46ade711f9abd Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Thu, 22 Apr 2021 14:41:48 +0300 Subject: [PATCH 51/55] Fixes for the reactive integrations (#2617) Reworked the comments, added new tests. Fixed a bug where `Maybe.collect()` would hang on success. --- .../kotlinx-coroutines-jdk9/src/Publish.kt | 29 ++-- .../src/ReactiveFlow.kt | 36 +++-- .../test/PublisherCollectTest.kt | 146 ++++++++++++++++++ .../src/Channel.kt | 14 +- .../src/Publish.kt | 21 +-- .../src/ReactiveFlow.kt | 20 +-- .../test/PublisherCollectTest.kt | 144 +++++++++++++++++ .../kotlinx-coroutines-rx2/src/RxChannel.kt | 12 +- .../kotlinx-coroutines-rx2/test/MaybeTest.kt | 50 ++++++ .../test/ObservableCollectTest.kt | 69 +++++++++ .../kotlinx-coroutines-rx3/src/RxChannel.kt | 12 +- .../kotlinx-coroutines-rx3/test/MaybeTest.kt | 52 ++++++- .../test/ObservableCollectTest.kt | 68 ++++++++ 13 files changed, 610 insertions(+), 63 deletions(-) create mode 100644 reactive/kotlinx-coroutines-jdk9/test/PublisherCollectTest.kt create mode 100644 reactive/kotlinx-coroutines-reactive/test/PublisherCollectTest.kt create mode 100644 reactive/kotlinx-coroutines-rx2/test/ObservableCollectTest.kt create mode 100644 reactive/kotlinx-coroutines-rx3/test/ObservableCollectTest.kt diff --git a/reactive/kotlinx-coroutines-jdk9/src/Publish.kt b/reactive/kotlinx-coroutines-jdk9/src/Publish.kt index 3db0d5da4c..cfd18d2b7a 100644 --- a/reactive/kotlinx-coroutines-jdk9/src/Publish.kt +++ b/reactive/kotlinx-coroutines-jdk9/src/Publish.kt @@ -6,33 +6,34 @@ package kotlinx.coroutines.jdk9 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* +import kotlinx.coroutines.reactive.* import java.util.concurrent.* import kotlin.coroutines.* import org.reactivestreams.FlowAdapters /** - * Creates cold reactive [Flow.Publisher] that runs a given [block] in a coroutine. + * Creates a cold reactive [Flow.Publisher] that runs a given [block] in a coroutine. + * * Every time the returned flux is subscribed, it starts a new coroutine in the specified [context]. - * Coroutine emits ([Subscriber.onNext]) values with `send`, completes ([Subscriber.onComplete]) - * when the coroutine completes or channel is explicitly closed and emits error ([Subscriber.onError]) - * if coroutine throws an exception or closes channel with a cause. - * Unsubscribing cancels running coroutine. + * The coroutine emits (via [Flow.Subscriber.onNext]) values with [send][ProducerScope.send], + * completes (via [Flow.Subscriber.onComplete]) when the coroutine completes or channel is explicitly closed, and emits + * errors (via [Flow.Subscriber.onError]) if the coroutine throws an exception or closes channel with a cause. + * Unsubscribing cancels the running coroutine. * - * Invocations of `send` are suspended appropriately when subscribers apply back-pressure and to ensure that - * `onNext` is not invoked concurrently. + * Invocations of [send][ProducerScope.send] are suspended appropriately when subscribers apply back-pressure and to + * ensure that [onNext][Flow.Subscriber.onNext] is not invoked concurrently. * * Coroutine context can be specified with [context] argument. - * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used. - * Method throws [IllegalArgumentException] if provided [context] contains a [Job] instance. + * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is + * used. * * **Note: This is an experimental api.** Behaviour of publishers that work as children in a parent scope with respect * to cancellation and error handling may change in the future. + * + * @throws IllegalArgumentException if the provided [context] contains a [Job] instance. */ -@ExperimentalCoroutinesApi // Since 1.3.x +@ExperimentalCoroutinesApi public fun flowPublish( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit -): Flow.Publisher { - val reactivePublisher : org.reactivestreams.Publisher = kotlinx.coroutines.reactive.publish(context, block) - return FlowAdapters.toFlowPublisher(reactivePublisher) -} +): Flow.Publisher = FlowAdapters.toFlowPublisher(publish(context, block)) diff --git a/reactive/kotlinx-coroutines-jdk9/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-jdk9/src/ReactiveFlow.kt index d3942a1629..6031e0a8ad 100644 --- a/reactive/kotlinx-coroutines-jdk9/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-jdk9/src/ReactiveFlow.kt @@ -7,41 +7,43 @@ package kotlinx.coroutines.jdk9 import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.reactive.asFlow -import kotlinx.coroutines.reactive.asPublisher +import kotlinx.coroutines.reactive.asPublisher as asReactivePublisher import kotlinx.coroutines.reactive.collect +import kotlinx.coroutines.channels.* import org.reactivestreams.* import kotlin.coroutines.* import java.util.concurrent.Flow as JFlow /** - * Transforms the given reactive [Publisher] into [Flow]. - * Use [buffer] operator on the resulting flow to specify the size of the backpressure. - * More precisely, it specifies the value of the subscription's [request][Subscription.request]. - * [buffer] default capacity is used by default. + * Transforms the given reactive [Flow Publisher][JFlow.Publisher] into [Flow]. + * Use the [buffer] operator on the resulting flow to specify the size of the back-pressure. + * In effect, it specifies the value of the subscription's [request][JFlow.Subscription.request]. + * The [default buffer capacity][Channel.BUFFERED] for a suspending channel is used by default. * - * If any of the resulting flow transformations fails, subscription is immediately cancelled and all in-flight elements - * are discarded. + * If any of the resulting flow transformations fails, the subscription is immediately cancelled and all the in-flight + * elements are discarded. */ public fun JFlow.Publisher.asFlow(): Flow = - FlowAdapters.toPublisher(this).asFlow() + FlowAdapters.toPublisher(this).asFlow() /** - * Transforms the given flow to a reactive specification compliant [Publisher]. + * Transforms the given flow into a reactive specification compliant [Flow Publisher][JFlow.Publisher]. * - * An optional [context] can be specified to control the execution context of calls to [Subscriber] methods. - * You can set a [CoroutineDispatcher] to confine them to a specific thread and/or various [ThreadContextElement] to + * An optional [context] can be specified to control the execution context of calls to the [Flow Subscriber][Subscriber] + * methods. + * A [CoroutineDispatcher] can be set to confine them to a specific thread; various [ThreadContextElement] can be set to * inject additional context into the caller thread. By default, the [Unconfined][Dispatchers.Unconfined] dispatcher * is used, so calls are performed from an arbitrary thread. */ @JvmOverloads // binary compatibility -public fun Flow.asPublisher(context: CoroutineContext = EmptyCoroutineContext): JFlow.Publisher { - val reactivePublisher : org.reactivestreams.Publisher = this.asPublisher(context) - return FlowAdapters.toFlowPublisher(reactivePublisher) -} +public fun Flow.asPublisher(context: CoroutineContext = EmptyCoroutineContext): JFlow.Publisher = + FlowAdapters.toFlowPublisher(asReactivePublisher(context)) /** - * Subscribes to this [Publisher] and performs the specified action for each received element. - * Cancels subscription if any exception happens during collect. + * Subscribes to this [Flow Publisher][JFlow.Publisher] and performs the specified action for each received element. + * + * If [action] throws an exception at some point, the subscription is cancelled, and the exception is rethrown from + * [collect]. Also, if the publisher signals an error, that error is rethrown from [collect]. */ public suspend inline fun JFlow.Publisher.collect(action: (T) -> Unit): Unit = FlowAdapters.toPublisher(this).collect(action) diff --git a/reactive/kotlinx-coroutines-jdk9/test/PublisherCollectTest.kt b/reactive/kotlinx-coroutines-jdk9/test/PublisherCollectTest.kt new file mode 100644 index 0000000000..c2e88483a7 --- /dev/null +++ b/reactive/kotlinx-coroutines-jdk9/test/PublisherCollectTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.jdk9 + +import kotlinx.coroutines.* +import kotlinx.coroutines.reactive.* +import org.junit.Test +import org.reactivestreams.* +import kotlin.test.* +import java.util.concurrent.Flow as JFlow + +class PublisherCollectTest: TestBase() { + + /** Tests the simple scenario where the publisher outputs a bounded stream of values to collect. */ + @Test + fun testCollect() = runTest { + val x = 100 + val xSum = x * (x + 1) / 2 + val publisher = JFlow.Publisher { subscriber -> + var requested = 0L + var lastOutput = 0 + subscriber.onSubscribe(object: JFlow.Subscription { + + override fun request(n: Long) { + requested += n + if (n <= 0) { + subscriber.onError(IllegalArgumentException()) + return + } + while (lastOutput < x && lastOutput < requested) { + lastOutput += 1 + subscriber.onNext(lastOutput) + } + if (lastOutput == x) + subscriber.onComplete() + } + + override fun cancel() { + /** According to rule 3.5 of the + * [reactive spec](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#3.5), + * this method can be called by the subscriber at any point, so it's not an error if it's called + * in this scenario. */ + } + + }) + } + var sum = 0 + publisher.collect { + sum += it + } + assertEquals(xSum, sum) + } + + /** Tests the behavior of [collect] when the publisher raises an error. */ + @Test + fun testCollectThrowingPublisher() = runTest { + val errorString = "Too many elements requested" + val x = 100 + val xSum = x * (x + 1) / 2 + val publisher = Publisher { subscriber -> + var requested = 0L + var lastOutput = 0 + subscriber.onSubscribe(object: Subscription { + + override fun request(n: Long) { + requested += n + if (n <= 0) { + subscriber.onError(IllegalArgumentException()) + return + } + while (lastOutput < x && lastOutput < requested) { + lastOutput += 1 + subscriber.onNext(lastOutput) + } + if (lastOutput == x) + subscriber.onError(IllegalArgumentException(errorString)) + } + + override fun cancel() { + /** See the comment for the corresponding part of [testCollect]. */ + } + + }) + } + var sum = 0 + try { + publisher.collect { + sum += it + } + } catch (e: IllegalArgumentException) { + assertEquals(errorString, e.message) + } + assertEquals(xSum, sum) + } + + /** Tests the behavior of [collect] when the action throws. */ + @Test + fun testCollectThrowingAction() = runTest { + val errorString = "Too many elements produced" + val x = 100 + val xSum = x * (x + 1) / 2 + val publisher = Publisher { subscriber -> + var requested = 0L + var lastOutput = 0 + subscriber.onSubscribe(object: Subscription { + + override fun request(n: Long) { + requested += n + if (n <= 0) { + subscriber.onError(IllegalArgumentException()) + return + } + while (lastOutput < x && lastOutput < requested) { + lastOutput += 1 + subscriber.onNext(lastOutput) + } + } + + override fun cancel() { + assertEquals(x, lastOutput) + expect(x + 2) + } + + }) + } + var sum = 0 + try { + expect(1) + var i = 1 + publisher.collect { + sum += it + i += 1 + expect(i) + if (sum >= xSum) { + throw IllegalArgumentException(errorString) + } + } + } catch (e: IllegalArgumentException) { + expect(x + 3) + assertEquals(errorString, e.message) + } + finish(x + 4) + } +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-reactive/src/Channel.kt b/reactive/kotlinx-coroutines-reactive/src/Channel.kt index d9c9b91a2c..b7fbf134c5 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Channel.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Channel.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.internal.* import org.reactivestreams.* /** - * Subscribes to this [Publisher] and returns a channel to receive elements emitted by it. - * The resulting channel shall be [cancelled][ReceiveChannel.cancel] to unsubscribe from this publisher. + * Subscribes to this [Publisher] and returns a channel to receive the elements emitted by it. + * The resulting channel needs to be [cancelled][ReceiveChannel.cancel] in order to unsubscribe from this publisher. - * @param request how many items to request from publisher in advance (optional, one by default). + * @param request how many items to request from the publisher in advance (optional, a single element by default). * * This method is deprecated in the favor of [Flow]. * Instead of iterating over the resulting channel please use [collect][Flow.collect]: @@ -35,7 +35,9 @@ public fun Publisher.openSubscription(request: Int = 1): ReceiveChannel Publisher.collect(action: (T) -> Unit): Unit = toChannel().consumeEach(action) @@ -61,7 +63,7 @@ private class SubscriptionChannel( // can be negative if we have receivers, but no subscription yet private val _requested = atomic(0) - // AbstractChannel overrides + // --------------------- AbstractChannel overrides ------------------------------- @Suppress("CANNOT_OVERRIDE_INVISIBLE_MEMBER") override fun onReceiveEnqueued() { _requested.loop { wasRequested -> @@ -89,7 +91,7 @@ private class SubscriptionChannel( _subscription.getAndSet(null)?.cancel() // cancel exactly once } - // Subscriber overrides + // --------------------- Subscriber overrides ------------------------------- override fun onSubscribe(s: Subscription) { _subscription.value = s while (true) { // lock-free loop on _requested diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 37113849ab..8b94b95986 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -12,22 +12,25 @@ import org.reactivestreams.* import kotlin.coroutines.* /** - * Creates cold reactive [Publisher] that runs a given [block] in a coroutine. + * Creates a cold reactive [Publisher] that runs a given [block] in a coroutine. + * * Every time the returned flux is subscribed, it starts a new coroutine in the specified [context]. - * Coroutine emits ([Subscriber.onNext]) values with `send`, completes ([Subscriber.onComplete]) - * when the coroutine completes or channel is explicitly closed and emits error ([Subscriber.onError]) - * if coroutine throws an exception or closes channel with a cause. - * Unsubscribing cancels running coroutine. + * The coroutine emits (via [Subscriber.onNext]) values with [send][ProducerScope.send], + * completes (via [Subscriber.onComplete]) when the coroutine completes or channel is explicitly closed, and emits + * errors (via [Subscriber.onError]) if the coroutine throws an exception or closes channel with a cause. + * Unsubscribing cancels the running coroutine. * - * Invocations of `send` are suspended appropriately when subscribers apply back-pressure and to ensure that - * `onNext` is not invoked concurrently. + * Invocations of [send][ProducerScope.send] are suspended appropriately when subscribers apply back-pressure and to + * ensure that [onNext][Subscriber.onNext] is not invoked concurrently. * * Coroutine context can be specified with [context] argument. - * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used. - * Method throws [IllegalArgumentException] if provided [context] contains a [Job] instance. + * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is + * used. * * **Note: This is an experimental api.** Behaviour of publishers that work as children in a parent scope with respect * to cancellation and error handling may change in the future. + * + * @throws IllegalArgumentException if the provided [context] contains a [Job] instance. */ @ExperimentalCoroutinesApi public fun publish( diff --git a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt index 1f197f94ed..1a527a3c2b 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt @@ -17,12 +17,12 @@ import kotlinx.coroutines.internal.* /** * Transforms the given reactive [Publisher] into [Flow]. - * Use [buffer] operator on the resulting flow to specify the size of the backpressure. - * More precisely, it specifies the value of the subscription's [request][Subscription.request]. - * [buffer] default capacity is used by default. + * Use the [buffer] operator on the resulting flow to specify the size of the back-pressure. + * In effect, it specifies the value of the subscription's [request][Subscription.request]. + * The [default buffer capacity][Channel.BUFFERED] for a suspending channel is used by default. * - * If any of the resulting flow transformations fails, subscription is immediately cancelled and all in-flight elements - * are discarded. + * If any of the resulting flow transformations fails, the subscription is immediately cancelled and all the in-flight + * elements are discarded. * * This function is integrated with `ReactorContext` from `kotlinx-coroutines-reactor` module, * see its documentation for additional details. @@ -31,13 +31,13 @@ public fun Publisher.asFlow(): Flow = PublisherAsFlow(this) /** - * Transforms the given flow to a reactive specification compliant [Publisher]. + * Transforms the given flow into a reactive specification compliant [Publisher]. * * This function is integrated with `ReactorContext` from `kotlinx-coroutines-reactor` module, * see its documentation for additional details. * - * An optional [context] can be specified to control the execution context of calls to [Subscriber] methods. - * You can set a [CoroutineDispatcher] to confine them to a specific thread and/or various [ThreadContextElement] to + * An optional [context] can be specified to control the execution context of calls to the [Subscriber] methods. + * A [CoroutineDispatcher] can be set to confine them to a specific thread; various [ThreadContextElement] can be set to * inject additional context into the caller thread. By default, the [Unconfined][Dispatchers.Unconfined] dispatcher * is used, so calls are performed from an arbitrary thread. */ @@ -55,8 +55,8 @@ private class PublisherAsFlow( PublisherAsFlow(publisher, context, capacity, onBufferOverflow) /* - * Suppress for Channel.CHANNEL_DEFAULT_CAPACITY. - * It's too counter-intuitive to be public and moving it to Flow companion + * The @Suppress is for Channel.CHANNEL_DEFAULT_CAPACITY. + * It's too counter-intuitive to be public, and moving it to Flow companion * will also create undesired effect. */ @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") diff --git a/reactive/kotlinx-coroutines-reactive/test/PublisherCollectTest.kt b/reactive/kotlinx-coroutines-reactive/test/PublisherCollectTest.kt new file mode 100644 index 0000000000..e4753f0467 --- /dev/null +++ b/reactive/kotlinx-coroutines-reactive/test/PublisherCollectTest.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.reactive + +import kotlinx.coroutines.* +import org.junit.Test +import org.reactivestreams.* +import kotlin.test.* + +class PublisherCollectTest: TestBase() { + + /** Tests the simple scenario where the publisher outputs a bounded stream of values to collect. */ + @Test + fun testCollect() = runTest { + val x = 100 + val xSum = x * (x + 1) / 2 + val publisher = Publisher { subscriber -> + var requested = 0L + var lastOutput = 0 + subscriber.onSubscribe(object: Subscription { + + override fun request(n: Long) { + requested += n + if (n <= 0) { + subscriber.onError(IllegalArgumentException()) + return + } + while (lastOutput < x && lastOutput < requested) { + lastOutput += 1 + subscriber.onNext(lastOutput) + } + if (lastOutput == x) + subscriber.onComplete() + } + + override fun cancel() { + /** According to rule 3.5 of the + * [reactive spec](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#3.5), + * this method can be called by the subscriber at any point, so it's not an error if it's called + * in this scenario. */ + } + + }) + } + var sum = 0 + publisher.collect { + sum += it + } + assertEquals(xSum, sum) + } + + /** Tests the behavior of [collect] when the publisher raises an error. */ + @Test + fun testCollectThrowingPublisher() = runTest { + val errorString = "Too many elements requested" + val x = 100 + val xSum = x * (x + 1) / 2 + val publisher = Publisher { subscriber -> + var requested = 0L + var lastOutput = 0 + subscriber.onSubscribe(object: Subscription { + + override fun request(n: Long) { + requested += n + if (n <= 0) { + subscriber.onError(IllegalArgumentException()) + return + } + while (lastOutput < x && lastOutput < requested) { + lastOutput += 1 + subscriber.onNext(lastOutput) + } + if (lastOutput == x) + subscriber.onError(IllegalArgumentException(errorString)) + } + + override fun cancel() { + /** See the comment for the corresponding part of [testCollect]. */ + } + + }) + } + var sum = 0 + try { + publisher.collect { + sum += it + } + } catch (e: IllegalArgumentException) { + assertEquals(errorString, e.message) + } + assertEquals(xSum, sum) + } + + /** Tests the behavior of [collect] when the action throws. */ + @Test + fun testCollectThrowingAction() = runTest { + val errorString = "Too many elements produced" + val x = 100 + val xSum = x * (x + 1) / 2 + val publisher = Publisher { subscriber -> + var requested = 0L + var lastOutput = 0 + subscriber.onSubscribe(object: Subscription { + + override fun request(n: Long) { + requested += n + if (n <= 0) { + subscriber.onError(IllegalArgumentException()) + return + } + while (lastOutput < x && lastOutput < requested) { + lastOutput += 1 + subscriber.onNext(lastOutput) + } + } + + override fun cancel() { + assertEquals(x, lastOutput) + expect(x + 2) + } + + }) + } + var sum = 0 + try { + expect(1) + var i = 1 + publisher.collect { + sum += it + i += 1 + expect(i) + if (sum >= xSum) { + throw IllegalArgumentException(errorString) + } + } + } catch (e: IllegalArgumentException) { + expect(x + 3) + assertEquals(errorString, e.message) + } + finish(x + 4) + } +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt index 3bc50c8d57..51a5d54e1c 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxChannel.kt @@ -10,6 +10,7 @@ import kotlinx.atomicfu.* import kotlinx.coroutines.channels.* import kotlinx.coroutines.internal.* import kotlinx.coroutines.flow.* +import kotlinx.coroutines.reactive.* /** * Subscribes to this [MaybeSource] and returns a channel to receive elements emitted by it. @@ -41,14 +42,18 @@ public fun ObservableSource.openSubscription(): ReceiveChannel { /** * Subscribes to this [MaybeSource] and performs the specified action for each received element. - * Cancels subscription if any exception happens during collect. + * + * If [action] throws an exception at some point or if the [MaybeSource] raises an error, the exception is rethrown from + * [collect]. */ public suspend inline fun MaybeSource.collect(action: (T) -> Unit): Unit = toChannel().consumeEach(action) /** * Subscribes to this [ObservableSource] and performs the specified action for each received element. - * Cancels subscription if any exception happens during collect. + * + * If [action] throws an exception at some point, the subscription is cancelled, and the exception is rethrown from + * [collect]. Also, if the [ObservableSource] signals an error, that error is rethrown from [collect]. */ public suspend inline fun ObservableSource.collect(action: (T) -> Unit): Unit = toChannel().consumeEach(action) @@ -84,7 +89,8 @@ private class SubscriptionChannel : } override fun onSuccess(t: T) { - trySend(t) // Safe to ignore return value here, expectedly racing with cancellation + trySend(t) + close(cause = null) } override fun onNext(t: T) { diff --git a/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt b/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt index 292f6187fa..f5d128d311 100644 --- a/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt +++ b/reactive/kotlinx-coroutines-rx2/test/MaybeTest.kt @@ -254,6 +254,56 @@ class MaybeTest : TestBase() { finish(7) } + /** Tests the simple scenario where the Maybe doesn't output a value. */ + @Test + fun testMaybeCollectEmpty() = runTest { + expect(1) + Maybe.empty().collect { + expectUnreached() + } + finish(2) + } + + /** Tests the simple scenario where the Maybe doesn't output a value. */ + @Test + fun testMaybeCollectSingle() = runTest { + expect(1) + Maybe.just("OK").collect { + assertEquals("OK", it) + expect(2) + } + finish(3) + } + + /** Tests the behavior of [collect] when the Maybe raises an error. */ + @Test + fun testMaybeCollectThrowingMaybe() = runTest { + expect(1) + try { + Maybe.error(TestException()).collect { + expectUnreached() + } + } catch (e: TestException) { + expect(2) + } + finish(3) + } + + /** Tests the behavior of [collect] when the action throws. */ + @Test + fun testMaybeCollectThrowingAction() = runTest { + expect(1) + try { + Maybe.just("OK").collect { + expect(2) + throw TestException() + } + } catch (e: TestException) { + expect(3) + } + finish(4) + } + @Test fun testSuppressedException() = runTest { val maybe = rxMaybe(currentDispatcher()) { diff --git a/reactive/kotlinx-coroutines-rx2/test/ObservableCollectTest.kt b/reactive/kotlinx-coroutines-rx2/test/ObservableCollectTest.kt new file mode 100644 index 0000000000..508f594ac3 --- /dev/null +++ b/reactive/kotlinx-coroutines-rx2/test/ObservableCollectTest.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.rx2 + +import io.reactivex.* +import io.reactivex.disposables.* +import kotlinx.coroutines.* +import org.junit.Test +import kotlin.test.* + +class ObservableCollectTest: TestBase() { + + /** Tests the behavior of [collect] when the publisher raises an error. */ + @Test + fun testObservableCollectThrowingObservable() = runTest { + expect(1) + var sum = 0 + try { + rxObservable { + for (i in 0..100) { + send(i) + } + throw TestException() + }.collect { + sum += it + } + } catch (e: TestException) { + assertTrue(sum > 0) + finish(2) + } + } + + /** Tests the behavior of [collect] when the action throws. */ + @Test + fun testObservableCollectThrowingAction() = runTest { + expect(1) + var sum = 0 + val expectedSum = 5 + try { + var disposed = false + ObservableSource { observer -> + launch(Dispatchers.Default) { + observer.onSubscribe(object : Disposable { + override fun dispose() { + disposed = true + expect(expectedSum + 2) + } + + override fun isDisposed(): Boolean = disposed + }) + while (!disposed) { + observer.onNext(1) + } + } + }.collect { + expect(sum + 2) + sum += it + if (sum == expectedSum) { + throw TestException() + } + } + } catch (e: TestException) { + assertEquals(expectedSum, sum) + finish(expectedSum + 3) + } + } +} \ No newline at end of file diff --git a/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt b/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt index ad780f7504..21238d2491 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxChannel.kt @@ -9,6 +9,7 @@ import io.reactivex.rxjava3.disposables.* import kotlinx.atomicfu.* import kotlinx.coroutines.channels.* import kotlinx.coroutines.internal.* +import kotlinx.coroutines.flow.* /** * Subscribes to this [MaybeSource] and returns a channel to receive elements emitted by it. @@ -40,14 +41,18 @@ internal fun ObservableSource.openSubscription(): ReceiveChannel { /** * Subscribes to this [MaybeSource] and performs the specified action for each received element. - * Cancels subscription if any exception happens during collect. + * + * If [action] throws an exception at some point or if the [MaybeSource] raises an error, the exception is rethrown from + * [collect]. */ public suspend inline fun MaybeSource.collect(action: (T) -> Unit): Unit = openSubscription().consumeEach(action) /** * Subscribes to this [ObservableSource] and performs the specified action for each received element. - * Cancels subscription if any exception happens during collect. + * + * If [action] throws an exception at some point, the subscription is cancelled, and the exception is rethrown from + * [collect]. Also, if the [ObservableSource] signals an error, that error is rethrown from [collect]. */ public suspend inline fun ObservableSource.collect(action: (T) -> Unit): Unit = openSubscription().consumeEach(action) @@ -69,7 +74,8 @@ private class SubscriptionChannel : } override fun onSuccess(t: T) { - trySend(t) // Safe to ignore return value here, expectedly racing with cancellation + trySend(t) + close(cause = null) } override fun onNext(t: T) { diff --git a/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt b/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt index bdb5481d80..bea939efde 100644 --- a/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt +++ b/reactive/kotlinx-coroutines-rx3/test/MaybeTest.kt @@ -84,7 +84,7 @@ class MaybeTest : TestBase() { expectUnreached() } expect(2) - // nothing is called on a disposed rx3 maybe + // nothing is called on a disposed rx2 maybe val sub = maybe.subscribe({ expectUnreached() }, { @@ -254,6 +254,56 @@ class MaybeTest : TestBase() { finish(7) } + /** Tests the simple scenario where the Maybe doesn't output a value. */ + @Test + fun testMaybeCollectEmpty() = runTest { + expect(1) + Maybe.empty().collect { + expectUnreached() + } + finish(2) + } + + /** Tests the simple scenario where the Maybe doesn't output a value. */ + @Test + fun testMaybeCollectSingle() = runTest { + expect(1) + Maybe.just("OK").collect { + assertEquals("OK", it) + expect(2) + } + finish(3) + } + + /** Tests the behavior of [collect] when the Maybe raises an error. */ + @Test + fun testMaybeCollectThrowingMaybe() = runTest { + expect(1) + try { + Maybe.error(TestException()).collect { + expectUnreached() + } + } catch (e: TestException) { + expect(2) + } + finish(3) + } + + /** Tests the behavior of [collect] when the action throws. */ + @Test + fun testMaybeCollectThrowingAction() = runTest { + expect(1) + try { + Maybe.just("OK").collect { + expect(2) + throw TestException() + } + } catch (e: TestException) { + expect(3) + } + finish(4) + } + @Test fun testSuppressedException() = runTest { val maybe = rxMaybe(currentDispatcher()) { diff --git a/reactive/kotlinx-coroutines-rx3/test/ObservableCollectTest.kt b/reactive/kotlinx-coroutines-rx3/test/ObservableCollectTest.kt new file mode 100644 index 0000000000..680786f9b3 --- /dev/null +++ b/reactive/kotlinx-coroutines-rx3/test/ObservableCollectTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.rx3 + +import io.reactivex.rxjava3.core.ObservableSource +import io.reactivex.rxjava3.disposables.* +import kotlinx.coroutines.* +import org.junit.Test +import kotlin.test.* + +class ObservableCollectTest: TestBase() { + + /** Tests the behavior of [collect] when the publisher raises an error. */ + @Test + fun testObservableCollectThrowingObservable() = runTest { + expect(1) + var sum = 0 + try { + rxObservable { + for (i in 0..100) { + send(i) + } + throw TestException() + }.collect { + sum += it + } + } catch (e: TestException) { + assertTrue(sum > 0) + finish(2) + } + } + + @Test + fun testObservableCollectThrowingAction() = runTest { + expect(1) + var sum = 0 + val expectedSum = 5 + try { + var disposed = false + ObservableSource { observer -> + launch(Dispatchers.Default) { + observer.onSubscribe(object : Disposable { + override fun dispose() { + disposed = true + expect(expectedSum + 2) + } + + override fun isDisposed(): Boolean = disposed + }) + while (!disposed) { + observer.onNext(1) + } + } + }.collect { + expect(sum + 2) + sum += it + if (sum == expectedSum) { + throw TestException() + } + } + } catch (e: TestException) { + assertEquals(expectedSum, sum) + finish(expectedSum + 3) + } + } +} \ No newline at end of file From 3116b8c8d4100fa7f6deb6e64887d804f7f9804b Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Fri, 23 Apr 2021 12:41:29 +0300 Subject: [PATCH 52/55] CoroutinesTimeout for JUnit5 (#2402) --- gradle.properties | 1 + .../api/kotlinx-coroutines-debug.api | 5 + kotlinx-coroutines-debug/build.gradle | 9 + .../src/junit/CoroutinesTimeoutImpl.kt | 81 +++++ .../{ => junit}/junit4/CoroutinesTimeout.kt | 0 .../junit4/CoroutinesTimeoutStatement.kt | 30 ++ .../src/junit/junit5/CoroutinesTimeout.kt | 63 ++++ .../junit5/CoroutinesTimeoutExtension.kt | 279 ++++++++++++++++++ .../src/junit4/CoroutinesTimeoutStatement.kt | 87 ------ .../junit5/CoroutinesTimeoutExtensionTest.kt | 121 ++++++++ .../CoroutinesTimeoutInheritanceTest.kt | 60 ++++ .../junit5/CoroutinesTimeoutMethodTest.kt | 44 +++ .../junit5/CoroutinesTimeoutNestedTest.kt | 29 ++ .../junit5/CoroutinesTimeoutSimpleTest.kt | 61 ++++ .../test/junit5/CoroutinesTimeoutTest.kt | 170 +++++++++++ .../test/junit5/RegisterExtensionExample.kt | 20 ++ 16 files changed, 973 insertions(+), 87 deletions(-) create mode 100644 kotlinx-coroutines-debug/src/junit/CoroutinesTimeoutImpl.kt rename kotlinx-coroutines-debug/src/{ => junit}/junit4/CoroutinesTimeout.kt (100%) create mode 100644 kotlinx-coroutines-debug/src/junit/junit4/CoroutinesTimeoutStatement.kt create mode 100644 kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeout.kt create mode 100644 kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeoutExtension.kt delete mode 100644 kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt create mode 100644 kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutExtensionTest.kt create mode 100644 kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutInheritanceTest.kt create mode 100644 kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutMethodTest.kt create mode 100644 kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutNestedTest.kt create mode 100644 kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutSimpleTest.kt create mode 100644 kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutTest.kt create mode 100644 kotlinx-coroutines-debug/test/junit5/RegisterExtensionExample.kt diff --git a/gradle.properties b/gradle.properties index 97c6af986f..15935ab114 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,7 @@ kotlin_version=1.5.0-RC # Dependencies junit_version=4.12 +junit5_version=5.7.0 atomicfu_version=0.15.2 knit_version=0.2.3 html_version=0.7.2 diff --git a/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api b/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api index b6056c410c..5bf70626a4 100644 --- a/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api +++ b/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api @@ -61,3 +61,8 @@ public final class kotlinx/coroutines/debug/junit4/CoroutinesTimeout$Companion { public static synthetic fun seconds$default (Lkotlinx/coroutines/debug/junit4/CoroutinesTimeout$Companion;JZZILjava/lang/Object;)Lkotlinx/coroutines/debug/junit4/CoroutinesTimeout; } +public abstract interface annotation class kotlinx/coroutines/debug/junit5/CoroutinesTimeout : java/lang/annotation/Annotation { + public abstract fun cancelOnTimeout ()Z + public abstract fun testTimeoutMs ()J +} + diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index faaed91206..b2e3f2cf53 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -20,6 +20,9 @@ configurations { dependencies { compileOnly "junit:junit:$junit_version" + compileOnly "org.junit.jupiter:junit-jupiter-api:$junit5_version" + testCompile "org.junit.jupiter:junit-jupiter-engine:$junit5_version" + testCompile "org.junit.platform:junit-platform-testkit:1.7.0" shadowDeps "net.bytebuddy:byte-buddy:$byte_buddy_version" shadowDeps "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" compileOnly "io.projectreactor.tools:blockhound:$blockhound_version" @@ -38,6 +41,12 @@ if (rootProject.ext.jvm_ir_enabled) { } } +java { + /* This is needed to be able to run JUnit5 tests. Otherwise, Gradle complains that it can't find the + JVM1.6-compatible version of the `junit-jupiter-api` artifact. */ + disableAutoTargetJvm() +} + jar { manifest { attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain" diff --git a/kotlinx-coroutines-debug/src/junit/CoroutinesTimeoutImpl.kt b/kotlinx-coroutines-debug/src/junit/CoroutinesTimeoutImpl.kt new file mode 100644 index 0000000000..06a84a5bf9 --- /dev/null +++ b/kotlinx-coroutines-debug/src/junit/CoroutinesTimeoutImpl.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug + +import java.util.concurrent.* + +/** + * Run [invocation] in a separate thread with the given timeout in ms, after which the coroutines info is dumped and, if + * [cancelOnTimeout] is set, the execution is interrupted. + * + * Assumes that [DebugProbes] are installed. Does not deinstall them. + */ +internal inline fun runWithTimeoutDumpingCoroutines( + methodName: String, + testTimeoutMs: Long, + cancelOnTimeout: Boolean, + initCancellationException: () -> Throwable, + crossinline invocation: () -> T +): T { + val testStartedLatch = CountDownLatch(1) + val testResult = FutureTask { + testStartedLatch.countDown() + invocation() + } + /* + * We are using hand-rolled thread instead of single thread executor + * in order to be able to safely interrupt thread in the end of a test + */ + val testThread = Thread(testResult, "Timeout test thread").apply { isDaemon = true } + try { + testThread.start() + // Await until test is started to take only test execution time into account + testStartedLatch.await() + return testResult.get(testTimeoutMs, TimeUnit.MILLISECONDS) + } catch (e: TimeoutException) { + handleTimeout(testThread, methodName, testTimeoutMs, cancelOnTimeout, initCancellationException()) + } catch (e: ExecutionException) { + throw e.cause ?: e + } +} + +private fun handleTimeout(testThread: Thread, methodName: String, testTimeoutMs: Long, cancelOnTimeout: Boolean, + cancellationException: Throwable): Nothing { + val units = + if (testTimeoutMs % 1000 == 0L) + "${testTimeoutMs / 1000} seconds" + else "$testTimeoutMs milliseconds" + + System.err.println("\nTest $methodName timed out after $units\n") + System.err.flush() + + DebugProbes.dumpCoroutines() + System.out.flush() // Synchronize serr/sout + + /* + * Order is important: + * 1) Create exception with a stacktrace of hang test + * 2) Cancel all coroutines via debug agent API (changing system state!) + * 3) Throw created exception + */ + cancellationException.attachStacktraceFrom(testThread) + testThread.interrupt() + cancelIfNecessary(cancelOnTimeout) + // If timed out test throws an exception, we can't do much except ignoring it + throw cancellationException +} + +private fun cancelIfNecessary(cancelOnTimeout: Boolean) { + if (cancelOnTimeout) { + DebugProbes.dumpCoroutinesInfo().forEach { + it.job?.cancel() + } + } +} + +private fun Throwable.attachStacktraceFrom(thread: Thread) { + val stackTrace = thread.stackTrace + this.stackTrace = stackTrace +} diff --git a/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeout.kt b/kotlinx-coroutines-debug/src/junit/junit4/CoroutinesTimeout.kt similarity index 100% rename from kotlinx-coroutines-debug/src/junit4/CoroutinesTimeout.kt rename to kotlinx-coroutines-debug/src/junit/junit4/CoroutinesTimeout.kt diff --git a/kotlinx-coroutines-debug/src/junit/junit4/CoroutinesTimeoutStatement.kt b/kotlinx-coroutines-debug/src/junit/junit4/CoroutinesTimeoutStatement.kt new file mode 100644 index 0000000000..aa6b8df243 --- /dev/null +++ b/kotlinx-coroutines-debug/src/junit/junit4/CoroutinesTimeoutStatement.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit4 + +import kotlinx.coroutines.debug.* +import org.junit.runner.* +import org.junit.runners.model.* +import java.util.concurrent.* + +internal class CoroutinesTimeoutStatement( + private val testStatement: Statement, + private val testDescription: Description, + private val testTimeoutMs: Long, + private val cancelOnTimeout: Boolean = false +) : Statement() { + + override fun evaluate() { + try { + runWithTimeoutDumpingCoroutines(testDescription.methodName, testTimeoutMs, cancelOnTimeout, + { TestTimedOutException(testTimeoutMs, TimeUnit.MILLISECONDS) }) + { + testStatement.evaluate() + } + } finally { + DebugProbes.uninstall() + } + } +} diff --git a/kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeout.kt b/kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeout.kt new file mode 100644 index 0000000000..9a8263fe5e --- /dev/null +++ b/kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeout.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 +import kotlinx.coroutines.debug.* +import org.junit.jupiter.api.* +import org.junit.jupiter.api.extension.* +import org.junit.jupiter.api.parallel.* +import java.lang.annotation.* + +/** + * Coroutines timeout annotation that is similar to JUnit5's [Timeout] annotation. It allows running test methods in a + * separate thread, failing them after the provided time limit and interrupting the thread. + * + * Additionally, it installs [DebugProbes] and dumps all coroutines at the moment of the timeout. It also cancels + * coroutines on timeout if [cancelOnTimeout] set to `true`. The dump contains the coroutine creation stack traces. + * + * This annotation has an effect on test, test factory, test template, and lifecycle methods and test classes that are + * annotated with it. + * + * Annotating a class is the same as annotating every test, test factory, and test template method (but not lifecycle + * methods) of that class and its inner test classes, unless any of them is annotated with [CoroutinesTimeout], in which + * case their annotation overrides the one on the containing class. + * + * Declaring [CoroutinesTimeout] on a test factory checks that it finishes in the specified time, but does not check + * whether the methods that it produces obey the timeout as well. + * + * Example usage: + * ``` + * @CoroutinesTimeout(100) + * class CoroutinesTimeoutSimpleTest { + * // does not time out, as the annotation on the method overrides the class-level one + * @CoroutinesTimeout(1000) + * @Test + * fun classTimeoutIsOverridden() { + * runBlocking { + * delay(150) + * } + * } + * + * // times out in 100 ms, timeout value is taken from the class-level annotation + * @Test + * fun classTimeoutIsUsed() { + * runBlocking { + * delay(150) + * } + * } + * } + * ``` + * + * @see Timeout + */ +@ExtendWith(CoroutinesTimeoutExtension::class) +@Inherited +@MustBeDocumented +@ResourceLock("coroutines timeout", mode = ResourceAccessMode.READ) +@Retention(value = AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +public annotation class CoroutinesTimeout( + val testTimeoutMs: Long, + val cancelOnTimeout: Boolean = false +) diff --git a/kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeoutExtension.kt b/kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeoutExtension.kt new file mode 100644 index 0000000000..a3e7713a5c --- /dev/null +++ b/kotlinx-coroutines-debug/src/junit/junit5/CoroutinesTimeoutExtension.kt @@ -0,0 +1,279 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import kotlinx.coroutines.debug.* +import kotlinx.coroutines.debug.runWithTimeoutDumpingCoroutines +import org.junit.jupiter.api.extension.* +import org.junit.platform.commons.support.AnnotationSupport +import java.lang.reflect.* +import java.util.* +import java.util.concurrent.atomic.* + +internal class CoroutinesTimeoutException(val timeoutMs: Long): Exception("test timed out ofter $timeoutMs ms") + +/** + * This JUnit5 extension allows running test, test factory, test template, and lifecycle methods in a separate thread, + * failing them after the provided time limit and interrupting the thread. + * + * Additionally, it installs [DebugProbes] and dumps all coroutines at the moment of the timeout. It also cancels + * coroutines on timeout if [cancelOnTimeout] set to `true`. + * [enableCoroutineCreationStackTraces] controls the corresponding [DebugProbes.enableCreationStackTraces] property + * and can be optionally disabled to speed-up tests if creation stack traces are not needed. + * + * Beware that if several tests that use this extension set [enableCoroutineCreationStackTraces] to different values and + * execute in parallel, the behavior is ill-defined. In order to avoid conflicts between different instances of this + * extension when using JUnit5 in parallel, use [ResourceLock] with resource name `coroutines timeout` on tests that use + * it. Note that the tests annotated with [CoroutinesTimeout] already use this [ResourceLock], so there is no need to + * annotate them additionally. + * + * Note that while calls to test factories are verified to finish in the specified time, but the methods that they + * produce are not affected by this extension. + * + * Beware that registering the extension via [CoroutinesTimeout] annotation conflicts with manually registering it on + * the same tests via other methods (most notably, [RegisterExtension]) and is prohibited. + * + * Example of usage: + * ``` + * class HangingTest { + * @JvmField + * @RegisterExtension + * val timeout = CoroutinesTimeoutExtension.seconds(5) + * + * @Test + * fun testThatHangs() = runBlocking { + * ... + * delay(Long.MAX_VALUE) // somewhere deep in the stack + * ... + * } + * } + * ``` + * + * @see [CoroutinesTimeout] + * */ +// NB: the constructor is not private so that JUnit is able to call it via reflection. +internal class CoroutinesTimeoutExtension internal constructor( + private val enableCoroutineCreationStackTraces: Boolean = true, + private val timeoutMs: Long? = null, + private val cancelOnTimeout: Boolean? = null): InvocationInterceptor +{ + /** + * Creates the [CoroutinesTimeoutExtension] extension with the given timeout in milliseconds. + */ + public constructor(timeoutMs: Long, cancelOnTimeout: Boolean = false, + enableCoroutineCreationStackTraces: Boolean = true): + this(enableCoroutineCreationStackTraces, timeoutMs, cancelOnTimeout) + + public companion object { + /** + * Creates the [CoroutinesTimeoutExtension] extension with the given timeout in seconds. + */ + @JvmOverloads + public fun seconds(timeout: Int, cancelOnTimeout: Boolean = false, + enableCoroutineCreationStackTraces: Boolean = true): CoroutinesTimeoutExtension = + CoroutinesTimeoutExtension(enableCoroutineCreationStackTraces, timeout.toLong() * 1000, cancelOnTimeout) + } + + /** @see [initialize] */ + private val debugProbesOwnershipPassed = AtomicBoolean(false) + + private fun tryPassDebugProbesOwnership() = debugProbesOwnershipPassed.compareAndSet(false, true) + + /* We install the debug probes early so that the coroutines launched from the test constructor are captured as well. + However, this is not enough as the same extension instance may be reused several times, even cleaning up its + resources from the store. */ + init { + DebugProbes.enableCreationStackTraces = enableCoroutineCreationStackTraces + DebugProbes.install() + } + + // This is needed so that a class with no tests still successfully passes the ownership of DebugProbes to JUnit5. + override fun interceptTestClassConstructor( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext>, + extensionContext: ExtensionContext + ): T { + initialize(extensionContext) + return invocation.proceed() + } + + /** + * Initialize this extension instance and/or the extension value store. + * + * It seems that the only way to reliably have JUnit5 clean up after its extensions is to put an instance of + * [ExtensionContext.Store.CloseableResource] into the value store corresponding to the extension instance, which + * means that [DebugProbes.uninstall] must be placed into the value store. [debugProbesOwnershipPassed] is `true` + * if the call to [DebugProbes.install] performed in the constructor of the extension instance was matched with a + * placing of [DebugProbes.uninstall] into the value store. We call the process of placing the cleanup procedure + * "passing the ownership", as now JUnit5 (and not our code) has to worry about uninstalling the debug probes. + * + * However, extension instances can be reused with different value stores, and value stores can be reused across + * extension instances. This leads to a tricky scheme of performing [DebugProbes.uninstall]: + * + * * If neither the ownership of this instance's [DebugProbes] was yet passed nor there is any cleanup procedure + * stored, it means that we can just store our cleanup procedure, passing the ownership. + * * If the ownership was not yet passed, but a cleanup procedure is already stored, we can't just replace it with + * another one, as this would lead to imbalance between [DebugProbes.install] and [DebugProbes.uninstall]. + * Instead, we know that this extension context will at least outlive this use of this instance, so some debug + * probes other than the ones from our constructor are already installed and won't be uninstalled during our + * operation. We simply uninstall the debug probes that were installed in our constructor. + * * If the ownership was passed, but the store is empty, it means that this test instance is reused and, possibly, + * the debug probes installed in its constructor were already uninstalled. This means that we have to install them + * anew and store an uninstaller. + */ + private fun initialize(extensionContext: ExtensionContext) { + val store: ExtensionContext.Store = extensionContext.getStore( + ExtensionContext.Namespace.create(CoroutinesTimeoutExtension::class, extensionContext.uniqueId)) + /** It seems that the JUnit5 documentation does not specify the relationship between the extension instances and + * the corresponding [ExtensionContext] (in which the value stores are managed), so it is unclear whether it's + * theoretically possible for two extension instances that run concurrently to share an extension context. So, + * just in case this risk exists, we synchronize here. */ + synchronized(store) { + if (store["debugProbes"] == null) { + if (!tryPassDebugProbesOwnership()) { + /** This means that the [DebugProbes.install] call from the constructor of this extensions has + * already been matched with a corresponding cleanup procedure for JUnit5, but then JUnit5 cleaned + * everything up and later reused the same extension instance for other tests. Therefore, we need to + * install the [DebugProbes] anew. */ + DebugProbes.enableCreationStackTraces = enableCoroutineCreationStackTraces + DebugProbes.install() + } + /** put a fake resource into this extensions's store so that JUnit cleans it up, uninstalling the + * [DebugProbes] after this extension instance is no longer needed. **/ + store.put("debugProbes", ExtensionContext.Store.CloseableResource { DebugProbes.uninstall() }) + } else if (!debugProbesOwnershipPassed.get()) { + /** This instance shares its store with other ones. Because of this, there was no need to install + * [DebugProbes], they are already installed, and this fact will outlive this use of this instance of + * the extension. */ + if (tryPassDebugProbesOwnership()) { + // We successfully marked the ownership as passed and now may uninstall the extraneous debug probes. + DebugProbes.uninstall() + } + } + } + } + + override fun interceptTestMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + interceptNormalMethod(invocation, invocationContext, extensionContext) + } + + override fun interceptAfterAllMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + interceptLifecycleMethod(invocation, invocationContext, extensionContext) + } + + override fun interceptAfterEachMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + interceptLifecycleMethod(invocation, invocationContext, extensionContext) + } + + override fun interceptBeforeAllMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + interceptLifecycleMethod(invocation, invocationContext, extensionContext) + } + + override fun interceptBeforeEachMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + interceptLifecycleMethod(invocation, invocationContext, extensionContext) + } + + override fun interceptTestFactoryMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ): T = interceptNormalMethod(invocation, invocationContext, extensionContext) + + override fun interceptTestTemplateMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + interceptNormalMethod(invocation, invocationContext, extensionContext) + } + + private fun Class.coroutinesTimeoutAnnotation(): Optional = + AnnotationSupport.findAnnotation(this, CoroutinesTimeout::class.java).or { + enclosingClass?.coroutinesTimeoutAnnotation() ?: Optional.empty() + } + + private fun interceptMethod( + useClassAnnotation: Boolean, + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ): T { + initialize(extensionContext) + val testAnnotationOptional = + AnnotationSupport.findAnnotation(invocationContext.executable, CoroutinesTimeout::class.java) + val classAnnotationOptional = extensionContext.testClass.flatMap { it.coroutinesTimeoutAnnotation() } + if (timeoutMs != null && cancelOnTimeout != null) { + // this means we @RegisterExtension was used in order to register this extension. + if (testAnnotationOptional.isPresent || classAnnotationOptional.isPresent) { + /* Using annotations creates a separate instance of the extension, which composes in a strange way: both + timeouts are applied. This is at odds with the concept that method-level annotations override the outer + rules and may lead to unexpected outcomes, so we prohibit this. */ + throw UnsupportedOperationException("Using CoroutinesTimeout along with instance field-registered CoroutinesTimeout is prohibited; please use either @RegisterExtension or @CoroutinesTimeout, but not both") + } + return interceptInvocation(invocation, invocationContext.executable.name, timeoutMs, cancelOnTimeout) + } + /* The extension was registered via an annotation; check that we succeeded in finding the annotation that led to + the extension being registered and taking its parameters. */ + if (testAnnotationOptional.isEmpty && classAnnotationOptional.isEmpty) { + throw UnsupportedOperationException("Timeout was registered with a CoroutinesTimeout annotation, but we were unable to find it. Please report this.") + } + return when { + testAnnotationOptional.isPresent -> { + val annotation = testAnnotationOptional.get() + interceptInvocation(invocation, invocationContext.executable.name, annotation.testTimeoutMs, + annotation.cancelOnTimeout) + } + useClassAnnotation && classAnnotationOptional.isPresent -> { + val annotation = classAnnotationOptional.get() + interceptInvocation(invocation, invocationContext.executable.name, annotation.testTimeoutMs, + annotation.cancelOnTimeout) + } + else -> { + invocation.proceed() + } + } + } + + private fun interceptNormalMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ): T = interceptMethod(true, invocation, invocationContext, extensionContext) + + private fun interceptLifecycleMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) = interceptMethod(false, invocation, invocationContext, extensionContext) + + private fun interceptInvocation( + invocation: InvocationInterceptor.Invocation, + methodName: String, + testTimeoutMs: Long, + cancelOnTimeout: Boolean + ): T = + runWithTimeoutDumpingCoroutines(methodName, testTimeoutMs, cancelOnTimeout, + { CoroutinesTimeoutException(testTimeoutMs) }, { invocation.proceed() }) +} \ No newline at end of file diff --git a/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt b/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt deleted file mode 100644 index 4baf409de8..0000000000 --- a/kotlinx-coroutines-debug/src/junit4/CoroutinesTimeoutStatement.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.coroutines.debug.junit4 - -import kotlinx.coroutines.debug.* -import org.junit.runner.* -import org.junit.runners.model.* -import java.util.concurrent.* - -internal class CoroutinesTimeoutStatement( - testStatement: Statement, - private val testDescription: Description, - private val testTimeoutMs: Long, - private val cancelOnTimeout: Boolean = false -) : Statement() { - - private val testStartedLatch = CountDownLatch(1) - - private val testResult = FutureTask { - testStartedLatch.countDown() - testStatement.evaluate() - } - - /* - * We are using hand-rolled thread instead of single thread executor - * in order to be able to safely interrupt thread in the end of a test - */ - private val testThread = Thread(testResult, "Timeout test thread").apply { isDaemon = true } - - override fun evaluate() { - try { - testThread.start() - // Await until test is started to take only test execution time into account - testStartedLatch.await() - testResult.get(testTimeoutMs, TimeUnit.MILLISECONDS) - return - } catch (e: TimeoutException) { - handleTimeout(testDescription) - } catch (e: ExecutionException) { - throw e.cause ?: e - } finally { - DebugProbes.uninstall() - } - } - - private fun handleTimeout(description: Description) { - val units = - if (testTimeoutMs % 1000 == 0L) - "${testTimeoutMs / 1000} seconds" - else "$testTimeoutMs milliseconds" - - System.err.println("\nTest ${description.methodName} timed out after $units\n") - System.err.flush() - - DebugProbes.dumpCoroutines() - System.out.flush() // Synchronize serr/sout - - /* - * Order is important: - * 1) Create exception with a stacktrace of hang test - * 2) Cancel all coroutines via debug agent API (changing system state!) - * 3) Throw created exception - */ - val exception = createTimeoutException(testThread) - cancelIfNecessary() - // If timed out test throws an exception, we can't do much except ignoring it - throw exception - } - - private fun cancelIfNecessary() { - if (cancelOnTimeout) { - DebugProbes.dumpCoroutinesInfo().forEach { - it.job?.cancel() - } - } - } - - private fun createTimeoutException(thread: Thread): Exception { - val stackTrace = thread.stackTrace - val exception = TestTimedOutException(testTimeoutMs, TimeUnit.MILLISECONDS) - exception.stackTrace = stackTrace - thread.interrupt() - return exception - } -} diff --git a/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutExtensionTest.kt b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutExtensionTest.kt new file mode 100644 index 0000000000..752c6c35cd --- /dev/null +++ b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutExtensionTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import kotlinx.coroutines.* +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.* +import org.junit.jupiter.api.parallel.* + +class CoroutinesTimeoutExtensionTest { + + /** + * Tests that disabling coroutine creation stacktraces in [CoroutinesTimeoutExtension] does lead to them not being + * created. + * + * Adapted from [CoroutinesTimeoutDisabledTracesTest], an identical test for the JUnit4 rule. + * + * This test class is not intended to be run manually. Instead, use [CoroutinesTimeoutTest] as the entry point. + */ + class DisabledStackTracesTest { + @JvmField + @RegisterExtension + internal val timeout = CoroutinesTimeoutExtension(500, true, false) + + private val job = GlobalScope.launch(Dispatchers.Unconfined) { hangForever() } + + private suspend fun hangForever() { + suspendCancellableCoroutine { } + expectUnreached() + } + + @Test + fun hangingTest() = runBlocking { + waitForHangJob() + expectUnreached() + } + + private suspend fun waitForHangJob() { + job.join() + expectUnreached() + } + } + + /** + * Tests that [CoroutinesTimeoutExtension] is installed eagerly and detects the coroutines that were launched before + * any test events start happening. + * + * Adapted from [CoroutinesTimeoutEagerTest], an identical test for the JUnit4 rule. + * + * This test class is not intended to be run manually. Instead, use [CoroutinesTimeoutTest] as the entry point. + */ + class EagerTest { + + @JvmField + @RegisterExtension + internal val timeout = CoroutinesTimeoutExtension(500) + + private val job = GlobalScope.launch(Dispatchers.Unconfined) { hangForever() } + + private suspend fun hangForever() { + suspendCancellableCoroutine { } + expectUnreached() + } + + @Test + fun hangingTest() = runBlocking { + waitForHangJob() + expectUnreached() + } + + private suspend fun waitForHangJob() { + job.join() + expectUnreached() + } + } + + /** + * Tests that [CoroutinesTimeoutExtension] performs sensibly in some simple scenarios. + * + * Adapted from [CoroutinesTimeoutTest], an identical test for the JUnit4 rule. + * + * This test class is not intended to be run manually. Instead, use [CoroutinesTimeoutTest] as the entry point. + */ + class SimpleTest { + + @JvmField + @RegisterExtension + internal val timeout = CoroutinesTimeoutExtension(1000, false, true) + + @Test + fun hangingTest() = runBlocking { + suspendForever() + expectUnreached() + } + + private suspend fun suspendForever() { + delay(Long.MAX_VALUE) + expectUnreached() + } + + @Test + fun throwingTest() = runBlocking { + throw RuntimeException() + } + + @Test + fun successfulTest() = runBlocking { + val job = launch { + yield() + } + + job.join() + } + } +} + +private fun expectUnreached(): Nothing { + error("Should not be reached") +} \ No newline at end of file diff --git a/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutInheritanceTest.kt b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutInheritanceTest.kt new file mode 100644 index 0000000000..7c8de53db7 --- /dev/null +++ b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutInheritanceTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import kotlinx.coroutines.* +import org.junit.jupiter.api.* + +/** + * Tests that [CoroutinesTimeout] is inherited. + * + * This test class is not intended to be run manually. Instead, use [CoroutinesTimeoutTest] as the entry point. + */ +class CoroutinesTimeoutInheritanceTest { + + @CoroutinesTimeout(100) + open class Base + + @TestMethodOrder(MethodOrderer.OrderAnnotation::class) + class InheritedWithNoTimeout: Base() { + + @Test + @Order(1) + fun usesBaseClassTimeout() = runBlocking { + delay(1000) + } + + @CoroutinesTimeout(300) + @Test + @Order(2) + fun methodOverridesBaseClassTimeoutWithGreaterTimeout() = runBlocking { + delay(200) + } + + @CoroutinesTimeout(10) + @Test + @Order(3) + fun methodOverridesBaseClassTimeoutWithLesserTimeout() = runBlocking { + delay(50) + } + + } + + @CoroutinesTimeout(300) + class InheritedWithGreaterTimeout : TestBase() { + + @Test + fun classOverridesBaseClassTimeout1() = runBlocking { + delay(200) + } + + @Test + fun classOverridesBaseClassTimeout2() = runBlocking { + delay(400) + } + + } + +} \ No newline at end of file diff --git a/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutMethodTest.kt b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutMethodTest.kt new file mode 100644 index 0000000000..64611b31e4 --- /dev/null +++ b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutMethodTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import kotlinx.coroutines.* +import org.junit.jupiter.api.* + +/** + * Tests usage of [CoroutinesTimeout] on classes and test methods when only methods are annotated. + * + * This test class is not intended to be run manually. Instead, use [CoroutinesTimeoutTest] as the entry point. + */ +@TestMethodOrder(MethodOrderer.OrderAnnotation::class) +class CoroutinesTimeoutMethodTest { + + @Test + @Order(1) + fun noClassTimeout() { + runBlocking { + delay(150) + } + } + + @CoroutinesTimeout(100) + @Test + @Order(2) + fun usesMethodTimeoutWithNoClassTimeout() { + runBlocking { + delay(1000) + } + } + + @CoroutinesTimeout(1000) + @Test + @Order(3) + fun fitsInMethodTimeout() { + runBlocking { + delay(10) + } + } + +} diff --git a/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutNestedTest.kt b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutNestedTest.kt new file mode 100644 index 0000000000..04c933d043 --- /dev/null +++ b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutNestedTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import kotlinx.coroutines.* +import org.junit.jupiter.api.* + +/** + * This test checks that nested classes correctly recognize the [CoroutinesTimeout] annotation. + * + * This test class is not intended to be run manually. Instead, use [CoroutinesTimeoutTest] as the entry point. + */ +@CoroutinesTimeout(200) +class CoroutinesTimeoutNestedTest { + @Nested + inner class NestedInInherited { + @Test + fun usesOuterClassTimeout() = runBlocking { + delay(1000) + } + + @Test + fun fitsInOuterClassTimeout() = runBlocking { + delay(10) + } + } +} diff --git a/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutSimpleTest.kt b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutSimpleTest.kt new file mode 100644 index 0000000000..513a884601 --- /dev/null +++ b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutSimpleTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import kotlinx.coroutines.* +import org.junit.jupiter.api.* + +/** + * Tests the basic usage of [CoroutinesTimeout] on classes and test methods. + * + * This test class is not intended to be run manually. Instead, use [CoroutinesTimeoutTest] as the entry point. + */ +@TestMethodOrder(MethodOrderer.OrderAnnotation::class) +@CoroutinesTimeout(100) +class CoroutinesTimeoutSimpleTest { + + @Test + @Order(1) + fun usesClassTimeout1() { + runBlocking { + delay(150) + } + } + + @CoroutinesTimeout(1000) + @Test + @Order(2) + fun ignoresClassTimeout() { + runBlocking { + delay(150) + } + } + + @CoroutinesTimeout(200) + @Test + @Order(3) + fun usesMethodTimeout() { + runBlocking { + delay(300) + } + } + + @Test + @Order(4) + fun fitsInClassTimeout() { + runBlocking { + delay(50) + } + } + + @Test + @Order(5) + fun usesClassTimeout2() { + runBlocking { + delay(150) + } + } + +} diff --git a/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutTest.kt b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutTest.kt new file mode 100644 index 0000000000..1f7b2080cb --- /dev/null +++ b/kotlinx-coroutines-debug/test/junit5/CoroutinesTimeoutTest.kt @@ -0,0 +1,170 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import org.assertj.core.api.* +import org.junit.Ignore +import org.junit.Assert.* +import org.junit.Test +import org.junit.platform.engine.* +import org.junit.platform.engine.discovery.DiscoverySelectors.* +import org.junit.platform.testkit.engine.* +import org.junit.platform.testkit.engine.EventConditions.* +import java.io.* + +// note that these tests are run using JUnit4 in order not to mix the testing systems. +class CoroutinesTimeoutTest { + + // This test is ignored because it just checks an example. + @Test + @Ignore + fun testRegisterExtensionExample() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(RegisterExtensionExample::class.java), capturedOut) + .testTimedOut("testThatHangs", 5000) + } + + @Test + fun testCoroutinesTimeoutSimple() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(CoroutinesTimeoutSimpleTest::class.java), capturedOut) + .testFinishedSuccessfully("ignoresClassTimeout") + .testFinishedSuccessfully("fitsInClassTimeout") + .testTimedOut("usesClassTimeout1", 100) + .testTimedOut("usesMethodTimeout", 200) + .testTimedOut("usesClassTimeout2", 100) + assertEquals(capturedOut.toString(), 3, countDumps(capturedOut)) + } + + @Test + fun testCoroutinesTimeoutMethod() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(CoroutinesTimeoutMethodTest::class.java), capturedOut) + .testFinishedSuccessfully("fitsInMethodTimeout") + .testFinishedSuccessfully("noClassTimeout") + .testTimedOut("usesMethodTimeoutWithNoClassTimeout", 100) + assertEquals(capturedOut.toString(), 1, countDumps(capturedOut)) + } + + @Test + fun testCoroutinesTimeoutNested() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(CoroutinesTimeoutNestedTest::class.java), capturedOut) + .testFinishedSuccessfully("fitsInOuterClassTimeout") + .testTimedOut("usesOuterClassTimeout", 200) + assertEquals(capturedOut.toString(), 1, countDumps(capturedOut)) + } + + @Test + fun testCoroutinesTimeoutInheritanceWithNoTimeoutInDerived() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(CoroutinesTimeoutInheritanceTest.InheritedWithNoTimeout::class.java), capturedOut) + .testFinishedSuccessfully("methodOverridesBaseClassTimeoutWithGreaterTimeout") + .testTimedOut("usesBaseClassTimeout", 100) + .testTimedOut("methodOverridesBaseClassTimeoutWithLesserTimeout", 10) + assertEquals(capturedOut.toString(), 2, countDumps(capturedOut)) + } + + @Test + fun testCoroutinesTimeoutInheritanceWithGreaterTimeoutInDerived() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector( + selectClass(CoroutinesTimeoutInheritanceTest.InheritedWithGreaterTimeout::class.java), + capturedOut + ) + .testFinishedSuccessfully("classOverridesBaseClassTimeout1") + .testTimedOut("classOverridesBaseClassTimeout2", 300) + assertEquals(capturedOut.toString(), 1, countDumps(capturedOut)) + } + + /* Currently there's no ability to replicate [TestFailureValidation] as is for JUnit5: + https://github.com/junit-team/junit5/issues/506. So, the test mechanism is more ad-hoc. */ + + @Test + fun testCoroutinesTimeoutExtensionDisabledTraces() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(CoroutinesTimeoutExtensionTest.DisabledStackTracesTest::class.java), capturedOut) + .testTimedOut("hangingTest", 500) + assertEquals(false, capturedOut.toString().contains("Coroutine creation stacktrace")) + assertEquals(capturedOut.toString(), 1, countDumps(capturedOut)) + } + + @Test + fun testCoroutinesTimeoutExtensionEager() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(CoroutinesTimeoutExtensionTest.EagerTest::class.java), capturedOut) + .testTimedOut("hangingTest", 500) + for (expectedPart in listOf("hangForever", "waitForHangJob", "BlockingCoroutine{Active}")) { + assertEquals(expectedPart, true, capturedOut.toString().contains(expectedPart)) + } + assertEquals(capturedOut.toString(), 1, countDumps(capturedOut)) + } + + @Test + fun testCoroutinesTimeoutExtensionSimple() { + val capturedOut = ByteArrayOutputStream() + eventsForSelector(selectClass(CoroutinesTimeoutExtensionTest.SimpleTest::class.java), capturedOut) + .testFinishedSuccessfully("successfulTest") + .testTimedOut("hangingTest", 1000) + .haveExactly(1, event( + test("throwingTest"), + finishedWithFailure(Condition({ it is RuntimeException}, "is RuntimeException")) + )) + for (expectedPart in listOf("suspendForever", "invokeSuspend", "BlockingCoroutine{Active}")) { + assertEquals(expectedPart, true, capturedOut.toString().contains(expectedPart)) + } + for (nonExpectedPart in listOf("delay", "throwingTest")) { + assertEquals(nonExpectedPart, false, capturedOut.toString().contains(nonExpectedPart)) + } + assertEquals(capturedOut.toString(), 1, countDumps(capturedOut)) + } +} + +private fun eventsForSelector(selector: DiscoverySelector, capturedOut: OutputStream): ListAssert { + val systemOut: PrintStream = System.out + val systemErr: PrintStream = System.err + return try { + System.setOut(PrintStream(capturedOut)) + System.setErr(PrintStream(capturedOut)) + EngineTestKit.engine("junit-jupiter") + .selectors(selector) + .execute() + .testEvents() + .assertThatEvents() + } finally { + System.setOut(systemOut) + System.setErr(systemErr) + } +} + +private fun ListAssert.testFinishedSuccessfully(testName: String): ListAssert = + haveExactly(1, event( + test(testName), + finishedSuccessfully() + )) + +private fun ListAssert.testTimedOut(testName: String, after: Long): ListAssert = + haveExactly(1, event( + test(testName), + finishedWithFailure(Condition({ it is CoroutinesTimeoutException && it.timeoutMs == after }, + "is CoroutinesTimeoutException($after)")) + )) + +/** Counts the number of occurrences of "Coroutines dump" in [capturedOut] */ +private fun countDumps(capturedOut: ByteArrayOutputStream): Int { + var result = 0 + val outStr = capturedOut.toString() + val header = "Coroutines dump" + var i = 0 + while (i < outStr.length - header.length) { + if (outStr.substring(i, i + header.length) == header) { + result += 1 + i += header.length + } else { + i += 1 + } + } + return result +} \ No newline at end of file diff --git a/kotlinx-coroutines-debug/test/junit5/RegisterExtensionExample.kt b/kotlinx-coroutines-debug/test/junit5/RegisterExtensionExample.kt new file mode 100644 index 0000000000..2de6b5b289 --- /dev/null +++ b/kotlinx-coroutines-debug/test/junit5/RegisterExtensionExample.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.coroutines.debug.junit5 + +import kotlinx.coroutines.* +import org.junit.jupiter.api.* +import org.junit.jupiter.api.extension.* + +class RegisterExtensionExample { + @JvmField + @RegisterExtension + internal val timeout = CoroutinesTimeoutExtension.seconds(5) + + @Test + fun testThatHangs() = runBlocking { + delay(Long.MAX_VALUE) // somewhere deep in the stack + } +} \ No newline at end of file From 47a063c0987177551bdbdf09a458998a30571ac2 Mon Sep 17 00:00:00 2001 From: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> Date: Fri, 23 Apr 2021 18:24:20 +0300 Subject: [PATCH 53/55] Remove the @ExperimentalCoroutinesApi annotation from reactive integrations (#2670) --- reactive/kotlinx-coroutines-jdk9/src/Publish.kt | 1 - reactive/kotlinx-coroutines-reactive/src/Publish.kt | 1 - reactive/kotlinx-coroutines-reactor/src/Convert.kt | 2 -- reactive/kotlinx-coroutines-reactor/src/Flux.kt | 1 - reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt | 3 --- reactive/kotlinx-coroutines-rx2/src/RxConvert.kt | 6 ------ reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt | 1 - reactive/kotlinx-coroutines-rx2/src/RxObservable.kt | 1 - reactive/kotlinx-coroutines-rx3/src/RxConvert.kt | 6 ------ reactive/kotlinx-coroutines-rx3/src/RxFlowable.kt | 1 - reactive/kotlinx-coroutines-rx3/src/RxObservable.kt | 1 - 11 files changed, 24 deletions(-) diff --git a/reactive/kotlinx-coroutines-jdk9/src/Publish.kt b/reactive/kotlinx-coroutines-jdk9/src/Publish.kt index cfd18d2b7a..529bc12fdc 100644 --- a/reactive/kotlinx-coroutines-jdk9/src/Publish.kt +++ b/reactive/kotlinx-coroutines-jdk9/src/Publish.kt @@ -32,7 +32,6 @@ import org.reactivestreams.FlowAdapters * * @throws IllegalArgumentException if the provided [context] contains a [Job] instance. */ -@ExperimentalCoroutinesApi public fun flowPublish( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit diff --git a/reactive/kotlinx-coroutines-reactive/src/Publish.kt b/reactive/kotlinx-coroutines-reactive/src/Publish.kt index 8b94b95986..7ebe269436 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Publish.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Publish.kt @@ -32,7 +32,6 @@ import kotlin.coroutines.* * * @throws IllegalArgumentException if the provided [context] contains a [Job] instance. */ -@ExperimentalCoroutinesApi public fun publish( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit diff --git a/reactive/kotlinx-coroutines-reactor/src/Convert.kt b/reactive/kotlinx-coroutines-reactor/src/Convert.kt index 002baa6185..73cc336d0d 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Convert.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Convert.kt @@ -21,7 +21,6 @@ import kotlin.coroutines.* * * @param context -- the coroutine context from which the resulting mono is going to be signalled */ -@ExperimentalCoroutinesApi public fun Job.asMono(context: CoroutineContext): Mono = mono(context) { this@asMono.join() } /** * Converts this deferred value to the hot reactive mono that signals @@ -35,7 +34,6 @@ public fun Job.asMono(context: CoroutineContext): Mono = mono(context) { t * * @param context -- the coroutine context from which the resulting mono is going to be signalled */ -@ExperimentalCoroutinesApi public fun Deferred.asMono(context: CoroutineContext): Mono = mono(context) { this@asMono.await() } /** diff --git a/reactive/kotlinx-coroutines-reactor/src/Flux.kt b/reactive/kotlinx-coroutines-reactor/src/Flux.kt index df5f64f262..63168a443b 100644 --- a/reactive/kotlinx-coroutines-reactor/src/Flux.kt +++ b/reactive/kotlinx-coroutines-reactor/src/Flux.kt @@ -29,7 +29,6 @@ import kotlin.coroutines.* * * @throws IllegalArgumentException if the provided [context] contains a [Job] instance. */ -@ExperimentalCoroutinesApi public fun flux( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt index 8969662adc..c4370afa89 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorContext.kt @@ -49,7 +49,6 @@ import reactor.util.context.* * .subscribe() // Will print "Reactor context in Flow: Context{'answer'=42}" * ``` */ -@ExperimentalCoroutinesApi public class ReactorContext(public val context: Context) : AbstractCoroutineContextElement(ReactorContext) { // `Context.of` is zero-cost if the argument is a `Context` @@ -62,14 +61,12 @@ public class ReactorContext(public val context: Context) : AbstractCoroutineCont * Wraps the given [ContextView] into [ReactorContext], so it can be added to the coroutine's context * and later used via `coroutineContext[ReactorContext]`. */ -@ExperimentalCoroutinesApi public fun ContextView.asCoroutineContext(): ReactorContext = ReactorContext(this) /** * Wraps the given [Context] into [ReactorContext], so it can be added to the coroutine's context * and later used via `coroutineContext[ReactorContext]`. */ -@ExperimentalCoroutinesApi @Deprecated("The more general version for ContextView should be used instead", level = DeprecationLevel.HIDDEN) public fun Context.asCoroutineContext(): ReactorContext = readOnly().asCoroutineContext() // `readOnly()` is zero-cost. diff --git a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt index a64e6d02ed..2aeb994de1 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxConvert.kt @@ -26,7 +26,6 @@ import kotlin.coroutines.* * * @param context -- the coroutine context from which the resulting completable is going to be signalled */ -@ExperimentalCoroutinesApi public fun Job.asCompletable(context: CoroutineContext): Completable = rxCompletable(context) { this@asCompletable.join() } @@ -43,7 +42,6 @@ public fun Job.asCompletable(context: CoroutineContext): Completable = rxComplet * * @param context -- the coroutine context from which the resulting maybe is going to be signalled */ -@ExperimentalCoroutinesApi public fun Deferred.asMaybe(context: CoroutineContext): Maybe = rxMaybe(context) { this@asMaybe.await() } @@ -60,7 +58,6 @@ public fun Deferred.asMaybe(context: CoroutineContext): Maybe = rxMay * * @param context -- the coroutine context from which the resulting single is going to be signalled */ -@ExperimentalCoroutinesApi public fun Deferred.asSingle(context: CoroutineContext): Single = rxSingle(context) { this@asSingle.await() } @@ -75,7 +72,6 @@ public fun Deferred.asSingle(context: CoroutineContext): Single * resulting flow to specify a user-defined value and to control what happens when data is produced faster * than consumed, i.e. to control the back-pressure behavior. Check [callbackFlow] for more details. */ -@ExperimentalCoroutinesApi public fun ObservableSource.asFlow(): Flow = callbackFlow { val disposableRef = AtomicReference() val observer = object : Observer { @@ -108,7 +104,6 @@ public fun ObservableSource.asFlow(): Flow = callbackFlow { * inject additional context into the caller thread. By default, the [Unconfined][Dispatchers.Unconfined] dispatcher * is used, so calls are performed from an arbitrary thread. */ -@ExperimentalCoroutinesApi public fun Flow.asObservable(context: CoroutineContext = EmptyCoroutineContext) : Observable = Observable.create { emitter -> /* * ATOMIC is used here to provide stable behaviour of subscribe+dispose pair even if @@ -141,7 +136,6 @@ public fun Flow.asObservable(context: CoroutineContext = EmptyCorout * inject additional context into the caller thread. By default, the [Unconfined][Dispatchers.Unconfined] dispatcher * is used, so calls are performed from an arbitrary thread. */ -@ExperimentalCoroutinesApi public fun Flow.asFlowable(context: CoroutineContext = EmptyCoroutineContext): Flowable = Flowable.fromPublisher(asPublisher(context)) diff --git a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt index 6b299437aa..f3ae65aadf 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxFlowable.kt @@ -31,7 +31,6 @@ import kotlin.internal.* * * **Note: This is an experimental api.** Behaviour of publishers that work as children in a parent scope with respect */ -@ExperimentalCoroutinesApi public fun rxFlowable( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit diff --git a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt index c096c0d254..7300b484c9 100644 --- a/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx2/src/RxObservable.kt @@ -30,7 +30,6 @@ import kotlin.coroutines.* * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used. * Method throws [IllegalArgumentException] if provided [context] contains a [Job] instance. */ -@ExperimentalCoroutinesApi public fun rxObservable( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit diff --git a/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt b/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt index b0ba4ab2e0..dfe4c055f8 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxConvert.kt @@ -26,7 +26,6 @@ import kotlin.coroutines.* * * @param context -- the coroutine context from which the resulting completable is going to be signalled */ -@ExperimentalCoroutinesApi public fun Job.asCompletable(context: CoroutineContext): Completable = rxCompletable(context) { this@asCompletable.join() } @@ -43,7 +42,6 @@ public fun Job.asCompletable(context: CoroutineContext): Completable = rxComplet * * @param context -- the coroutine context from which the resulting maybe is going to be signalled */ -@ExperimentalCoroutinesApi public fun Deferred.asMaybe(context: CoroutineContext): Maybe = rxMaybe(context) { this@asMaybe.await() } @@ -60,7 +58,6 @@ public fun Deferred.asMaybe(context: CoroutineContext): Maybe = rxMay * * @param context -- the coroutine context from which the resulting single is going to be signalled */ -@ExperimentalCoroutinesApi public fun Deferred.asSingle(context: CoroutineContext): Single = rxSingle(context) { this@asSingle.await() } @@ -75,7 +72,6 @@ public fun Deferred.asSingle(context: CoroutineContext): Single * resulting flow to specify a user-defined value and to control what happens when data is produced faster * than consumed, i.e. to control the back-pressure behavior. Check [callbackFlow] for more details. */ -@ExperimentalCoroutinesApi public fun ObservableSource.asFlow(): Flow = callbackFlow { val disposableRef = AtomicReference() val observer = object : Observer { @@ -108,7 +104,6 @@ public fun ObservableSource.asFlow(): Flow = callbackFlow { * inject additional context into the caller thread. By default, the [Unconfined][Dispatchers.Unconfined] dispatcher * is used, so calls are performed from an arbitrary thread. */ -@ExperimentalCoroutinesApi public fun Flow.asObservable(context: CoroutineContext = EmptyCoroutineContext) : Observable = Observable.create { emitter -> /* * ATOMIC is used here to provide stable behaviour of subscribe+dispose pair even if @@ -141,7 +136,6 @@ public fun Flow.asObservable(context: CoroutineContext = EmptyCorout * inject additional context into the caller thread. By default, the [Unconfined][Dispatchers.Unconfined] dispatcher * is used, so calls are performed from an arbitrary thread. */ -@ExperimentalCoroutinesApi public fun Flow.asFlowable(context: CoroutineContext = EmptyCoroutineContext): Flowable = Flowable.fromPublisher(asPublisher(context)) diff --git a/reactive/kotlinx-coroutines-rx3/src/RxFlowable.kt b/reactive/kotlinx-coroutines-rx3/src/RxFlowable.kt index 445a61401f..9357f2834a 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxFlowable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxFlowable.kt @@ -28,7 +28,6 @@ import kotlin.coroutines.* * * **Note: This is an experimental api.** Behaviour of publishers that work as children in a parent scope with respect */ -@ExperimentalCoroutinesApi public fun rxFlowable( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit diff --git a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt index 5c810c498d..57007bbdd4 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxObservable.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.internal.* * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used. * Method throws [IllegalArgumentException] if provided [context] contains a [Job] instance. */ -@ExperimentalCoroutinesApi public fun rxObservable( context: CoroutineContext = EmptyCoroutineContext, @BuilderInference block: suspend ProducerScope.() -> Unit From e61ef4bb731932bb61f3bbbe6781d15bd92deda5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 26 Apr 2021 19:06:33 +0300 Subject: [PATCH 54/55] Update Kotlin to 1.5.0 (#2674) * Update Kotlin to 1.5.0 * Update atomicfu to 0.16.1 Co-authored-by: SokolovaMaria --- README.md | 4 ++-- gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 376b5a68f4..6a38dd4c07 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ [![official JetBrains 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.4.3)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.4.3/pom) -[![Kotlin](https://img.shields.io/badge/kotlin-1.4.30-blue.svg?logo=kotlin)](http://kotlinlang.org) +[![Kotlin](https://img.shields.io/badge/kotlin-1.5.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.4.30` release. +This is a companion version for the Kotlin `1.5.0` release. ```kotlin suspend fun main() = coroutineScope { diff --git a/gradle.properties b/gradle.properties index 15935ab114..ef7c01ef37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,12 +5,12 @@ # Kotlin version=1.4.3-SNAPSHOT group=org.jetbrains.kotlinx -kotlin_version=1.5.0-RC +kotlin_version=1.5.0 # Dependencies junit_version=4.12 junit5_version=5.7.0 -atomicfu_version=0.15.2 +atomicfu_version=0.16.1 knit_version=0.2.3 html_version=0.7.2 lincheck_version=2.12 From 497312e95a0a28b68f064debc6097a2a3da1dddf Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 26 Apr 2021 19:18:59 +0300 Subject: [PATCH 55/55] Version 1.5.0-RC --- CHANGES.md | 32 ++++++++++++++++++++++++++++++ README.md | 20 +++++++++---------- gradle.properties | 2 +- kotlinx-coroutines-debug/README.md | 2 +- kotlinx-coroutines-test/README.md | 2 +- ui/coroutines-guide-ui.md | 2 +- 6 files changed, 46 insertions(+), 14 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 089b1991b3..25769073e7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,37 @@ # Change log for kotlinx.coroutines +## Version 1.5.0-RC + +### Channels API + +* Major channels API rework (#330, #974). Existing `offer`, `poll`, and `sendBlocking` methods are deprecated, internal `receiveCatching` and `onReceiveCatching` removed, `receiveOrNull` and `onReceiveOrNull` are completely deprecated. Previously deprecated `SendChannel.isFull` declaration is removed. Channel operators deprecated with `ERROR` are now `HIDDEN`. +* New methods `receiveCatching`, `onReceiveCatching` `trySend`, `tryReceive`, and `trySendBlocking` along with the new result type `ChannelResult` are introduced. They provide better type safety, are less error-prone, and have a consistent future-proof naming scheme. The full rationale behind this change can be found [here](https://github.com/Kotlin/kotlinx.coroutines/issues/974#issuecomment-806569582). +* `BroadcastChannel` and `ConflatedBroadcastChannel` are marked as `ObsoleteCoroutinesApi` in the favor or `SharedFlow` and `StateFlow`. The migration scheme can be found in their documentation. These classes will be deprecated in the next major release. +* `callbackFlow` and `channelFlow` are promoted to stable API. + +### Reactive integrations + +* All existing API in modules `kotlinx-coroutines-rx2`, `kotlinx-coroutines-rx3`, `kotlinx-coroutines-reactive`, `kotlinx-coroutines-reactor`, and `kotlinx-coroutines-jdk9` were revisited and promoted to stable (#2545). +* `publish` is no longer allowed to emit `null` values (#2646). +* Misleading `awaitSingleOr*` functions on `Publisher` type are deprecated (#2591). +* `MaybeSource.await` is deprecated in the favor of `awaitSingle`, additional lint functions for `Mono` are added in order to prevent ambiguous `Publisher` usages (#2628, #1587). +* `ContextView` support in `kotlinx-coroutines-reactor` (#2575). +* All reactive builders no longer ignore inner cancellation exceptions preventing their completion (#2262, #2646). +* `MaybeSource.collect` and `Maybe.collect` properly finish when they are completed without a value (#2617). +* All exceptions are now consistently handled according to reactive specification, whether they are considered 'fatal' or not by reactive frameworks (#2646). + +### Other improvements + +* `Flow.last` and `Flow.lastOrNull` operators (#2246). +* `Flow.runningFold` operator (#2641). +* `CoroutinesTimeout` rule for JUnit5 (#2197). +* Internals of `Job` and `AbstractCoroutine` was reworked, resulting in smaller code size, less memory footprint, and better performance (#2513, #2512). +* `CancellationException` from Kotlin standard library is used for cancellation on Koltin/JS and Kotlin/Native (#2638). +* Introduced new `DelicateCoroutineApi` annotation that warns users about potential target API pitfalls and suggests studying API's documentation first. The only delicate API right now is `GlobalScope` (#2637). +* Fixed bug introduced in `1.4.3` when `kotlinx-coroutines-core.jar` triggered IDEA debugger failure (#2619). +* Fixed memory leak of `ChildHandlerNode` with reusable continuations (#2564). +* Various documentation improvements (#2555, #2589, #2592, #2583, #2437, #2616, #2633, #2560). + ## Version 1.4.3 ### General changes diff --git a/README.md b/README.md index 6a38dd4c07..76adb21241 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![official JetBrains 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.4.3)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.4.3/pom) +[![Download](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0-RC)](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.5.0-RC/pom) [![Kotlin](https://img.shields.io/badge/kotlin-1.5.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/) @@ -83,7 +83,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-coroutines-core - 1.4.3 + 1.5.0-RC ``` @@ -91,7 +91,7 @@ And make sure that you use the latest Kotlin version: ```xml - 1.4.30 + 1.5.0 ``` @@ -101,7 +101,7 @@ Add dependencies (you can also add other modules that you need): ```groovy dependencies { - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC' } ``` @@ -109,7 +109,7 @@ And make sure that you use the latest Kotlin version: ```groovy buildscript { - ext.kotlin_version = '1.4.30' + ext.kotlin_version = '1.5.0' } ``` @@ -127,7 +127,7 @@ Add dependencies (you can also add other modules that you need): ```groovy dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC") } ``` @@ -135,7 +135,7 @@ And make sure that you use the latest Kotlin version: ```groovy plugins { - kotlin("jvm") version "1.4.30" + kotlin("jvm") version "1.5.0" } ``` @@ -147,7 +147,7 @@ Add [`kotlinx-coroutines-android`](ui/kotlinx-coroutines-android) module as a dependency when using `kotlinx.coroutines` on Android: ```groovy -implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3' +implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC' ``` This gives you access to the Android [Dispatchers.Main] @@ -180,7 +180,7 @@ In common code that should get compiled for different platforms, you can add a d ```groovy commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC") } } ``` @@ -192,7 +192,7 @@ Platform-specific dependencies are recommended to be used only for non-multiplat #### JS Kotlin/JS version of `kotlinx.coroutines` is published as -[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.4.3/jar) +[`kotlinx-coroutines-core-js`](https://search.maven.org/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-js/1.5.0-RC/jar) (follow the link to get the dependency declaration snippet) and as [`kotlinx-coroutines-core`](https://www.npmjs.com/package/kotlinx-coroutines-core) NPM package. #### Native diff --git a/gradle.properties b/gradle.properties index ef7c01ef37..be8d5fa670 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # # Kotlin -version=1.4.3-SNAPSHOT +version=1.5.0-RC-SNAPSHOT group=org.jetbrains.kotlinx kotlin_version=1.5.0 diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index 04dfa8a9fb..6748a2f3df 100644 --- a/kotlinx-coroutines-debug/README.md +++ b/kotlinx-coroutines-debug/README.md @@ -61,7 +61,7 @@ stacktraces will be dumped to the console. ### 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.4.3.jar`. +You can run your application with an additional argument: `-javaagent:kotlinx-coroutines-debug-1.5.0-RC.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-test/README.md b/kotlinx-coroutines-test/README.md index dd18d96662..385afecc07 100644 --- a/kotlinx-coroutines-test/README.md +++ b/kotlinx-coroutines-test/README.md @@ -9,7 +9,7 @@ This package provides testing utilities for effectively testing coroutines. Add `kotlinx-coroutines-test` to your project test dependencies: ``` dependencies { - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.0-RC' } ``` diff --git a/ui/coroutines-guide-ui.md b/ui/coroutines-guide-ui.md index d5bd2320b7..3fcee41901 100644 --- a/ui/coroutines-guide-ui.md +++ b/ui/coroutines-guide-ui.md @@ -110,7 +110,7 @@ Add dependencies on `kotlinx-coroutines-android` module to the `dependencies { . `app/build.gradle` file: ```groovy -implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3" +implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC" ``` You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your