Attempt to select SelectInstance in onReceiveOrClosed before starting a coroutine to avoid double resume
Fixed #1584
diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
index 6947b76..18a64c8 100644
--- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
@@ -747,7 +747,7 @@
}
}
else -> {
- // selected successfully
+ // selected successfully, pollSelectInternal is responsible for the select
block.startCoroutineUnintercepted(pollResult as E, select.completion)
return
}
@@ -776,10 +776,12 @@
pollResult === POLL_FAILED -> {} // retry
pollResult === RETRY_ATOMIC -> {} // retry
pollResult is Closed<*> -> {
- block.startCoroutineUnintercepted(ValueOrClosed.closed(pollResult.closeCause), select.completion)
+ if (select.trySelect())
+ block.startCoroutineUnintercepted(ValueOrClosed.closed(pollResult.closeCause), select.completion)
+ return
}
else -> {
- // selected successfully
+ // selected successfully, pollSelectInternal is responsible for the select
block.startCoroutineUnintercepted(ValueOrClosed.value(pollResult as E), select.completion)
return
}
diff --git a/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt b/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt
index c9747c6..a4f8c3b 100644
--- a/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt
+++ b/kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt
@@ -388,4 +388,35 @@
if (!trySelect()) return
block.startCoroutineUnintercepted(this)
}
+
+ @Test
+ fun testSelectReceiveOrClosedForClosedChannel() = runTest {
+ val channel = Channel<Int>(1)
+ channel.close()
+ expect(1)
+ select<Unit> {
+ expect(2)
+ channel.onReceiveOrClosed {
+ assertTrue(it.isClosed)
+ assertNull(it.closeCause)
+ finish(3)
+ }
+ }
+ }
+
+ @Test
+ fun testSelectReceiveOrClosedForClosedChannelWithValue() = runTest {
+ val channel = Channel<Int>(1)
+ channel.send(42)
+ channel.close()
+ expect(1)
+ select<Unit> {
+ expect(2)
+ channel.onReceiveOrClosed {
+ assertFalse(it.isClosed)
+ assertEquals(42, it.value)
+ finish(3)
+ }
+ }
+ }
}
diff --git a/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt b/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt
index e84514e..2027630 100644
--- a/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt
+++ b/kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt
@@ -348,6 +348,21 @@
}
@Test
+ fun testSelectReceiveOrClosedForClosedChannel() = runTest {
+ val channel = Channel<Unit>()
+ channel.close()
+ expect(1)
+ select<Unit> {
+ expect(2)
+ channel.onReceiveOrClosed {
+ assertTrue(it.isClosed)
+ assertNull(it.closeCause)
+ finish(3)
+ }
+ }
+ }
+
+ @Test
fun testSelectReceiveOrClosed() = runTest {
val channel = Channel<Int>(Channel.RENDEZVOUS)
val iterations = 10