Search code examples
kotlinkotlinx.coroutines

Ignoring offers to coroutine channels after closing


Is there a good way to have channels ignore offers once closed without throwing an exception?

Currently, it seems like only try catch would work, as isClosedForSend isn't atomic.

Alternatively, is there a problem if I just never close a channel at all? For my specific use case, I'm using channels as an alternative to Android livedata (as I don't need any of the benefits beyond sending values from any thread and listening from the main thread). In that case, I could listen to the channel through a producer that only sends values when I want to, and simply ignore all other inputs.

Ideally, I'd have a solution where the ReceiveChannel can still finish listening, but where SendChannel will never crash when offered a new value.


Solution

  • Channels throw this exception by design, as means of correct communication.

    If you absolutely must have something like this, you can use an extension function of this sort:

    private suspend fun <E> Channel<E>.sendOrNothing(e: E) {
        try {
            this.send(e)
        }
        catch (closedException: ClosedSendChannelException) {
            println("It's fine")
        }
    }
    

    You can test it with the following piece of code:

    val channel = Channel<Int>(capacity = 3)
        launch {
    
            try {
                for (i in 1..10) {
                    channel.sendOrNothing(i)
                    delay(50)
                    if (i == 5) {
                        channel.close()
                    }
                }
    
                println("Done")
            }
            catch (e: Exception) {
                e.printStackTrace()
            }
            finally {
                println("Finally")
            }
        }
    
        launch {
            for (c in channel) {
                println(c)
                delay(300)
            }
        }
    

    As you'll notice, producer will start printing "It's fine" since the channel is closed, but consumer will still be able to read first 5 values.

    Regarding your second question: it depends.

    Channels don't have such a big overhead, and neither do suspended coroutines. But a leak is a leak, you know.