Skip to content

Commit 7f1a927

Browse files
committed
Attempt to select SelectInstance in onReceiveOrClosed before starting a coroutine to avoid double resume
Fixed #1584
1 parent f86af23 commit 7f1a927

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt

+5-3
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
747747
}
748748
}
749749
else -> {
750-
// selected successfully
750+
// selected successfully, pollSelectInternal is responsible for the select
751751
block.startCoroutineUnintercepted(pollResult as E, select.completion)
752752
return
753753
}
@@ -776,10 +776,12 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
776776
pollResult === POLL_FAILED -> {} // retry
777777
pollResult === RETRY_ATOMIC -> {} // retry
778778
pollResult is Closed<*> -> {
779-
block.startCoroutineUnintercepted(ValueOrClosed.closed(pollResult.closeCause), select.completion)
779+
if (select.trySelect())
780+
block.startCoroutineUnintercepted(ValueOrClosed.closed(pollResult.closeCause), select.completion)
781+
return
780782
}
781783
else -> {
782-
// selected successfully
784+
// selected successfully, pollSelectInternal is responsible for the select
783785
block.startCoroutineUnintercepted(ValueOrClosed.value(pollResult as E), select.completion)
784786
return
785787
}

kotlinx-coroutines-core/common/test/selects/SelectArrayChannelTest.kt

+31
Original file line numberDiff line numberDiff line change
@@ -388,4 +388,35 @@ class SelectArrayChannelTest : TestBase() {
388388
if (!trySelect()) return
389389
block.startCoroutineUnintercepted(this)
390390
}
391+
392+
@Test
393+
fun testSelectReceiveOrClosedForClosedChannel() = runTest {
394+
val channel = Channel<Int>(1)
395+
channel.close()
396+
expect(1)
397+
select<Unit> {
398+
expect(2)
399+
channel.onReceiveOrClosed {
400+
assertTrue(it.isClosed)
401+
assertNull(it.closeCause)
402+
finish(3)
403+
}
404+
}
405+
}
406+
407+
@Test
408+
fun testSelectReceiveOrClosedForClosedChannelWithValue() = runTest {
409+
val channel = Channel<Int>(1)
410+
channel.send(42)
411+
channel.close()
412+
expect(1)
413+
select<Unit> {
414+
expect(2)
415+
channel.onReceiveOrClosed {
416+
assertFalse(it.isClosed)
417+
assertEquals(42, it.value)
418+
finish(3)
419+
}
420+
}
421+
}
391422
}

kotlinx-coroutines-core/common/test/selects/SelectRendezvousChannelTest.kt

+15
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,21 @@ class SelectRendezvousChannelTest : TestBase() {
347347
finish(6)
348348
}
349349

350+
@Test
351+
fun testSelectReceiveOrClosedForClosedChannel() = runTest {
352+
val channel = Channel<Unit>()
353+
channel.close()
354+
expect(1)
355+
select<Unit> {
356+
expect(2)
357+
channel.onReceiveOrClosed {
358+
assertTrue(it.isClosed)
359+
assertNull(it.closeCause)
360+
finish(3)
361+
}
362+
}
363+
}
364+
350365
@Test
351366
fun testSelectReceiveOrClosed() = runTest {
352367
val channel = Channel<Int>(Channel.RENDEZVOUS)

0 commit comments

Comments
 (0)