onReceiveOrClosed: Already resumed

2k views Asked by At

Take the following program:

package example

import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.selects.select

@InternalCoroutinesApi
fun main() {
    runBlocking {
        val chan = Channel<Unit>()
        chan.close()
        select<Unit> {
            println("Register onReceiveOrClosed.")
            chan.onReceiveOrClosed {
                println("Selected value $it.")
            }
        }
        println("Done.")
    }
}

Running it gives this output:

Register onReceiveOrClosed.
Selected value Closed(null).
Selected value Closed(null).
Exception in thread "main" java.lang.IllegalStateException: Already resumed
    at kotlinx.coroutines.selects.SelectBuilderImpl.resumeWith(Select.kt:458)
    at kotlinx.coroutines.selects.SelectBuilderImpl.handleBuilderException(Select.kt:309)
    at example.ExampleKt$main$1.invokeSuspend(example.kt:28)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at example.ExampleKt.main(example.kt:10)
    at example.ExampleKt.main(example.kt)

I would expect to see just one line Selected value Closed(null) and I would expect to see no exception (but of course the exception makes sense given that the block given to onReceiveOrClosed is executed twice).

Is my understanding of onReceiveOrClosed incorrect or is this a bug with onReceiveOrClosed?

I'm using Kotlin 1.3.50 and kotlinx-coroutines-core:1.3.1. The full example is available here: https://github.com/frececroka/kotlin-select-onreceiveorclosed.

1

There are 1 answers

2
Andrei Tanana On

I think it's because when you call select the channel is already closed. If you add some delay, it will work fine:

@InternalCoroutinesApi
fun main() {
    runBlocking {
        val chan = Channel<Unit>()
        launch {
            chan.close()
        }
        select<Unit> {
            println("Register onReceiveOrClosed.")
            chan.onReceiveOrClosed {
                println("Selected value $it.")
            }
        }
        println("Done.")
    }
}

// prints
// Register onReceiveOrClosed.
// Selected value Closed(null).
// Done.

I'm not sure is this a bug or intended behavior