Closing Channel
Traits:
- Channel.send() will throw exception if channel is closed.
- Channel.receive() will throw exception if channel is closed.
- Receiver will get out of for loop if channel is closed.
In Action
GIVEN channel is closed:
- WHEN sender attempts to write THEN exception is thrown on sender
- WHEN receiver uses for loop on channel THEN Receiver gets out of for loop.
Code
package com.glassthought.sandbox
import com.glassthought.sandbox.util.out.impl.OutSettings
import gt.sandbox.util.output.Out
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
val out = Out.standard(OutSettings(printCoroutineName = true, printThreadInfo = false))
fun main(args: Array<String>) {
runBlocking {
val channel = Channel<Int>(Channel.UNLIMITED)
val sender = launch(CoroutineName("${Emoji.LETTER}-sender")) {
try {
repeat(10) {
delay(500)
out.info("starting_to_send: $it")
channel.send(it)
out.info("sent: $it")
}
} catch (e: Exception) {
out.infoRed("sender exception: $e")
}
}
val listener = launch(CoroutineName("${Emoji.MAILBOX}-listener")) {
try {
for (i in channel) {
out.info("received: $i")
}
out.info("Listener Done - ${Emoji.CHECKERED_FLAG}")
} catch (e: Exception) {
out.infoRed("listener exception: $e")
}
}
delay(1700)
out.info("Closing channel from Main")
channel.close()
delay(2000)
out.info("Main completed. Exiting...")
System.exit(0)
}
}
class Emoji {
companion object {
const val LETTER = "✉\uFE0F"
const val MAILBOX = "\uD83D\uDCEC"
const val CHECKERED_FLAG = "\uD83C\uDFC1"
}
}
Command to reproduce:
gt.sandbox.checkout.commit 58e15651b7326dc65946 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"
Recorded output of command:
[elapsed: 559ms][coroutine:✉️-sender] starting_to_send: 0
[elapsed: 583ms][coroutine:✉️-sender] sent: 0
[elapsed: 583ms][coroutine:📬-listener] received: 0
[elapsed: 1088ms][coroutine:✉️-sender] starting_to_send: 1
[elapsed: 1089ms][coroutine:✉️-sender] sent: 1
[elapsed: 1090ms][coroutine:📬-listener] received: 1
[elapsed: 1593ms][coroutine:✉️-sender] starting_to_send: 2
[elapsed: 1594ms][coroutine:✉️-sender] sent: 2
[elapsed: 1594ms][coroutine:📬-listener] received: 2
[elapsed: 1742ms][coroutine:unnamed] Closing channel from Main
[elapsed: 1746ms][coroutine:📬-listener] Listener Done - 🏁
[elapsed: 2099ms][coroutine:✉️-sender] starting_to_send: 3
[elapsed: 2105ms][coroutine:✉️-sender] sender exception: kotlinx.coroutines.channels.ClosedSendChannelException: Channel was closed
[elapsed: 3752ms][coroutine:unnamed] Main completed. Exiting...
GIVEN channel is closed:
- WHEN receiver uses receive THEN Exception is thrown.
Code
package com.glassthought.sandbox
import com.glassthought.sandbox.util.out.impl.OutSettings
import gt.sandbox.util.output.Out
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
val out = Out.standard(OutSettings(printCoroutineName = true, printThreadInfo = false))
fun main(args: Array<String>) {
runBlocking {
val channel = Channel<Int>(Channel.UNLIMITED)
val sender = launch(CoroutineName("${Emoji.LETTER}-sender")) {
try {
repeat(1) {
delay(500)
out.info("starting_to_send: $it")
channel.send(it)
out.info("sent: $it")
}
} catch (e: Exception) {
out.infoRed("sender exception: $e")
}
}
val listener = launch(CoroutineName("${Emoji.MAILBOX}-listener")) {
try {
repeat(100){
out.info("received: ${channel.receive()}")
}
out.info("Listener Done - ${Emoji.CHECKERED_FLAG}")
} catch (e: Exception) {
out.infoRed("listener exception: $e")
}
}
delay(1700)
out.info("Closing channel from Main")
channel.close()
delay(2000)
out.info("Main completed. Exiting...")
System.exit(0)
}
}
class Emoji {
companion object {
const val LETTER = "✉\uFE0F"
const val MAILBOX = "\uD83D\uDCEC"
const val CHECKERED_FLAG = "\uD83C\uDFC1"
}
}
Command to reproduce:
gt.sandbox.checkout.commit 0a95e9908782f3bef115 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"
Recorded output of command:
[elapsed: 564ms][coroutine:✉️-sender] starting_to_send: 0
[elapsed: 588ms][coroutine:✉️-sender] sent: 0
[elapsed: 589ms][coroutine:📬-listener] received: 0
[elapsed: 1740ms][coroutine:unnamed] Closing channel from Main
[elapsed: 1746ms][coroutine:📬-listener] listener exception: kotlinx.coroutines.channels.ClosedReceiveChannelException: Channel was closed
[elapsed: 3748ms][coroutine:unnamed] Main completed. Exiting...
Backlinks