Hello! I have a question about Channel and it’s c...
# coroutines
d
Hello! I have a question about Channel and it’s cancel function. I saw that calling *cancel*() function with already suspended *send*() - will trigger cancellation exception. if the documentation is correct:
Copy code
Cancels reception of remaining elements from this channel with an optional cause. This function closes the channel and removes all buffered sent elements from it.
it’s just close with cancellation exception with removal of buffered items. But calling *close*() will not trigger any exceptions for suspended *send*(), what is correct according to documentation:
Copy code
Closing a channel after this function has suspended does not cause this suspended send invocation to abort, because closing a channel is conceptually like sending a special "close token" over this channel. All elements sent over the channel are delivered in first-in first-out order. The sent element will be delivered to receivers before the close token.
Could somebody explain me this cancel method please?
s
The
close()
method is part of the
SendChannel
interface, and is designed to be used by producers that are delivering values to the channel. It signals that the producer won't send any more values. But any values that have already been sent, or that are in the process of being sent, will still be delivered. The
cancel()
method is part of the
ReceiveChannel
interface, and is designed to be used by consumers that are receiving values from the channel. It signals that the consumer has gone away and won't consume any more values. It discards any values that haven't yet been received, because there would be no way to continue delivering values when there's no consumer to receive them.
Both functions are optional—you can use one, both or neither, depending on how you want to use your channel.
d
Thank you for your reply as always!
Copy code
val t = Channel<Int>(capacity = 0) { element ->
    println("Clean up element ${element}")
}

fun main() = runBlocking<Unit> { //sampleStart
    
    withTimeout(500) {
        val s = launch(newSingleThreadContext("S Coroutine")) {
        println("I'm inside === ${Thread.currentThread()}")
        delay(50)
        println("Before send === ${Thread.currentThread()}")
        try {
            t.send(4)
        } catch(e: Throwable) {
            println("Send finished with ${e} === ${Thread.currentThread()}")
            println(t.isClosedForReceive)
        }
        println("Finished send")
    }
    
    launch(newSingleThreadContext("Cancel Coroutine")) {
        println("Before cancel === ${Thread.currentThread()}")
        delay(90)
        println("Will cancel === ${Thread.currentThread()}")
        t.cancel()
        println(t.isClosedForSend)
    }
    
    launch(newSingleThreadContext("Check Coroutine")) {
        println("Before receive delay === ${Thread.currentThread()}")
        delay(150)
        println("Before receive === ${Thread.currentThread()}")
        try {
            val value1 = t.receive()
            
            println("Receive finished with ${value1} === ${Thread.currentThread()}")
            println(t.isClosedForReceive)
        } catch(e: Throwable) {
            println("Receive finished with ${e} === ${Thread.currentThread()}")
        }
        println("Received finished === ${Thread.currentThread()}")
    }
    }

//sampleEnd
}
cancel will make send() to throw CancellationExceeption, but close will not and value would be delivered. According to docs cancel use close and remove buffered elements, but why the behaviour is different? Thanks
s
I don't think
cancel()
actually calls
close()
internally. When the docs say that cancel "closes" the channel, I think that just means that it puts the channel into a "closed" state. That means that, for example,
isClosedForSend
will return
true
and new attempts to
send
will fail. Both
close()
and
cancel()
put the channel into this state, but they do it in different ways.
d
Thank you very much!