Skip to content

Commit b0517ba

Browse files
committed
"Channels are fair" section with ping-pong table example
1 parent 04d11ca commit b0517ba

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed

coroutines-guide.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ This is a short guide on core features of `kotlinx.coroutines` with a series of
7676
* [Fan-out](#fan-out)
7777
* [Fan-in](#fan-in)
7878
* [Buffered channels](#buffered-channels)
79+
* [Channels are fair](#channels-are-fair)
7980
* [Shared mutable state and concurrency](#shared-mutable-state-and-concurrency)
8081
* [The problem](#the-problem)
8182
* [Volatiles are of no help](#volatiles-are-of-no-help)
@@ -1477,6 +1478,53 @@ Sending 4
14771478

14781479
The first four elements are added to the buffer and the sender suspends when trying to send the fifth one.
14791480

1481+
1482+
### Channels are fair
1483+
1484+
Send and receive operations to channels are _fair_ with respect to the order of their invocation from
1485+
multiple coroutines. They are served in first-in first-out order, e.g. the first coroutine to invoke `receive`
1486+
gets the element. In the following example two coroutines "ping" and "pong" are
1487+
receiving the "ball" object from the shared "table" channel.
1488+
1489+
```kotlin
1490+
data class Ball(var hits: Int)
1491+
1492+
fun main(args: Array<String>) = runBlocking<Unit> {
1493+
val table = Channel<Ball>() // a shared table
1494+
launch(context) { player("ping", table) }
1495+
launch(context) { player("pong", table) }
1496+
table.send(Ball(0)) // serve the ball
1497+
delay(1000) // delay 1 second
1498+
table.receive() // game over, grab the ball
1499+
}
1500+
1501+
suspend fun player(name: String, table: Channel<Ball>) {
1502+
for (ball in table) { // receive the ball in a loop
1503+
ball.hits++
1504+
println("$name $ball")
1505+
delay(200) // wait a bit
1506+
table.send(ball) // send the ball back
1507+
}
1508+
}
1509+
```
1510+
1511+
> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/guide/example-channel-09.kt)
1512+
1513+
The "ping" coroutine is started first, so it is the first one to receive the ball. Even though "ping"
1514+
coroutine immediately starts receiving the ball again after sending it back to the table, the ball gets
1515+
received by the "pong" coroutine, because it was already waiting for it:
1516+
1517+
```text
1518+
ping Ball(hits=1)
1519+
pong Ball(hits=2)
1520+
ping Ball(hits=3)
1521+
pong Ball(hits=4)
1522+
ping Ball(hits=5)
1523+
pong Ball(hits=6)
1524+
```
1525+
1526+
<!--- TEST -->
1527+
14801528
## Shared mutable state and concurrency
14811529

14821530
Coroutines can be executed concurrently using a multi-threaded dispatcher like [CommonPool]. It presents
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2016-2017 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
18+
package guide.channel.example09
19+
20+
import kotlinx.coroutines.experimental.*
21+
import kotlinx.coroutines.experimental.channels.*
22+
23+
data class Ball(var hits: Int)
24+
25+
fun main(args: Array<String>) = runBlocking<Unit> {
26+
val table = Channel<Ball>() // a shared table
27+
launch(context) { player("ping", table) }
28+
launch(context) { player("pong", table) }
29+
table.send(Ball(0)) // serve the ball
30+
delay(1000) // delay 1 second
31+
table.receive() // game over, grab the ball
32+
}
33+
34+
suspend fun player(name: String, table: Channel<Ball>) {
35+
for (ball in table) { // receive the ball in a loop
36+
ball.hits++
37+
println("$name $ball")
38+
delay(200) // wait a bit
39+
table.send(ball) // send the ball back
40+
}
41+
}

kotlinx-coroutines-core/src/test/kotlin/guide/test/GuideTest.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,18 @@ class GuideTest {
334334
)
335335
}
336336

337+
@Test
338+
fun testGuideChannelExample09() {
339+
test { guide.channel.example09.main(emptyArray()) }.verifyLines(
340+
"ping Ball(hits=1)",
341+
"pong Ball(hits=2)",
342+
"ping Ball(hits=3)",
343+
"pong Ball(hits=4)",
344+
"ping Ball(hits=5)",
345+
"pong Ball(hits=6)"
346+
)
347+
}
348+
337349
@Test
338350
fun testGuideSyncExample01() {
339351
test { guide.sync.example01.main(emptyArray()) }.verifyLinesStart(

0 commit comments

Comments
 (0)