From 8f7c3b3682c42b1b9a32b71d24bda332845cf4d7 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 10 Dec 2021 10:44:33 +0300 Subject: [PATCH] Make trySendBlocking available on K/N (#3064) Move trySendBlocking from the JVM to the concurrent source-set to make it available on K/N. Fixes #2983 --- .../src/channels/Channels.kt | 76 +++++-------------- .../test/channels/TrySendBlockingTest.kt} | 7 +- 2 files changed, 23 insertions(+), 60 deletions(-) rename kotlinx-coroutines-core/{jvm => concurrent}/src/channels/Channels.kt (55%) rename kotlinx-coroutines-core/{jvm/test/channels/ChannelsJvmTest.kt => concurrent/test/channels/TrySendBlockingTest.kt} (89%) diff --git a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt b/kotlinx-coroutines-core/concurrent/src/channels/Channels.kt similarity index 55% rename from kotlinx-coroutines-core/jvm/src/channels/Channels.kt rename to kotlinx-coroutines-core/concurrent/src/channels/Channels.kt index d7454027fb..24422b5a6b 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Channels.kt +++ b/kotlinx-coroutines-core/concurrent/src/channels/Channels.kt @@ -1,65 +1,13 @@ -/* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - @file:JvmMultifileClass @file:JvmName("ChannelsKt") package kotlinx.coroutines.channels import kotlinx.coroutines.* +import kotlin.jvm.* /** - * **Deprecated** blocking variant of send. - * This method is deprecated in the favour of [trySendBlocking]. - * - * `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.ERROR, - message = "Deprecated in the favour of 'trySendBlocking'. " + - "Consider handling the result of 'trySendBlocking' explicitly and rethrow exception if necessary", - replaceWith = ReplaceWith("trySendBlocking(element)") -) // WARNING in 1.5.0, ERROR in 1.6.0, HIDDEN in 1.7.0 -public fun SendChannel.sendBlocking(element: E) { - // fast path - if (trySend(element).isSuccess) - return - // slow path - runBlocking { - send(element) - } -} - -/** - * Adds [element] into to this channel, **blocking** the caller while this channel is full, + * Adds [element] 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. * @@ -77,9 +25,8 @@ public fun SendChannel.sendBlocking(element: E) { * * 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` on JVM 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. @@ -94,3 +41,20 @@ public fun SendChannel.trySendBlocking(element: E): ChannelResult { else ChannelResult.closed(r.exceptionOrNull()) } } + +/** @suppress */ +@Deprecated( + level = DeprecationLevel.ERROR, + message = "Deprecated in the favour of 'trySendBlocking'. " + + "Consider handling the result of 'trySendBlocking' explicitly and rethrow exception if necessary", + replaceWith = ReplaceWith("trySendBlocking(element)") +) // WARNING in 1.5.0, ERROR in 1.6.0, HIDDEN in 1.7.0 +public fun SendChannel.sendBlocking(element: E) { + // fast path + if (trySend(element).isSuccess) + return + // slow path + runBlocking { + send(element) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt b/kotlinx-coroutines-core/concurrent/test/channels/TrySendBlockingTest.kt similarity index 89% rename from kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt rename to kotlinx-coroutines-core/concurrent/test/channels/TrySendBlockingTest.kt index 8512aebcc0..77c651835a 100644 --- a/kotlinx-coroutines-core/jvm/test/channels/ChannelsJvmTest.kt +++ b/kotlinx-coroutines-core/concurrent/test/channels/TrySendBlockingTest.kt @@ -1,17 +1,16 @@ /* - * 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 import kotlinx.coroutines.* -import org.junit.Test import kotlin.test.* -class ChannelsJvmTest : TestBase() { +class TrySendBlockingTest : TestBase() { @Test - fun testTrySendBlocking() { + fun testTrySendBlocking() = runBlocking { // For old MM val ch = Channel() val sum = GlobalScope.async { var sum = 0