19
19
import java .util .Collection ;
20
20
import java .util .List ;
21
21
import java .util .concurrent .ConcurrentLinkedQueue ;
22
+ import java .util .concurrent .atomic .AtomicBoolean ;
22
23
import java .util .concurrent .atomic .AtomicReference ;
23
24
24
25
import rx .Observable ;
@@ -269,6 +270,7 @@ private static final class AmbSubscriber<T> extends Subscriber<T> {
269
270
270
271
private final Subscriber <? super T > subscriber ;
271
272
private final Selection <T > selection ;
273
+ private boolean chosen ;
272
274
273
275
private AmbSubscriber (long requested , Subscriber <? super T > subscriber , Selection <T > selection ) {
274
276
this .subscriber = subscriber ;
@@ -306,12 +308,17 @@ public void onError(Throwable e) {
306
308
}
307
309
308
310
private boolean isSelected () {
311
+ if (chosen ) {
312
+ return true ;
313
+ }
309
314
if (selection .choice .get () == this ) {
310
315
// fast-path
316
+ chosen = true ;
311
317
return true ;
312
318
} else {
313
319
if (selection .choice .compareAndSet (null , this )) {
314
320
selection .unsubscribeOthers (this );
321
+ chosen = true ;
315
322
return true ;
316
323
} else {
317
324
// we lost so unsubscribe ... and force cleanup again due to possible race conditions
@@ -343,30 +350,54 @@ public void unsubscribeOthers(AmbSubscriber<T> notThis) {
343
350
}
344
351
345
352
}
346
-
347
- private final Iterable <? extends Observable <? extends T >> sources ;
348
- private final Selection <T > selection = new Selection <T >();
349
-
353
+
354
+ //give default access instead of private as a micro-optimization
355
+ //for access from anonymous classes below
356
+ final Iterable <? extends Observable <? extends T >> sources ;
357
+ final Selection <T > selection = new Selection <T >();
358
+ final AtomicReference <AmbSubscriber <T >> choice = selection .choice ;
359
+
350
360
private OnSubscribeAmb (Iterable <? extends Observable <? extends T >> sources ) {
351
361
this .sources = sources ;
352
362
}
353
363
354
364
@ Override
355
365
public void call (final Subscriber <? super T > subscriber ) {
366
+
367
+ //need to subscribe to all the sources
368
+ for (Observable <? extends T > source : sources ) {
369
+ if (subscriber .isUnsubscribed ()) {
370
+ return ;
371
+ }
372
+ AmbSubscriber <T > ambSubscriber = new AmbSubscriber <T >(0 , subscriber , selection );
373
+ selection .ambSubscribers .add (ambSubscriber );
374
+ // check again if choice has been made so can stop subscribing
375
+ // if all sources were backpressure aware then this check
376
+ // would be pointless given that 0 was requested above from each ambSubscriber
377
+ AmbSubscriber <T > c ;
378
+ if ((c = choice .get ()) != null ) {
379
+ // Already chose one, the rest can be skipped and we can clean up
380
+ selection .unsubscribeOthers (c );
381
+ return ;
382
+ }
383
+ source .unsafeSubscribe (ambSubscriber );
384
+ }
356
385
subscriber .add (Subscriptions .create (new Action0 () {
357
386
358
387
@ Override
359
388
public void call () {
360
- if (selection .choice .get () != null ) {
389
+ AmbSubscriber <T > c ;
390
+ if ((c = choice .get ()) != null ) {
361
391
// there is a single winner so we unsubscribe it
362
- selection . choice . get () .unsubscribe ();
392
+ c .unsubscribe ();
363
393
}
364
394
// if we are racing with others still existing, we'll also unsubscribe them
365
- if (!selection .ambSubscribers .isEmpty ()) {
366
- for (AmbSubscriber <T > other : selection .ambSubscribers ) {
395
+ Collection <AmbSubscriber <T >> ambSubs = selection .ambSubscribers ;
396
+ if (!ambSubs .isEmpty ()) {
397
+ for (AmbSubscriber <T > other : ambSubs ) {
367
398
other .unsubscribe ();
368
399
}
369
- selection . ambSubscribers .clear ();
400
+ ambSubs .clear ();
370
401
}
371
402
}
372
403
@@ -375,23 +406,25 @@ public void call() {
375
406
376
407
@ Override
377
408
public void request (long n ) {
378
- if (selection .choice .get () != null ) {
409
+ final AmbSubscriber <T > c ;
410
+ if ((c = choice .get ()) != null ) {
379
411
// propagate the request to that single Subscriber that won
380
- selection . choice . get () .requestMore (n );
412
+ c .requestMore (n );
381
413
} else {
382
- for (Observable <? extends T > source : sources ) {
383
- if (subscriber .isUnsubscribed ()) {
384
- break ;
385
- }
386
- AmbSubscriber <T > ambSubscriber = new AmbSubscriber <T >(n , subscriber , selection );
387
- selection .ambSubscribers .add (ambSubscriber );
388
- // possible race condition in previous lines ... a choice may have been made so double check (instead of synchronizing)
389
- if (selection .choice .get () != null ) {
390
- // Already chose one, the rest can be skipped and we can clean up
391
- selection .unsubscribeOthers (selection .choice .get ());
392
- break ;
414
+ //propagate the request to all the amb subscribers
415
+ for (AmbSubscriber <T > ambSubscriber : selection .ambSubscribers ) {
416
+ if (!ambSubscriber .isUnsubscribed ()) {
417
+ // make a best endeavours check to not waste requests
418
+ // if first emission has already occurred
419
+ if (choice .get () == ambSubscriber ) {
420
+ ambSubscriber .requestMore (n );
421
+ // don't need to request from other subscribers because choice has been made
422
+ // and request has gone to choice
423
+ return ;
424
+ } else {
425
+ ambSubscriber .requestMore (n );
426
+ }
393
427
}
394
- source .unsafeSubscribe (ambSubscriber );
395
428
}
396
429
}
397
430
}
0 commit comments