|
4 | 4 |
|
5 | 5 | package kotlinx.coroutines.flow.internal
|
6 | 6 |
|
7 |
| -import kotlinx.atomicfu.* |
8 | 7 | import kotlinx.coroutines.*
|
9 | 8 | import kotlinx.coroutines.channels.*
|
10 |
| -import kotlinx.coroutines.channels.ArrayChannel |
11 | 9 | import kotlinx.coroutines.flow.*
|
12 | 10 |
|
13 |
| -internal fun <T> FlowCollector<T>.asConcurrentFlowCollector(): ConcurrentFlowCollector<T> = |
14 |
| - this as? ConcurrentFlowCollector<T> ?: SerializingCollector(this) |
15 |
| - |
16 |
| -// Flow collector that supports concurrent emit calls. |
17 |
| -// It is internal for now but may be public in the future. |
18 |
| -// Two basic implementations are here: SendingCollector and ConcurrentFlowCollector |
19 |
| -internal interface ConcurrentFlowCollector<T> : FlowCollector<T> |
20 |
| - |
21 | 11 | /**
|
22 |
| - * Collection that sends to channel. It is marked as [ConcurrentFlowCollector] because it can be used concurrently. |
23 |
| - * |
| 12 | + * Collection that sends to channel |
24 | 13 | * @suppress **This an internal API and should not be used from general code.**
|
25 | 14 | */
|
26 | 15 | @InternalCoroutinesApi
|
27 | 16 | public class SendingCollector<T>(
|
28 | 17 | private val channel: SendChannel<T>
|
29 |
| -) : ConcurrentFlowCollector<T> { |
| 18 | +) : FlowCollector<T> { |
30 | 19 | override suspend fun emit(value: T) = channel.send(value)
|
31 | 20 | }
|
32 |
| - |
33 |
| -// Effectively serializes access to downstream collector for merging |
34 |
| -// This is basically a converted from FlowCollector interface to ConcurrentFlowCollector |
35 |
| -private class SerializingCollector<T>( |
36 |
| - private val downstream: FlowCollector<T> |
37 |
| -) : ConcurrentFlowCollector<T> { |
38 |
| - // Let's try to leverage the fact that merge is never contended |
39 |
| - // Should be Any, but KT-30796 |
40 |
| - private val _channel = atomic<ArrayChannel<Any?>?>(null) |
41 |
| - private val inProgressLock = atomic(false) |
42 |
| - |
43 |
| - private val channel: ArrayChannel<Any?> |
44 |
| - get() = _channel.updateAndGet { value -> |
45 |
| - if (value != null) return value |
46 |
| - ArrayChannel(Channel.CHANNEL_DEFAULT_CAPACITY) |
47 |
| - }!! |
48 |
| - |
49 |
| - public override suspend fun emit(value: T) { |
50 |
| - if (!inProgressLock.tryAcquire()) { |
51 |
| - channel.send(value ?: NULL) |
52 |
| - if (inProgressLock.tryAcquire()) { |
53 |
| - helpEmit() |
54 |
| - } |
55 |
| - return |
56 |
| - } |
57 |
| - downstream.emit(value) |
58 |
| - helpEmit() |
59 |
| - } |
60 |
| - |
61 |
| - @Suppress("UNCHECKED_CAST") |
62 |
| - private suspend fun helpEmit() { |
63 |
| - while (true) { |
64 |
| - while (true) { |
65 |
| - val element = _channel.value?.poll() ?: break // todo: pollOrClosed |
66 |
| - downstream.emit(NULL.unbox(element)) |
67 |
| - } |
68 |
| - inProgressLock.release() |
69 |
| - // Enforce liveness |
70 |
| - if (_channel.value?.isEmpty != false || !inProgressLock.tryAcquire()) break |
71 |
| - } |
72 |
| - } |
73 |
| -} |
74 |
| - |
75 |
| -@Suppress("NOTHING_TO_INLINE") |
76 |
| -private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true) |
77 |
| - |
78 |
| -@Suppress("NOTHING_TO_INLINE") |
79 |
| -private inline fun AtomicBoolean.release() { |
80 |
| - value = false |
81 |
| -} |
0 commit comments