Skip to content

Commit 1aec688

Browse files
committed
Fix offer inconsistency
1 parent 0c83b45 commit 1aec688

File tree

3 files changed

+110
-36
lines changed

3 files changed

+110
-36
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ public abstract class AbstractSendChannel<E> : SendChannel<E> {
181181
val result = offerInternal(element)
182182
return when {
183183
result === OFFER_SUCCESS -> true
184-
result === OFFER_FAILED -> false
184+
result === OFFER_FAILED -> throw closedForSend?.sendException ?: return false
185185
result is Closed<*> -> throw result.sendException
186186
else -> error("offerInternal returned $result")
187187
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package kotlinx.coroutines.experimental.channels
2+
3+
import kotlinx.coroutines.experimental.*
4+
import kotlin.coroutines.experimental.*
5+
import kotlin.test.*
6+
7+
class BasicOperationsTest : TestBase() {
8+
9+
@Test
10+
fun testSimpleSendReceive() = runTest {
11+
// Parametrized common test :(
12+
TestChannelKind.values().forEach { kind -> testSendReceive(kind, 100) }
13+
}
14+
15+
@Test
16+
fun testOfferAfterClose() = runTest {
17+
TestChannelKind.values().forEach { kind -> testOffer(kind) }
18+
}
19+
20+
@Test
21+
fun testReceiveOrNullAfterClose() = runTest {
22+
TestChannelKind.values().forEach { kind -> testReceiveOrNull(kind) }
23+
}
24+
25+
@Test
26+
fun testReceiveOrNullAfterCloseWithException() = runTest {
27+
TestChannelKind.values().forEach { kind -> testReceiveOrNullException(kind) }
28+
}
29+
30+
private suspend fun testReceiveOrNull(kind: TestChannelKind) {
31+
val channel = kind.create()
32+
val d = async(coroutineContext) {
33+
channel.receive()
34+
}
35+
36+
yield()
37+
channel.close()
38+
assertTrue(channel.isClosedForReceive)
39+
40+
assertNull(channel.receiveOrNull())
41+
assertNull(channel.poll())
42+
43+
d.join()
44+
assertTrue(d.getCancellationException().cause is ClosedReceiveChannelException)
45+
}
46+
47+
private suspend fun testReceiveOrNullException(kind: TestChannelKind) {
48+
val channel = kind.create()
49+
val d = async(coroutineContext) {
50+
channel.receive()
51+
}
52+
53+
yield()
54+
channel.close(IndexOutOfBoundsException())
55+
assertTrue(channel.isClosedForReceive)
56+
57+
assertFailsWith<IndexOutOfBoundsException> { channel.poll() }
58+
try {
59+
channel.receiveOrNull()
60+
fail()
61+
} catch (e: IndexOutOfBoundsException) {
62+
// Expected
63+
}
64+
65+
d.join()
66+
assertTrue(d.getCancellationException().cause is IndexOutOfBoundsException)
67+
}
68+
69+
70+
private suspend fun testOffer(kind: TestChannelKind) {
71+
val channel = kind.create()
72+
val d = async(coroutineContext) { channel.send(42) }
73+
yield()
74+
channel.close()
75+
76+
assertTrue(channel.isClosedForSend)
77+
try {
78+
channel.offer(2)
79+
fail()
80+
} catch (e: ClosedSendChannelException) {
81+
if (!kind.isConflated) {
82+
assertEquals(42, channel.receive())
83+
}
84+
}
85+
86+
d.await()
87+
}
88+
89+
private suspend fun testSendReceive(kind: TestChannelKind, iterations: Int) {
90+
val channel = kind.create()
91+
92+
launch(coroutineContext) {
93+
repeat(iterations) { channel.send(it) }
94+
channel.close()
95+
}
96+
var expected = 0
97+
for (x in channel) {
98+
if (!kind.isConflated) {
99+
assertEquals(expected++, x)
100+
} else {
101+
assertTrue(x >= expected)
102+
expected = x + 1
103+
}
104+
}
105+
if (!kind.isConflated) {
106+
assertEquals(iterations, expected)
107+
}
108+
}
109+
}

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

-35
This file was deleted.

0 commit comments

Comments
 (0)