@@ -30,178 +30,137 @@ import java.util.concurrent.atomic.AtomicInteger
30
30
import java.util.concurrent.atomic.AtomicReference
31
31
32
32
/* *
33
- * A subject implementation that dispatches signals to multiple
34
- * consumers or buffers them until such consumers arrive.
33
+ * A subject implementation that awaits a certain number of collectors
34
+ * to start consuming, then allows the producer side to deliver items
35
+ * to them.
35
36
*
36
37
* @param <T> the element type of the [Flow]
37
38
* @param bufferSize the number of items to buffer until consumers arrive
38
39
*/
39
40
@FlowPreview
40
- class MulticastSubject <T >(private val bufferSize : Int = 32 ) : AbstractFlow<T>(), SubjectAPI<T> {
41
+ class MulticastSubject <T >(private val expectedCollectors : Int ) : AbstractFlow<T>(), SubjectAPI<T> {
41
42
42
- val queue = ConcurrentLinkedQueue < T >( )
43
+ val collectors = AtomicReference < Array < ResumableCollector < T >>>( EMPTY as Array < ResumableCollector < T >> )
43
44
44
- val availableQueue = AtomicInteger (bufferSize )
45
+ val producer = Resumable ( )
45
46
46
- val consumers = AtomicReference (EMPTY as Array <ResumableCollector <T >>)
47
-
48
- val producerAwait = Resumable ()
49
-
50
- val wip = AtomicInteger ()
47
+ val remainingCollectors = AtomicInteger (expectedCollectors)
51
48
52
49
@Volatile
53
- var error : Throwable ? = null
50
+ var terminated : Throwable ? = null
54
51
55
52
override suspend fun emit (value : T ) {
56
- while (availableQueue.get() == 0 ) {
57
- producerAwait.await()
53
+ awaitCollectors()
54
+ for (collector in collectors.get()) {
55
+ try {
56
+ collector.next(value)
57
+ } catch (ex: CancellationException ) {
58
+ remove(collector)
59
+ }
58
60
}
59
- queue.offer(value)
60
- availableQueue.decrementAndGet();
61
- drain()
62
61
}
63
62
64
63
override suspend fun emitError (ex : Throwable ) {
65
- error = ex
66
- drain()
64
+ // awaitCollectors()
65
+ terminated = ex;
66
+ for (collector in collectors.getAndSet(TERMINATED as Array <ResumableCollector <T >>)) {
67
+ try {
68
+ collector.error(ex)
69
+ } catch (_: CancellationException ) {
70
+ // ignored at this point
71
+ }
72
+ }
67
73
}
68
74
69
75
override suspend fun complete () {
70
- error = DONE
71
- drain()
76
+ // awaitCollectors()
77
+ terminated = DONE
78
+ for (collector in collectors.getAndSet(TERMINATED as Array <ResumableCollector <T >>)) {
79
+ try {
80
+ collector.complete()
81
+ } catch (_: CancellationException ) {
82
+ // ignored at this point
83
+ }
84
+ }
72
85
}
73
86
74
87
override fun hasCollectors (): Boolean {
75
- return consumers .get().isNotEmpty();
88
+ return collectors .get().isNotEmpty()
76
89
}
77
90
78
91
override fun collectorCount (): Int {
79
- return consumers .get().size;
92
+ return collectors .get().size
80
93
}
81
94
82
- override suspend fun collectSafely (collector : FlowCollector <T >) {
83
- val c = ResumableCollector <T >()
84
- if (add(c)) {
85
- c.readyConsumer()
86
- drain()
87
- c.drain(collector) { remove(it) }
88
- } else {
89
- val ex = error
90
- if (ex != null && ex != DONE ) {
91
- throw ex
92
- }
95
+ private suspend fun awaitCollectors () {
96
+ if (remainingCollectors.get() != 0 ) {
97
+ producer.await()
93
98
}
94
99
}
95
100
96
- private fun add (collector : ResumableCollector <T >) : Boolean {
101
+ @Suppress(" UNCHECKED_CAST" , " " )
102
+ private fun add (inner : ResumableCollector <T >) : Boolean {
97
103
while (true ) {
98
- val a = consumers.get()
99
- if (a == TERMINATED ) {
104
+
105
+ val a = collectors.get()
106
+ if (a as Any == TERMINATED as Any ) {
100
107
return false
101
108
}
102
- val b = Array < ResumableCollector < T >>( a.size + 1 ) { idx ->
103
- if (idx < a.size) a[idx] else collector
104
- }
105
- if (consumers .compareAndSet(a, b)) {
109
+ val n = a.size
110
+ val b = a.copyOf(n + 1 )
111
+ b[n] = inner
112
+ if (collectors .compareAndSet(a, b as Array < ResumableCollector < T >> )) {
106
113
return true
107
114
}
108
115
}
109
116
}
110
- private fun remove (collector : ResumableCollector <T >) {
117
+
118
+ @Suppress(" UNCHECKED_CAST" )
119
+ private fun remove (inner : ResumableCollector <T >) {
111
120
while (true ) {
112
- val a = consumers .get()
121
+ val a = collectors .get()
113
122
val n = a.size
114
123
if (n == 0 ) {
115
124
return
116
125
}
117
- var j = - 1
118
- for (i in 0 until n) {
119
- if (a[i] == collector) {
120
- j = i
121
- break
122
- }
123
- }
126
+
127
+ val j = a.indexOf(inner)
124
128
if (j < 0 ) {
125
- return ;
129
+ return
126
130
}
131
+
127
132
var b = EMPTY as Array <ResumableCollector <T >? >
128
133
if (n != 1 ) {
129
- b = Array < ResumableCollector < T > ? > (n - 1 ) { null }
134
+ b = Array (n - 1 ) { null }
130
135
System .arraycopy(a, 0 , b, 0 , j)
131
136
System .arraycopy(a, j + 1 , b, j, n - j - 1 )
132
137
}
133
- if (consumers.compareAndSet(a, b as Array <ResumableCollector <T >>)) {
134
- return ;
135
- }
136
- }
137
- }
138
-
139
- private suspend fun drain () {
140
- if (wip.getAndIncrement() != 0 ) {
141
- return ;
142
- }
143
-
144
- while (true ) {
145
- val collectors = consumers.get()
146
- if (collectors.isNotEmpty()) {
147
- val ex = error;
148
- val v = queue.poll();
149
-
150
- if (v == null && ex != null ) {
151
- finish(ex)
152
- }
153
- else if (v != null ) {
154
- var k = 0 ;
155
- for (collector in consumers.get()) {
156
- try {
157
- // println("MulticastSubject -> [$k]: $v")
158
- collector.next(v)
159
- } catch (ex: CancellationException ) {
160
- remove(collector);
161
- }
162
- k++
163
- }
164
- availableQueue.getAndIncrement()
165
- producerAwait.resume()
166
- continue
167
- }
168
- } else {
169
- val ex = error;
170
- if (ex != null && queue.isEmpty()) {
171
- finish(ex)
172
- }
173
- }
174
- if (wip.decrementAndGet() == 0 ) {
175
- break
138
+ if (collectors.compareAndSet(a, b as Array <ResumableCollector <T >>)) {
139
+ return
176
140
}
177
141
}
178
142
}
179
143
180
- private suspend fun finish (ex : Throwable ) {
181
- if (ex == DONE ) {
182
- for (collector in consumers.getAndSet(TERMINATED as Array <ResumableCollector <T >>)) {
183
- try {
184
- collector.complete()
185
- } catch (_: CancellationException ) {
186
- // ignored
187
- }
144
+ override suspend fun collectSafely (collector : FlowCollector <T >) {
145
+ val rc = ResumableCollector <T >()
146
+ if (add(rc)) {
147
+ if (remainingCollectors.decrementAndGet() == 0 ) {
148
+ producer.resume()
188
149
}
150
+ rc.drain(collector) { remove(it) }
189
151
} else {
190
- for (collector in consumers.getAndSet(TERMINATED as Array <ResumableCollector <T >>)) {
191
- try {
192
- collector.error(ex)
193
- } catch (_: CancellationException ) {
194
- // ignored
195
- }
152
+ val ex = terminated;
153
+ if (ex != null && ex != DONE ) {
154
+ throw ex
196
155
}
197
156
}
198
157
}
199
158
200
159
companion object {
201
- val DONE : Throwable = Throwable ( " Subject Completed " )
160
+ val EMPTY = arrayOf< ResumableCollector < Any >>( )
202
161
203
- val EMPTY = arrayOf<ResumableCollector <Any >>();
162
+ val TERMINATED = arrayOf<ResumableCollector <Any >>()
204
163
205
- val TERMINATED = arrayOf< ResumableCollector < Any >>();
164
+ val DONE = Throwable ( " Subject completed " )
206
165
}
207
166
}
0 commit comments