35
35
36
36
import java .util .concurrent .atomic .AtomicBoolean ;
37
37
import java .util .concurrent .atomic .AtomicLong ;
38
- import java .util .concurrent .atomic .AtomicReference ;
39
38
40
39
import rx .Notification ;
41
40
import rx .Observable ;
53
52
54
53
public final class OnSubscribeRedo <T > implements OnSubscribe <T > {
55
54
56
- static final Func1 <Observable <? extends Notification <?>>, Observable <?>> REDO_INIFINITE = new Func1 <Observable <? extends Notification <?>>, Observable <?>>() {
55
+ static final Func1 <Observable <? extends Notification <?>>, Observable <?>> REDO_INFINITE = new Func1 <Observable <? extends Notification <?>>, Observable <?>>() {
57
56
@ Override
58
57
public Observable <?> call (Observable <? extends Notification <?>> ts ) {
59
58
return ts .map (new Func1 <Notification <?>, Notification <?>>() {
@@ -120,17 +119,15 @@ public Notification<Integer> call(Notification<Integer> n, Notification<?> term)
120
119
}
121
120
122
121
public static <T > Observable <T > retry (Observable <T > source ) {
123
- // return retry(source, REDO_INIFINITE);
124
- return retry (source , Long .MAX_VALUE );
122
+ return retry (source , REDO_INFINITE );
125
123
}
126
124
127
125
public static <T > Observable <T > retry (Observable <T > source , final long count ) {
128
126
if (count < 0 )
129
127
throw new IllegalArgumentException ("count >= 0 expected" );
130
128
if (count == 0 )
131
129
return source ;
132
- // return retry(source, new RedoFinite(count));
133
- return create (new OnSubscribeRetry <T >(source , count ));
130
+ return retry (source , new RedoFinite (count ));
134
131
}
135
132
136
133
public static <T > Observable <T > retry (Observable <T > source , Func1 <? super Observable <? extends Notification <?>>, ? extends Observable <?>> notificationHandler ) {
@@ -146,7 +143,7 @@ public static <T> Observable<T> repeat(Observable<T> source) {
146
143
}
147
144
148
145
public static <T > Observable <T > repeat (Observable <T > source , Scheduler scheduler ) {
149
- return repeat (source , REDO_INIFINITE , scheduler );
146
+ return repeat (source , REDO_INFINITE , scheduler );
150
147
}
151
148
152
149
public static <T > Observable <T > repeat (Observable <T > source , final long count ) {
@@ -195,7 +192,6 @@ public void call(final Subscriber<? super T> child) {
195
192
final AtomicBoolean resumeBoundary = new AtomicBoolean (true );
196
193
// incremented when requests are made, decremented when requests are fulfilled
197
194
final AtomicLong consumerCapacity = new AtomicLong (0l );
198
- final AtomicReference <Producer > currentProducer = new AtomicReference <Producer >();
199
195
200
196
final Scheduler .Worker worker = scheduler .createWorker ();
201
197
child .add (worker );
@@ -205,6 +201,8 @@ public void call(final Subscriber<? super T> child) {
205
201
206
202
final PublishSubject <Notification <?>> terminals = PublishSubject .create ();
207
203
204
+ final ProducerArbiter arbiter = new ProducerArbiter ();
205
+
208
206
final Action0 subscribeToSource = new Action0 () {
209
207
@ Override
210
208
public void call () {
@@ -214,43 +212,37 @@ public void call() {
214
212
215
213
Subscriber <T > terminalDelegatingSubscriber = new Subscriber <T >() {
216
214
boolean done ;
215
+
217
216
@ Override
218
217
public void onCompleted () {
219
218
if (!done ) {
220
219
done = true ;
221
- synchronized (consumerCapacity ) {
222
- currentProducer .set (null );
223
- unsubscribe ();
224
- terminals .onNext (Notification .createOnCompleted ());
225
- }
220
+ unsubscribe ();
221
+ terminals .onNext (Notification .createOnCompleted ());
226
222
}
227
223
}
228
224
229
225
@ Override
230
226
public void onError (Throwable e ) {
231
227
if (!done ) {
232
228
done = true ;
233
- synchronized (consumerCapacity ) {
234
- currentProducer .set (null );
235
- unsubscribe ();
236
- terminals .onNext (Notification .createOnError (e ));
237
- }
229
+ unsubscribe ();
230
+ terminals .onNext (Notification .createOnError (e ));
238
231
}
239
232
}
240
233
241
234
@ Override
242
235
public void onNext (T v ) {
243
236
if (!done ) {
244
- synchronized (consumerCapacity ) {
245
- child .onNext (v );
246
- decrementConsumerCapacity ();
247
- }
237
+ child .onNext (v );
238
+ decrementConsumerCapacity ();
239
+ arbiter .produced (1 );
248
240
}
249
241
}
250
-
242
+
251
243
private void decrementConsumerCapacity () {
252
- // use a CAS loop because we don't want to decrement the value
253
- // if it is Long.MAX_VALUE
244
+ // use a CAS loop because we don't want to decrement the
245
+ // value if it is Long.MAX_VALUE
254
246
while (true ) {
255
247
long cc = consumerCapacity .get ();
256
248
if (cc != Long .MAX_VALUE ) {
@@ -265,13 +257,7 @@ private void decrementConsumerCapacity() {
265
257
266
258
@ Override
267
259
public void setProducer (Producer producer ) {
268
- synchronized (consumerCapacity ) {
269
- currentProducer .set (producer );
270
- long c = consumerCapacity .get ();
271
- if (c > 0 ) {
272
- producer .request (c );
273
- }
274
- }
260
+ arbiter .setProducer (producer );
275
261
}
276
262
};
277
263
// new subscription each time so if it unsubscribes itself it does not prevent retries
@@ -337,17 +323,11 @@ public void onError(Throwable e) {
337
323
@ Override
338
324
public void onNext (Object t ) {
339
325
if (!isLocked .get () && !child .isUnsubscribed ()) {
340
- final boolean scheduleNow ;
341
- synchronized (consumerCapacity ) {
342
- if (consumerCapacity .get () > 0 ) {
343
- scheduleNow = true ;
344
- } else {
345
- scheduleNow = false ;
346
- resumeBoundary .compareAndSet (false , true );
347
- }
348
- }
349
- if (scheduleNow )
326
+ if (consumerCapacity .get () > 0 ) {
350
327
worker .schedule (subscribeToSource );
328
+ } else {
329
+ resumeBoundary .compareAndSet (false , true );
330
+ }
351
331
}
352
332
}
353
333
@@ -363,26 +343,173 @@ public void setProducer(Producer producer) {
363
343
364
344
@ Override
365
345
public void request (final long n ) {
366
- final Producer producer ;
367
- final boolean requestNow ;
368
- final boolean scheduleNow ;
369
- synchronized (consumerCapacity ) {
346
+ if (n > 0 ) {
370
347
BackpressureUtils .getAndAddRequest (consumerCapacity , n );
371
- producer = currentProducer .get ();
372
- if (producer != null ) {
373
- requestNow = true ;
374
- scheduleNow = false ;
375
- } else {
376
- requestNow = false ;
377
- scheduleNow = resumeBoundary .compareAndSet (true , false );
378
- }
348
+ arbiter .request (n );
349
+ if (resumeBoundary .compareAndSet (true , false ))
350
+ worker .schedule (subscribeToSource );
379
351
}
380
- if (requestNow )
381
- producer .request (n );
382
- else if (scheduleNow )
383
- worker .schedule (subscribeToSource );
384
352
}
385
353
});
386
354
387
355
}
356
+
357
+ static final class ProducerArbiter implements Producer {
358
+ /** Guarded by this. */
359
+ boolean emitting ;
360
+ /** The current producer. Accessed while emitting. */
361
+ Producer currentProducer ;
362
+ /** The current requested count. */
363
+ long requested ;
364
+
365
+ long missedRequested ;
366
+ Producer missedProducer ;
367
+ long missedProd ;
368
+
369
+ @ Override
370
+ public void request (long n ) {
371
+ if (n <= 0 ) {
372
+ return ;
373
+ }
374
+ Producer mp ;
375
+ long mprod ;
376
+ synchronized (this ) {
377
+ if (emitting ) {
378
+ missedRequested += n ;
379
+ return ;
380
+ }
381
+ emitting = true ;
382
+ mp = missedProducer ;
383
+ mprod = missedProd ;
384
+
385
+ missedProducer = null ;
386
+ missedProd = 0L ;
387
+ }
388
+
389
+ boolean skipFinal = false ;
390
+ try {
391
+ emit (n , mp , mprod );
392
+ drainLoop ();
393
+ skipFinal = true ;
394
+ } finally {
395
+ if (!skipFinal ) {
396
+ synchronized (this ) {
397
+ emitting = false ;
398
+ }
399
+ }
400
+ }
401
+ }
402
+ public void setProducer (Producer p ) {
403
+ if (p == null ) {
404
+ throw new NullPointerException ();
405
+ }
406
+
407
+ long mreq ;
408
+ long mprod ;
409
+ synchronized (this ) {
410
+ if (emitting ) {
411
+ missedProducer = p ;
412
+ return ;
413
+ }
414
+ emitting = true ;
415
+ mreq = missedRequested ;
416
+ mprod = missedProd ;
417
+
418
+ missedRequested = 0L ;
419
+ missedProd = 0L ;
420
+ }
421
+
422
+ boolean skipFinal = false ;
423
+ try {
424
+ emit (mreq , p , mprod );
425
+ drainLoop ();
426
+ skipFinal = true ;
427
+ } finally {
428
+ if (!skipFinal ) {
429
+ synchronized (this ) {
430
+ emitting = false ;
431
+ }
432
+ }
433
+ }
434
+ }
435
+ public void produced (long n ) {
436
+ if (n <= 0 ) {
437
+ throw new IllegalArgumentException (n + " produced?!" );
438
+ }
439
+
440
+ long mreq ;
441
+ Producer mp ;
442
+ synchronized (this ) {
443
+ if (emitting ) {
444
+ missedProd += n ;
445
+ return ;
446
+ }
447
+ emitting = true ;
448
+ mreq = missedRequested ;
449
+ mp = missedProducer ;
450
+
451
+ missedRequested = 0L ;
452
+ missedProducer = null ;
453
+ }
454
+
455
+ boolean skipFinal = false ;
456
+ try {
457
+ emit (mreq , mp , n );
458
+ drainLoop ();
459
+ skipFinal = true ;
460
+ } finally {
461
+ if (!skipFinal ) {
462
+ synchronized (this ) {
463
+ emitting = false ;
464
+ }
465
+ }
466
+ }
467
+ }
468
+ void drainLoop () {
469
+ for (;;) {
470
+ long mreq ;
471
+ long mprod ;
472
+ Producer mp ;
473
+ synchronized (this ) {
474
+ mreq = missedRequested ;
475
+ mprod = missedProd ;
476
+ mp = missedProducer ;
477
+ if (mreq == 0L && mp == null && mprod == 0L ) {
478
+ emitting = false ;
479
+ return ;
480
+ }
481
+ missedRequested = 0L ;
482
+ missedProd = 0L ;
483
+ missedProducer = null ;
484
+ }
485
+ emit (mreq , mp , mprod );
486
+ }
487
+ }
488
+ void emit (long mreq , Producer mp , long mprod ) {
489
+ boolean newMp = false ;
490
+ if (mp != null ) {
491
+ newMp = true ;
492
+ currentProducer = mp ;
493
+ } else {
494
+ mp = currentProducer ;
495
+ }
496
+
497
+ long u = requested + mreq ;
498
+ if (u < 0 ) {
499
+ u = Long .MAX_VALUE ;
500
+ }
501
+ u -= mprod ;
502
+ if (u < 0 ) {
503
+ throw new IllegalStateException ("More produced than requested" );
504
+ }
505
+ requested = u ;
506
+
507
+ if (mreq > 0 && mp != null ) {
508
+ mp .request (mreq );
509
+ } else
510
+ if (newMp && u > 0 ) {
511
+ mp .request (u );
512
+ }
513
+ }
514
+ }
388
515
}
0 commit comments