Skip to content

Commit 4e60132

Browse files
committed
Improve documentation on suspended Channel.send behaviour when the channel is closed
1 parent 80162ab commit 4e60132

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/channels/Channel.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public interface SendChannel<in E> {
4747
* or throws [ClosedSendChannelException] if the channel [isClosedForSend] _normally_.
4848
* It throws the original [close] cause exception if the channel has _failed_.
4949
*
50+
* Note, that closing a channel _after_ this function had suspended does not cause this suspended send invocation
51+
* to abort, because closing a channel is conceptually like sending a special "close token" over this channel.
52+
* All elements that are sent over the channel are delivered in first-in first-out order. The element that
53+
* is being sent will get delivered to receivers before a close token.
54+
*
5055
* This suspending function is cancellable. If the [Job] of the current coroutine is completed while this
5156
* function is suspended, this function immediately resumes with [CancellationException].
5257
* Cancellation of suspended send is *atomic* -- when this function
@@ -77,7 +82,7 @@ public interface SendChannel<in E> {
7782
/**
7883
* Closes this channel with an optional exceptional [cause].
7984
* This is an idempotent operation -- repeated invocations of this function have no effect and return `false`.
80-
* Conceptually, its sends a special close token of this channel. Immediately after invocation of this function
85+
* Conceptually, its sends a special "close token" over this channel. Immediately after invocation of this function
8186
* [isClosedForSend] starts returning `true`. However, [isClosedForReceive][ReceiveChannel.isClosedForReceive]
8287
* on the side of [ReceiveChannel] starts returning `true` only after all previously sent elements
8388
* are received.

kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/channels/RendezvousChannelTest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import kotlinx.coroutines.experimental.TestBase
2020
import kotlinx.coroutines.experimental.launch
2121
import kotlinx.coroutines.experimental.runBlocking
2222
import kotlinx.coroutines.experimental.yield
23+
import org.hamcrest.core.IsEqual
24+
import org.hamcrest.core.IsNull
2325
import org.junit.Assert.*
2426
import org.junit.Test
2527

@@ -247,6 +249,34 @@ class RendezvousChannelTest : TestBase() {
247249
finish(10)
248250
}
249251

252+
@Test
253+
fun testSuspendSendOnClosedChannel() = runBlocking<Unit> {
254+
val q = RendezvousChannel<Int>()
255+
expect(1)
256+
launch(context) {
257+
expect(4)
258+
q.send(42) // suspend
259+
expect(11)
260+
}
261+
expect(2)
262+
launch(context) {
263+
expect(5)
264+
q.close()
265+
expect(6)
266+
}
267+
expect(3)
268+
yield() // to sender
269+
expect(7)
270+
yield() // try to resume sender (it will not resume despite the close!)
271+
expect(8)
272+
assertThat(q.receiveOrNull(), IsEqual(42))
273+
expect(9)
274+
assertThat(q.receiveOrNull(), IsNull())
275+
expect(10)
276+
yield() // to sender, it was resumed!
277+
finish(12)
278+
}
279+
250280
class BadClass {
251281
override fun equals(other: Any?): Boolean = error("equals")
252282
override fun hashCode(): Int = error("hashCode")

0 commit comments

Comments
 (0)