15
15
*/
16
16
package rx .internal .operators ;
17
17
18
- import java .util .concurrent .ConcurrentLinkedQueue ;
18
+ import java .util .concurrent .atomic . AtomicLong ;
19
19
import java .util .concurrent .atomic .AtomicLongFieldUpdater ;
20
20
21
21
import rx .Observable ;
22
22
import rx .Observable .Operator ;
23
23
import rx .Observer ;
24
+ import rx .Producer ;
24
25
import rx .Subscriber ;
26
+ import rx .exceptions .MissingBackpressureException ;
25
27
import rx .exceptions .OnErrorThrowable ;
26
28
import rx .functions .Func2 ;
27
29
import rx .functions .Func3 ;
33
35
import rx .functions .Func9 ;
34
36
import rx .functions .FuncN ;
35
37
import rx .functions .Functions ;
38
+ import rx .internal .util .RxRingBuffer ;
36
39
import rx .subscriptions .CompositeSubscription ;
37
40
38
41
/**
48
51
* <p>
49
52
* The resulting Observable returned from zip will invoke <code>onNext</code> as many times as the
50
53
* number of <code>onNext</code> invocations of the source Observable that emits the fewest items.
51
- * @param <R> the result type
54
+ *
55
+ * @param <R>
56
+ * the result type
52
57
*/
53
58
public final class OperatorZip <R > implements Operator <R , Observable <?>[]> {
54
59
/*
@@ -104,69 +109,106 @@ public OperatorZip(Func9 f) {
104
109
105
110
@ SuppressWarnings ("rawtypes" )
106
111
@ Override
107
- public Subscriber <? super Observable []> call (final Subscriber <? super R > observer ) {
108
- return new Subscriber <Observable []>(observer ) {
112
+ public Subscriber <? super Observable []> call (final Subscriber <? super R > child ) {
113
+ final Zip <R > zipper = new Zip <R >(child , zipFunction );
114
+ final ZipProducer <R > producer = new ZipProducer <R >(zipper );
115
+ child .setProducer (producer );
116
+ final ZipSubscriber subscriber = new ZipSubscriber (child , zipper , producer );
117
+ return subscriber ;
118
+ }
109
119
110
- boolean started = false ;
120
+ private final class ZipSubscriber extends Subscriber < Observable []> {
111
121
112
- @ Override
113
- public void onCompleted () {
114
- if (!started ) {
115
- // this means we have not received a valid onNext before termination so we emit the onCompleted
116
- observer .onCompleted ();
117
- }
118
- }
122
+ final Subscriber <? super R > child ;
123
+ final Zip <R > zipper ;
124
+ final ZipProducer <R > producer ;
119
125
120
- @ Override
121
- public void onError (Throwable e ) {
122
- observer .onError (e );
126
+ public ZipSubscriber (Subscriber <? super R > child , Zip <R > zipper , ZipProducer <R > producer ) {
127
+ super (child );
128
+ this .child = child ;
129
+ this .zipper = zipper ;
130
+ this .producer = producer ;
131
+ }
132
+
133
+ boolean started = false ;
134
+
135
+ @ Override
136
+ public void onCompleted () {
137
+ if (!started ) {
138
+ // this means we have not received a valid onNext before termination so we emit the onCompleted
139
+ child .onCompleted ();
123
140
}
141
+ }
124
142
125
- @ Override
126
- public void onNext (Observable [] observables ) {
127
- if (observables == null || observables .length == 0 ) {
128
- observer .onCompleted ();
129
- } else {
130
- started = true ;
131
- new Zip <R >(observables , observer , zipFunction ).zip ();
132
- }
143
+ @ Override
144
+ public void onError (Throwable e ) {
145
+ child .onError (e );
146
+ }
147
+
148
+ @ Override
149
+ public void onNext (Observable [] observables ) {
150
+ if (observables == null || observables .length == 0 ) {
151
+ child .onCompleted ();
152
+ } else {
153
+ started = true ;
154
+ zipper .start (observables , producer );
133
155
}
156
+ }
157
+
158
+ }
159
+
160
+ private static final class ZipProducer <R > extends AtomicLong implements Producer {
161
+
162
+ private Zip <R > zipper ;
163
+
164
+ public ZipProducer (Zip <R > zipper ) {
165
+ this .zipper = zipper ;
166
+ }
167
+
168
+ @ Override
169
+ public void request (long n ) {
170
+ addAndGet (n );
171
+ // try and claim emission if no other threads are doing so
172
+ zipper .tick ();
173
+ }
134
174
135
- };
136
175
}
137
176
138
- static final NotificationLite <Object > on = NotificationLite .instance ();
139
177
private static final class Zip <R > {
140
- @ SuppressWarnings ("rawtypes" )
141
- final Observable [] os ;
142
- final Object [] observers ;
143
- final Observer <? super R > observer ;
144
- final FuncN <? extends R > zipFunction ;
145
- final CompositeSubscription childSubscription = new CompositeSubscription ();
178
+ private final Observer <? super R > child ;
179
+ private final FuncN <? extends R > zipFunction ;
180
+ private final CompositeSubscription childSubscription = new CompositeSubscription ();
181
+
146
182
volatile long counter ;
147
183
@ SuppressWarnings ("rawtypes" )
148
- static final AtomicLongFieldUpdater <Zip > COUNTER_UPDATER
149
- = AtomicLongFieldUpdater .newUpdater (Zip .class , "counter" );
184
+ static final AtomicLongFieldUpdater <Zip > COUNTER_UPDATER = AtomicLongFieldUpdater .newUpdater (Zip .class , "counter" );
185
+
186
+ static final int THRESHOLD = (int ) (RxRingBuffer .SIZE * 0.7 );
187
+ int emitted = 0 ; // not volatile/synchronized as accessed inside COUNTER_UPDATER block
188
+
189
+ /* initialized when started in `start` */
190
+ private Object [] observers ;
191
+ private AtomicLong requested ;
150
192
151
193
@ SuppressWarnings ("rawtypes" )
152
- public Zip (Observable [] os , final Subscriber <? super R > observer , FuncN <? extends R > zipFunction ) {
153
- this .os = os ;
154
- this .observer = observer ;
194
+ public Zip (final Subscriber <? super R > child , FuncN <? extends R > zipFunction ) {
195
+ this .child = child ;
155
196
this .zipFunction = zipFunction ;
197
+ child .add (childSubscription );
198
+ }
199
+
200
+ @ SuppressWarnings ("unchecked" )
201
+ public void start (@ SuppressWarnings ("rawtypes" ) Observable [] os , AtomicLong requested ) {
156
202
observers = new Object [os .length ];
203
+ this .requested = requested ;
157
204
for (int i = 0 ; i < os .length ; i ++) {
158
- InnerObserver io = new InnerObserver ();
205
+ InnerSubscriber io = new InnerSubscriber ();
159
206
observers [i ] = io ;
160
207
childSubscription .add (io );
161
208
}
162
209
163
- observer .add (childSubscription );
164
- }
165
-
166
- @ SuppressWarnings ("unchecked" )
167
- public void zip () {
168
210
for (int i = 0 ; i < os .length ; i ++) {
169
- os [i ].unsafeSubscribe ((InnerObserver ) observers [i ]);
211
+ os [i ].unsafeSubscribe ((InnerSubscriber ) observers [i ]);
170
212
}
171
213
}
172
214
@@ -179,51 +221,64 @@ public void zip() {
179
221
*/
180
222
@ SuppressWarnings ("unchecked" )
181
223
void tick () {
224
+ if (observers == null ) {
225
+ // nothing yet to do (initial request from Producer)
226
+ return ;
227
+ }
182
228
if (COUNTER_UPDATER .getAndIncrement (this ) == 0 ) {
183
229
do {
184
- final Object [] vs = new Object [observers .length ];
185
- boolean allHaveValues = true ;
186
- for (int i = 0 ; i < observers .length ; i ++) {
187
- Object n = ((InnerObserver ) observers [i ]).items .peek ();
188
-
189
- if (n == null ) {
190
- allHaveValues = false ;
191
- continue ;
192
- }
230
+ // we only emit if requested > 0
231
+ if (requested .get () > 0 ) {
232
+ final Object [] vs = new Object [observers .length ];
233
+ boolean allHaveValues = true ;
234
+ for (int i = 0 ; i < observers .length ; i ++) {
235
+ RxRingBuffer buffer = ((InnerSubscriber ) observers [i ]).items ;
236
+ Object n = buffer .peek ();
237
+
238
+ if (n == null ) {
239
+ allHaveValues = false ;
240
+ continue ;
241
+ }
193
242
194
- switch (on .kind (n )) {
195
- case OnNext :
196
- vs [i ] = on .getValue (n );
197
- break ;
198
- case OnCompleted :
199
- observer .onCompleted ();
200
- // we need to unsubscribe from all children since children are
201
- // independently subscribed
202
- childSubscription .unsubscribe ();
203
- return ;
204
- default :
205
- // shouldn't get here
206
- }
207
- }
208
- if (allHaveValues ) {
209
- try {
210
- // all have something so emit
211
- observer .onNext (zipFunction .call (vs ));
212
- } catch (Throwable e ) {
213
- observer .onError (OnErrorThrowable .addValueAsLastCause (e , vs ));
214
- return ;
215
- }
216
- // now remove them
217
- for (Object obj : observers ) {
218
- InnerObserver io = (InnerObserver )obj ;
219
- io .items .poll ();
220
- // eagerly check if the next item on this queue is an onComplete
221
- if (on .isCompleted (io .items .peek ())) {
222
- // it is an onComplete so shut down
223
- observer .onCompleted ();
224
- // we need to unsubscribe from all children since children are independently subscribed
243
+ if (buffer .isCompleted (n )) {
244
+ child .onCompleted ();
245
+ // we need to unsubscribe from all children since children are
246
+ // independently subscribed
225
247
childSubscription .unsubscribe ();
226
248
return ;
249
+ } else {
250
+ vs [i ] = buffer .getValue (n );
251
+ }
252
+ }
253
+ if (allHaveValues ) {
254
+ try {
255
+ // all have something so emit
256
+ child .onNext (zipFunction .call (vs ));
257
+ // we emitted so decrement the requested counter
258
+ requested .decrementAndGet ();
259
+ emitted ++;
260
+ } catch (Throwable e ) {
261
+ child .onError (OnErrorThrowable .addValueAsLastCause (e , vs ));
262
+ return ;
263
+ }
264
+ // now remove them
265
+ for (Object obj : observers ) {
266
+ RxRingBuffer buffer = ((InnerSubscriber ) obj ).items ;
267
+ buffer .poll ();
268
+ // eagerly check if the next item on this queue is an onComplete
269
+ if (buffer .isCompleted (buffer .peek ())) {
270
+ // it is an onComplete so shut down
271
+ child .onCompleted ();
272
+ // we need to unsubscribe from all children since children are independently subscribed
273
+ childSubscription .unsubscribe ();
274
+ return ;
275
+ }
276
+ }
277
+ if (emitted > THRESHOLD ) {
278
+ for (Object obj : observers ) {
279
+ ((InnerSubscriber ) obj ).request (emitted );
280
+ }
281
+ emitted = 0 ;
227
282
}
228
283
}
229
284
}
@@ -235,27 +290,36 @@ void tick() {
235
290
// used to observe each Observable we are zipping together
236
291
// it collects all items in an internal queue
237
292
@ SuppressWarnings ("rawtypes" )
238
- final class InnerObserver extends Subscriber {
293
+ final class InnerSubscriber extends Subscriber {
239
294
// Concurrent* since we need to read it from across threads
240
- final ConcurrentLinkedQueue items = new ConcurrentLinkedQueue ();
295
+ final RxRingBuffer items = RxRingBuffer .getSpmcInstance ();
296
+
297
+ @ Override
298
+ public void onStart () {
299
+ request (RxRingBuffer .SIZE );
300
+ }
241
301
242
302
@ SuppressWarnings ("unchecked" )
243
303
@ Override
244
304
public void onCompleted () {
245
- items .add ( on . completed () );
305
+ items .onCompleted ( );
246
306
tick ();
247
307
}
248
308
249
309
@ Override
250
310
public void onError (Throwable e ) {
251
- // emit error and shut down
252
- observer .onError (e );
311
+ // emit error immediately and shut down
312
+ child .onError (e );
253
313
}
254
314
255
315
@ SuppressWarnings ("unchecked" )
256
316
@ Override
257
317
public void onNext (Object t ) {
258
- items .add (on .next (t ));
318
+ try {
319
+ items .onNext (t );
320
+ } catch (MissingBackpressureException e ) {
321
+ onError (e );
322
+ }
259
323
tick ();
260
324
}
261
325
};
0 commit comments