Skip to content

Commit

Permalink
Make trySendBlocking available on K/N (Kotlin#3064)
Browse files Browse the repository at this point in the history
Move trySendBlocking from the JVM to the concurrent source-set
to make it available on K/N.

Fixes Kotlin#2983
  • Loading branch information
qwwdfsad authored and pablobaxter committed Sep 14, 2022
1 parent d5e3ae9 commit 8f7c3b3
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 60 deletions.
@@ -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 <E> SendChannel<E>.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.
*
Expand All @@ -77,9 +25,8 @@ public fun <E> SendChannel<E>.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 <E> SendChannel<E>.trySendBlocking(element: E): ChannelResult<Unit> {
/*
* Sent successfully -- bail out.
Expand All @@ -94,3 +41,20 @@ public fun <E> SendChannel<E>.trySendBlocking(element: E): ChannelResult<Unit> {
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 <E> SendChannel<E>.sendBlocking(element: E) {
// fast path
if (trySend(element).isSuccess)
return
// slow path
runBlocking {
send(element)
}
}
@@ -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<Unit> { // For old MM
val ch = Channel<Int>()
val sum = GlobalScope.async {
var sum = 0
Expand Down

0 comments on commit 8f7c3b3

Please sign in to comment.