1
1
/*
2
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3
3
*/
4
4
5
5
package kotlinx.coroutines.channels
@@ -10,28 +10,43 @@ import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
10
10
import kotlinx.coroutines.intrinsics.*
11
11
import kotlin.coroutines.*
12
12
import kotlin.coroutines.intrinsics.*
13
+ import kotlin.native.concurrent.*
13
14
14
15
/* *
15
16
* Broadcasts all elements of the channel.
17
+ * This function [consumes][ReceiveChannel.consume] all elements of the original [ReceiveChannel].
16
18
*
17
19
* The kind of the resulting channel depends on the specified [capacity] parameter:
18
20
* when `capacity` is positive (1 by default), but less than [UNLIMITED] -- uses `ArrayBroadcastChannel` with a buffer of given capacity,
19
21
* when `capacity` is [CONFLATED] -- uses [ConflatedBroadcastChannel] that conflates back-to-back sends;
20
22
* Note that resulting channel behaves like [ConflatedBroadcastChannel] but is not an instance of [ConflatedBroadcastChannel].
21
23
* otherwise -- throws [IllegalArgumentException].
22
24
*
25
+ * ### Cancelling broadcast
26
+ *
27
+ * **To stop broadcasting from the underlying channel call [cancel][BroadcastChannel.cancel] on the result.**
28
+ *
29
+ * Do not use [close][BroadcastChannel.close] on the resulting channel.
30
+ * It causes eventual failure of the broadcast coroutine and cancellation of the underlying channel, too,
31
+ * but it is not as prompt.
32
+ *
23
33
* @param start coroutine start option. The default value is [CoroutineStart.LAZY].
24
34
*/
25
35
fun <E > ReceiveChannel<E>.broadcast (
26
36
capacity : Int = 1,
27
37
start : CoroutineStart = CoroutineStart .LAZY
28
38
): BroadcastChannel <E > =
29
- GlobalScope .broadcast(Dispatchers .Unconfined , capacity = capacity, start = start, onCompletion = consumes()) {
39
+ OperatorScope .broadcast(capacity = capacity, start = start, onCompletion = consumes()) {
40
+ // We can run this coroutine in the context that ignores all exceptions, because of `onCompletion = consume()`
41
+ // which passes all exceptions upstream to the source ReceiveChannel
30
42
for (e in this @broadcast) {
31
43
send(e)
32
44
}
33
45
}
34
46
47
+ @SharedImmutable
48
+ private val OperatorScope = GlobalScope + Dispatchers .Unconfined + CoroutineExceptionHandler { _, _ -> }
49
+
35
50
/* *
36
51
* Launches new coroutine to produce a stream of values by sending them to a broadcast channel
37
52
* and returns a reference to the coroutine as a [BroadcastChannel]. The resulting
@@ -63,6 +78,14 @@ fun <E> ReceiveChannel<E>.broadcast(
63
78
*
64
79
* See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
65
80
*
81
+ * ### Cancelling broadcast
82
+ *
83
+ * **To stop broadcasting from the underlying channel call [cancel][BroadcastChannel.cancel] on the result.**
84
+ *
85
+ * Do not use [close][BroadcastChannel.close] on the resulting channel.
86
+ * It causes failure of the `send` operation in broadcast coroutine and would not cancel it if the
87
+ * coroutine is doing something else.
88
+ *
66
89
* @param context additional to [CoroutineScope.coroutineContext] context of the coroutine.
67
90
* @param capacity capacity of the channel's buffer (1 by default).
68
91
* @param start coroutine start option. The default value is [CoroutineStart.LAZY].
@@ -107,8 +130,9 @@ private open class BroadcastCoroutine<E>(
107
130
}
108
131
109
132
override fun cancelInternal (cause : Throwable ) {
110
- _channel .cancel(cause.toCancellationException()) // cancel the channel
111
- cancelCoroutine(cause) // cancel the job
133
+ val exception = cause.toCancellationException()
134
+ _channel .cancel(exception) // cancel the channel
135
+ cancelCoroutine(exception) // cancel the job
112
136
}
113
137
114
138
override fun onCompleted (value : Unit ) {
@@ -119,6 +143,13 @@ private open class BroadcastCoroutine<E>(
119
143
val processed = _channel .close(cause)
120
144
if (! processed && ! handled) handleCoroutineException(context, cause)
121
145
}
146
+
147
+ // The BroadcastChannel could be also closed
148
+ override fun close (cause : Throwable ? ): Boolean {
149
+ val result = _channel .close(cause)
150
+ start() // start coroutine if it was not started yet
151
+ return result
152
+ }
122
153
}
123
154
124
155
private class LazyBroadcastCoroutine <E >(
0 commit comments