1515 */
1616package rx .internal .operators ;
1717
18- import java .util .concurrent .ConcurrentLinkedQueue ;
18+ import java .util .concurrent .atomic . AtomicLong ;
1919import java .util .concurrent .atomic .AtomicLongFieldUpdater ;
2020
2121import rx .Observable ;
2222import rx .Observable .Operator ;
2323import rx .Observer ;
24+ import rx .Producer ;
2425import rx .Subscriber ;
26+ import rx .exceptions .MissingBackpressureException ;
2527import rx .exceptions .OnErrorThrowable ;
2628import rx .functions .Func2 ;
2729import rx .functions .Func3 ;
3335import rx .functions .Func9 ;
3436import rx .functions .FuncN ;
3537import rx .functions .Functions ;
38+ import rx .internal .util .RxRingBuffer ;
3639import rx .subscriptions .CompositeSubscription ;
3740
3841/**
4851 * <p>
4952 * The resulting Observable returned from zip will invoke <code>onNext</code> as many times as the
5053 * 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
5257 */
5358public final class OperatorZip <R > implements Operator <R , Observable <?>[]> {
5459 /*
@@ -104,69 +109,106 @@ public OperatorZip(Func9 f) {
104109
105110 @ SuppressWarnings ("rawtypes" )
106111 @ 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+ }
109119
110- boolean started = false ;
120+ private final class ZipSubscriber extends Subscriber < Observable []> {
111121
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 ;
119125
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 ();
123140 }
141+ }
124142
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 );
133155 }
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+ }
134174
135- };
136175 }
137176
138- static final NotificationLite <Object > on = NotificationLite .instance ();
139177 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+
146182 volatile long counter ;
147183 @ 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 ;
150192
151193 @ 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 ;
155196 this .zipFunction = zipFunction ;
197+ child .add (childSubscription );
198+ }
199+
200+ @ SuppressWarnings ("unchecked" )
201+ public void start (@ SuppressWarnings ("rawtypes" ) Observable [] os , AtomicLong requested ) {
156202 observers = new Object [os .length ];
203+ this .requested = requested ;
157204 for (int i = 0 ; i < os .length ; i ++) {
158- InnerObserver io = new InnerObserver ();
205+ InnerSubscriber io = new InnerSubscriber ();
159206 observers [i ] = io ;
160207 childSubscription .add (io );
161208 }
162209
163- observer .add (childSubscription );
164- }
165-
166- @ SuppressWarnings ("unchecked" )
167- public void zip () {
168210 for (int i = 0 ; i < os .length ; i ++) {
169- os [i ].unsafeSubscribe ((InnerObserver ) observers [i ]);
211+ os [i ].unsafeSubscribe ((InnerSubscriber ) observers [i ]);
170212 }
171213 }
172214
@@ -179,51 +221,64 @@ public void zip() {
179221 */
180222 @ SuppressWarnings ("unchecked" )
181223 void tick () {
224+ if (observers == null ) {
225+ // nothing yet to do (initial request from Producer)
226+ return ;
227+ }
182228 if (COUNTER_UPDATER .getAndIncrement (this ) == 0 ) {
183229 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+ }
193242
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
225247 childSubscription .unsubscribe ();
226248 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 ;
227282 }
228283 }
229284 }
@@ -235,27 +290,36 @@ void tick() {
235290 // used to observe each Observable we are zipping together
236291 // it collects all items in an internal queue
237292 @ SuppressWarnings ("rawtypes" )
238- final class InnerObserver extends Subscriber {
293+ final class InnerSubscriber extends Subscriber {
239294 // 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+ }
241301
242302 @ SuppressWarnings ("unchecked" )
243303 @ Override
244304 public void onCompleted () {
245- items .add ( on . completed () );
305+ items .onCompleted ( );
246306 tick ();
247307 }
248308
249309 @ Override
250310 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 );
253313 }
254314
255315 @ SuppressWarnings ("unchecked" )
256316 @ Override
257317 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+ }
259323 tick ();
260324 }
261325 };
0 commit comments