@@ -34,7 +34,7 @@ public class RxRingBuffer implements Subscription {
34
34
public static RxRingBuffer getSpscInstance () {
35
35
if (UnsafeAccess .isUnsafeAvailable ()) {
36
36
// TODO the SpscArrayQueue isn't ready yet so using SpmcArrayQueue for now
37
- return new RxRingBuffer (SPMC_POOL , SIZE );
37
+ return new RxRingBuffer (SPSC_POOL , SIZE );
38
38
} else {
39
39
return new RxRingBuffer ();
40
40
}
@@ -306,12 +306,13 @@ private RxRingBuffer(ObjectPool<Queue<Object>> pool, int size) {
306
306
this .size = size ;
307
307
}
308
308
309
- public void release () {
310
- if (pool != null ) {
311
- Queue <Object > q = queue ;
309
+ public synchronized void release () {
310
+ Queue <Object > q = queue ;
311
+ ObjectPool <Queue <Object >> p = pool ;
312
+ if (p != null && q != null ) {
312
313
q .clear ();
313
314
queue = null ;
314
- pool .returnObject (q );
315
+ p .returnObject (q );
315
316
}
316
317
}
317
318
@@ -331,10 +332,21 @@ public void unsubscribe() {
331
332
* if more onNext are sent than have been requested
332
333
*/
333
334
public void onNext (Object o ) throws MissingBackpressureException {
334
- if (queue == null ) {
335
+ boolean iae = false ;
336
+ boolean mbe = false ;
337
+ synchronized (this ) {
338
+ Queue <Object > q = queue ;
339
+ if (q != null ) {
340
+ mbe = !q .offer (on .next (o ));
341
+ } else {
342
+ iae = true ;
343
+ }
344
+ }
345
+
346
+ if (iae ) {
335
347
throw new IllegalStateException ("This instance has been unsubscribed and the queue is no longer usable." );
336
348
}
337
- if (! queue . offer ( on . next ( o )) ) {
349
+ if (mbe ) {
338
350
throw new MissingBackpressureException ();
339
351
}
340
352
}
@@ -362,55 +374,66 @@ public int capacity() {
362
374
}
363
375
364
376
public int count () {
365
- if (queue == null ) {
377
+ Queue <Object > q = queue ;
378
+ if (q == null ) {
366
379
return 0 ;
367
380
}
368
- return queue .size ();
381
+ return q .size ();
369
382
}
370
383
371
384
public boolean isEmpty () {
372
- if (queue == null ) {
385
+ Queue <Object > q = queue ;
386
+ if (q == null ) {
373
387
return true ;
374
388
}
375
- return queue .isEmpty ();
389
+ return q .isEmpty ();
376
390
}
377
391
378
392
public Object poll () {
379
- if (queue == null ) {
380
- // we are unsubscribed and have released the undelrying queue
381
- return null ;
382
- }
383
393
Object o ;
384
- o = queue .poll ();
385
- /*
386
- * benjchristensen July 10 2014 => The check for 'queue.isEmpty()' came from a very rare concurrency bug where poll()
387
- * is invoked, then an "onNext + onCompleted/onError" arrives before hitting the if check below. In that case,
388
- * "o == null" and there is a terminal state, but now "queue.isEmpty()" and we should NOT return the terminalState.
389
- *
390
- * The queue.size() check is a double-check that works to handle this, without needing to synchronize poll with on*
391
- * or needing to enqueue terminalState.
392
- *
393
- * This did make me consider eliminating the 'terminalState' ref and enqueuing it ... but then that requires
394
- * a +1 of the size, or -1 of how many onNext can be sent. See comment on 'terminalState' above for why it
395
- * is currently the way it is.
396
- */
397
- if (o == null && terminalState != null && queue .isEmpty ()) {
398
- o = terminalState ;
399
- // once emitted we clear so a poll loop will finish
400
- terminalState = null ;
394
+ synchronized (this ) {
395
+ Queue <Object > q = queue ;
396
+ if (q == null ) {
397
+ // we are unsubscribed and have released the undelrying queue
398
+ return null ;
399
+ }
400
+ o = q .poll ();
401
+
402
+ /*
403
+ * benjchristensen July 10 2014 => The check for 'queue.isEmpty()' came from a very rare concurrency bug where poll()
404
+ * is invoked, then an "onNext + onCompleted/onError" arrives before hitting the if check below. In that case,
405
+ * "o == null" and there is a terminal state, but now "queue.isEmpty()" and we should NOT return the terminalState.
406
+ *
407
+ * The queue.size() check is a double-check that works to handle this, without needing to synchronize poll with on*
408
+ * or needing to enqueue terminalState.
409
+ *
410
+ * This did make me consider eliminating the 'terminalState' ref and enqueuing it ... but then that requires
411
+ * a +1 of the size, or -1 of how many onNext can be sent. See comment on 'terminalState' above for why it
412
+ * is currently the way it is.
413
+ */
414
+ Object ts = terminalState ;
415
+ if (o == null && ts != null && q .peek () == null ) {
416
+ o = ts ;
417
+ // once emitted we clear so a poll loop will finish
418
+ terminalState = null ;
419
+ }
401
420
}
402
421
return o ;
403
422
}
404
423
405
424
public Object peek () {
406
- if (queue == null ) {
407
- // we are unsubscribed and have released the undelrying queue
408
- return null ;
409
- }
410
425
Object o ;
411
- o = queue .peek ();
412
- if (o == null && terminalState != null && queue .isEmpty ()) {
413
- o = terminalState ;
426
+ synchronized (this ) {
427
+ Queue <Object > q = queue ;
428
+ if (q == null ) {
429
+ // we are unsubscribed and have released the undelrying queue
430
+ return null ;
431
+ }
432
+ o = q .peek ();
433
+ Object ts = terminalState ;
434
+ if (o == null && ts != null && q .peek () == null ) {
435
+ o = ts ;
436
+ }
414
437
}
415
438
return o ;
416
439
}
0 commit comments