Skip to content

Commit d4dcbe2

Browse files
committed
Guide to select
1 parent 1216e91 commit d4dcbe2

File tree

8 files changed

+720
-65
lines changed

8 files changed

+720
-65
lines changed

coroutines-guide.md

Lines changed: 365 additions & 23 deletions
Large diffs are not rendered by default.

knit/src/Knit.kt

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ fun knit(markdownFileName: String) {
117117
requireSingleLine(directive)
118118
require(siteRoot != null) { "$SITE_ROOT_DIRECTIVE must be specified" }
119119
require(docsRoot != null) { "$DOCS_ROOT_DIRECTIVE must be specified" }
120-
val indexLines = readApiIndex(directive.param, remainingApiRefNames, siteRoot!!, docsRoot!!)
120+
val indexLines = processApiIndex(siteRoot!!, docsRoot!!, directive.param, remainingApiRefNames)
121121
skip = true
122122
while (true) {
123123
val skipLine = readLine() ?: break@mainLoop
@@ -275,38 +275,62 @@ fun writeLines(file: File, lines: List<String>) {
275275
}
276276
}
277277

278+
data class ApiIndexKey(
279+
val docsRoot: String,
280+
val pkg: String
281+
)
282+
283+
val apiIndexCache: MutableMap<ApiIndexKey, Map<String, String>> = HashMap()
284+
278285
val REF_LINE_REGEX = Regex("<a href=\"([a-z/.\\-]+)\">([a-zA-z.]+)</a>")
279286
val INDEX_HTML = "/index.html"
280287
val INDEX_MD = "/index.md"
281288

282-
fun readApiIndex(
283-
path: String,
284-
remainingApiRefNames: MutableSet<String>,
285-
siteRoot: String,
286-
docsRoot: String,
287-
prefix: String = ""
288-
): List<String> {
289+
fun loadApiIndex(
290+
docsRoot: String,
291+
path: String,
292+
pkg: String,
293+
namePrefix: String = ""
294+
): Map<String, String> {
289295
val fileName = docsRoot + "/" + path + INDEX_MD
290296
println("Reading index from $fileName")
291-
val indexList = arrayListOf<String>()
292297
val visited = mutableSetOf<String>()
298+
val map = HashMap<String,String>()
293299
File(fileName).withLineNumberReader<LineNumberReader>(::LineNumberReader) {
294300
while (true) {
295301
val line = readLine() ?: break
296302
val result = REF_LINE_REGEX.matchEntire(line) ?: continue
297303
val refLink = result.groups[1]!!.value
298304
if (refLink.startsWith("..")) continue // ignore cross-references
299-
val refName = prefix + result.groups[2]!!.value
300-
if (remainingApiRefNames.remove(refName)) {
301-
indexList += "[$refName]: $siteRoot/$path/$refLink"
302-
}
305+
val refName = namePrefix + result.groups[2]!!.value
306+
map.put(refName, refLink)
307+
map.put(pkg + "." + refName, refLink)
303308
if (refLink.endsWith(INDEX_HTML)) {
304309
if (visited.add(refLink)) {
305310
val path2 = path + "/" + refLink.substring(0, refLink.length - INDEX_HTML.length)
306-
indexList += readApiIndex(path2, remainingApiRefNames, siteRoot, docsRoot, refName + ".")
311+
map += loadApiIndex(docsRoot, path2, pkg, refName + ".")
307312
}
308313
}
309314
}
310315
}
316+
return map
317+
}
318+
319+
fun processApiIndex(
320+
siteRoot: String,
321+
docsRoot: String,
322+
pkg: String,
323+
remainingApiRefNames: MutableSet<String>
324+
): List<String> {
325+
val key = ApiIndexKey(docsRoot, pkg)
326+
val map = apiIndexCache.getOrPut(key, { loadApiIndex(docsRoot, pkg, pkg) })
327+
val indexList = arrayListOf<String>()
328+
val it = remainingApiRefNames.iterator()
329+
while (it.hasNext()) {
330+
val refName = it.next()
331+
val refLink = map[refName] ?: continue
332+
indexList += "[$refName]: $siteRoot/$pkg/$refLink"
333+
it.remove()
334+
}
311335
return indexList
312-
}
336+
}

kotlinx-coroutines-core/README.md

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ Top-level suspending functions:
3737
| [run] | Switches to a different context
3838
| [withTimeout] | Set execution time-limit (deadline)
3939

40+
[Select][kotlinx.coroutines.experimental.selects.select] expression waits for the result of multiple suspending functions simultaneously:
41+
42+
| **Receiver** | **Suspending function** | **Select clause** | **Non-suspending version**
43+
| ---------------- | --------------------------------------------- | ------------------------------------------------ | --------------------------
44+
| [Deferred] | [await][Deferred.await] | [onAwait][SelectBuilder.onAwait] | [isCompleted][Deferred.isCompleted]
45+
| [SendChannel] | [send][SendChannel.send] | [onSend][SelectBuilder.onSend] | [offer][SendChannel.offer]
46+
| [ReceiveChannel] | [receive][ReceiveChannel.receive] | [onReceive][SelectBuilder.onReceive] | [poll][ReceiveChannel.poll]
47+
| [ReceiveChannel] | [receiveOrNull][ReceiveChannel.receiveOrNull] | [onReceiveOrNull][SelectBuilder.onReceiveOrNull] | [poll][ReceiveChannel.poll]
48+
4049
Cancellation support for user-defined suspending functions is available with [suspendCancellableCoroutine]
4150
helper function. [NonCancellable] job object is provided to suppress cancellation with
4251
`run(NonCancellable) {...}` block of code.
@@ -45,43 +54,57 @@ This module provides debugging facilities for coroutines (run JVM with `-ea` or
4554
and [newCoroutineContext] function to write user-defined coroutine builders that work with these
4655
debugging facilities.
4756

57+
# Package kotlinx.coroutines.experimental
58+
59+
General-purpose coroutine builders, contexts, and helper functions.
60+
61+
# Package kotlinx.coroutines.experimental.channels
62+
63+
Channels -- non-blocking primitives for communicating a stream of elements between coroutines.
64+
65+
# Package kotlinx.coroutines.experimental.selects
66+
67+
Select expression to perform multiple suspending operations simultaneously until one of them succeeds.
68+
4869
<!--- SITE_ROOT https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core -->
4970
<!--- DOCS_ROOT kotlinx-coroutines-core/target/dokka/kotlinx-coroutines-core -->
5071
<!--- INDEX kotlinx.coroutines.experimental -->
51-
[CommonPool]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-common-pool/index.html
52-
[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-dispatcher/index.html
72+
[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/launch.html
73+
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/index.html
5374
[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-scope/index.html
75+
[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
5476
[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
55-
[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/index.html
56-
[Mutex]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-mutex/index.html
57-
[Mutex.lock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-mutex/lock.html
58-
[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-non-cancellable/index.html
77+
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html
78+
[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-dispatcher/index.html
79+
[CommonPool]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-common-pool/index.html
80+
[newSingleThreadContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/new-single-thread-context.html
81+
[newFixedThreadPoolContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/new-fixed-thread-pool-context.html
82+
[java.util.concurrent.Executor.toCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/to-coroutine-dispatcher.html
5983
[Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-unconfined/index.html
60-
[java.util.concurrent.Executor.toCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/java.util.concurrent.-executor/to-coroutine-dispatcher.html
61-
[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
84+
[Mutex]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-mutex/index.html
85+
[Mutex.lock]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/lock.html
6286
[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/delay.html
63-
[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/launch.html
64-
[newCoroutineContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/new-coroutine-context.html
65-
[newFixedThreadPoolContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/new-fixed-thread-pool-context.html
66-
[newSingleThreadContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/new-single-thread-context.html
87+
[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/yield.html
6788
[run]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run.html
68-
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html
69-
[suspendCancellableCoroutine]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/suspend-cancellable-coroutine.html
7089
[withTimeout]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/with-timeout.html
71-
[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/yield.html
90+
[suspendCancellableCoroutine]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/suspend-cancellable-coroutine.html
91+
[NonCancellable]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-non-cancellable/index.html
92+
[newCoroutineContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/new-coroutine-context.html
7293
<!--- INDEX kotlinx.coroutines.experimental.channels -->
73-
[Channel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-channel/index.html
94+
[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/produce.html
7495
[ProducerJob]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-producer-job/index.html
7596
[ProducerScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-producer-scope/index.html
76-
[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-receive-channel/receive.html
77-
[SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-send-channel/send.html
78-
[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/produce.html
97+
[Channel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-channel/index.html
98+
[SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/send.html
99+
[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/receive.html
100+
[SendChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-send-channel/index.html
101+
[SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/offer.html
102+
[ReceiveChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-receive-channel/index.html
103+
[ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/poll.html
104+
[ReceiveChannel.receiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/receive-or-null.html
105+
<!--- INDEX kotlinx.coroutines.experimental.selects -->
106+
[kotlinx.coroutines.experimental.selects.select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.selects/select.html
107+
[SelectBuilder.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.selects/on-send.html
108+
[SelectBuilder.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.selects/on-receive.html
109+
[SelectBuilder.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.selects/on-receive-or-null.html
79110
<!--- END -->
80-
81-
# Package kotlinx.coroutines.experimental
82-
83-
General-purpose coroutine builders, contexts, and helper functions.
84-
85-
# Package kotlinx.coroutines.experimental.channels
86-
87-
Channels -- non-blocking primitives for communicating a stream of elements between coroutines.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.select.example01
19+
20+
import kotlinx.coroutines.experimental.*
21+
import kotlinx.coroutines.experimental.channels.*
22+
import kotlinx.coroutines.experimental.selects.*
23+
24+
val fizz = produce<String>(CommonPool) { // produce using common thread pool
25+
while (true) {
26+
delay(300)
27+
send("Fizz")
28+
}
29+
}
30+
31+
val buzz = produce<String>(CommonPool) {
32+
while (true) {
33+
delay(500)
34+
send("Buzz!")
35+
}
36+
}
37+
38+
suspend fun selectFizzBuzz() {
39+
select<Unit> { // <Unit> means that this select expression does not produce any result
40+
fizz.onReceive { value -> // this is the first select clause
41+
println("fizz -> '$value'")
42+
}
43+
buzz.onReceive { value -> // this is the second select clause
44+
println("buzz -> '$value'")
45+
}
46+
}
47+
}
48+
49+
fun main(args: Array<String>) = runBlocking<Unit> {
50+
repeat(7) {
51+
selectFizzBuzz()
52+
}
53+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.select.example02
19+
20+
import kotlinx.coroutines.experimental.*
21+
import kotlinx.coroutines.experimental.channels.*
22+
import kotlinx.coroutines.experimental.selects.*
23+
24+
suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
25+
select<String> {
26+
a.onReceiveOrNull { value ->
27+
if (value == null)
28+
"Channel 'a' is closed"
29+
else
30+
"a -> '$value'"
31+
}
32+
b.onReceiveOrNull { value ->
33+
if (value == null)
34+
"Channel 'b' is closed"
35+
else
36+
"b -> '$value'"
37+
}
38+
}
39+
40+
fun main(args: Array<String>) = runBlocking<Unit> {
41+
// we are using the context of the main thread in this example for predictability ...
42+
val a = produce<String>(context) {
43+
repeat(4) {
44+
send("Hello $it")
45+
}
46+
}
47+
val b = produce<String>(context) {
48+
repeat(4) {
49+
send("World $it")
50+
}
51+
}
52+
repeat(8) { // print first eight results
53+
println(selectAorB(a, b))
54+
}
55+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.select.example03
19+
20+
import kotlinx.coroutines.experimental.*
21+
import kotlinx.coroutines.experimental.channels.*
22+
import kotlinx.coroutines.experimental.selects.*
23+
24+
fun produceNumbers(side: SendChannel<Int>) = produce<Int>(CommonPool) {
25+
for (num in 1..10) { // produce 10 numbers from 1 to 10
26+
delay(100) // every 100 ms
27+
select<Unit> {
28+
onSend(num) { } // Send to the primary channel
29+
side.onSend(num) { } // or to the side channel
30+
}
31+
}
32+
}
33+
34+
fun main(args: Array<String>) = runBlocking<Unit> {
35+
val side = Channel<Int>() // allocate side channel
36+
launch(context) { // this is a very fast consumer for the side channel
37+
for (num in side) println("Side channel has $num")
38+
}
39+
for (num in produceNumbers(side)) {
40+
println("Consuming $num")
41+
delay(250) // let us digest the consumed number properly, do not hurry
42+
}
43+
println("Done consuming")
44+
}

0 commit comments

Comments
 (0)