@@ -206,7 +206,6 @@ public void onNext(T t) {
206
206
*/
207
207
private static class State <T > {
208
208
private long outstandingRequests = -1 ;
209
- private long emittedSinceRequest = 0 ;
210
209
private OriginSubscriber <T > origin ;
211
210
// using AtomicLong to simplify mutating it, not for thread-safety since we're synchronizing access to this class
212
211
// using LinkedHashMap so the order of Subscribers having onNext invoked is deterministic (same each time the code is run)
@@ -225,15 +224,13 @@ public synchronized void setOrigin(OriginSubscriber<T> o) {
225
224
public synchronized boolean canEmitWithDecrement () {
226
225
if (outstandingRequests > 0 ) {
227
226
outstandingRequests --;
228
- emittedSinceRequest ++;
229
227
return true ;
230
228
}
231
229
return false ;
232
230
}
233
231
234
232
public synchronized void incrementOutstandingAfterFailedEmit () {
235
233
outstandingRequests ++;
236
- emittedSinceRequest --;
237
234
}
238
235
239
236
public synchronized Subscriber <? super T >[] getSubscribers () {
@@ -243,50 +240,55 @@ public synchronized Subscriber<? super T>[] getSubscribers() {
243
240
/**
244
241
* @return long outstandingRequests
245
242
*/
246
- public synchronized long requestFromSubscriber (Subscriber <? super T > subscriber , Long request ) {
247
- AtomicLong r = ss .get (subscriber );
243
+ public synchronized long requestFromSubscriber (Subscriber <? super T > subscriber , long request ) {
244
+ Map <Subscriber <? super T >, AtomicLong > subs = ss ;
245
+ AtomicLong r = subs .get (subscriber );
248
246
if (r == null ) {
249
- ss .put (subscriber , new AtomicLong (request ));
247
+ subs .put (subscriber , new AtomicLong (request ));
250
248
} else {
251
- if (r .get () != Long .MAX_VALUE ) {
252
- if (request == Long .MAX_VALUE ) {
253
- r .set (Long .MAX_VALUE );
254
- } else {
255
- r .addAndGet (request .longValue ());
249
+ do {
250
+ long current = r .get ();
251
+ if (current == Long .MAX_VALUE ) {
252
+ break ;
256
253
}
257
- }
254
+ long u = current + request ;
255
+ if (u < 0 ) {
256
+ u = Long .MAX_VALUE ;
257
+ }
258
+ if (r .compareAndSet (current , u )) {
259
+ break ;
260
+ }
261
+ } while (true );
258
262
}
259
263
260
- return resetAfterSubscriberUpdate ();
264
+ return resetAfterSubscriberUpdate (subs );
261
265
}
262
266
263
267
public synchronized void removeSubscriber (Subscriber <? super T > subscriber ) {
264
- ss .remove (subscriber );
265
- resetAfterSubscriberUpdate ();
268
+ Map <Subscriber <? super T >, AtomicLong > subs = ss ;
269
+ subs .remove (subscriber );
270
+ resetAfterSubscriberUpdate (subs );
266
271
}
267
272
268
273
@ SuppressWarnings ("unchecked" )
269
- private long resetAfterSubscriberUpdate () {
270
- subscribers = new Subscriber [ss .size ()];
274
+ private long resetAfterSubscriberUpdate (Map < Subscriber <? super T >, AtomicLong > subs ) {
275
+ Subscriber <? super T >[] subscriberArray = new Subscriber [subs .size ()];
271
276
int i = 0 ;
272
- for (Subscriber <? super T > s : ss .keySet ()) {
273
- subscribers [i ++] = s ;
274
- }
275
-
276
277
long lowest = -1 ;
277
- for (AtomicLong l : ss .values ()) {
278
- // decrement all we have emitted since last request
279
- long c = l .addAndGet (-emittedSinceRequest );
278
+ for (Map .Entry <Subscriber <? super T >, AtomicLong > e : subs .entrySet ()) {
279
+ subscriberArray [i ++] = e .getKey ();
280
+ AtomicLong l = e .getValue ();
281
+ long c = l .get ();
280
282
if (lowest == -1 || c < lowest ) {
281
283
lowest = c ;
282
284
}
283
285
}
286
+ this .subscribers = subscriberArray ;
284
287
/*
285
288
* when receiving a request from a subscriber we reset 'outstanding' to the lowest of all subscribers
286
289
*/
287
290
outstandingRequests = lowest ;
288
- emittedSinceRequest = 0 ;
289
- return outstandingRequests ;
291
+ return lowest ;
290
292
}
291
293
}
292
294
@@ -299,7 +301,7 @@ private static class RequestHandler<T> {
299
301
@ SuppressWarnings ("rawtypes" )
300
302
static final AtomicLongFieldUpdater <RequestHandler > WIP = AtomicLongFieldUpdater .newUpdater (RequestHandler .class , "wip" );
301
303
302
- public void requestFromChildSubscriber (Subscriber <? super T > subscriber , Long request ) {
304
+ public void requestFromChildSubscriber (Subscriber <? super T > subscriber , long request ) {
303
305
state .requestFromSubscriber (subscriber , request );
304
306
OriginSubscriber <T > originSubscriber = state .getOrigin ();
305
307
if (originSubscriber != null ) {
@@ -333,6 +335,11 @@ private void requestMoreAfterEmission(int emitted) {
333
335
334
336
public void drainQueue (OriginSubscriber <T > originSubscriber ) {
335
337
if (WIP .getAndIncrement (this ) == 0 ) {
338
+ State <T > localState = state ;
339
+ Map <Subscriber <? super T >, AtomicLong > localMap = localState .ss ;
340
+ RxRingBuffer localBuffer = originSubscriber .buffer ;
341
+ NotificationLite <T > nl = notifier ;
342
+
336
343
int emitted = 0 ;
337
344
do {
338
345
/*
@@ -345,26 +352,24 @@ public void drainQueue(OriginSubscriber<T> originSubscriber) {
345
352
* If we want to batch this then we need to account for new subscribers arriving with a lower request count
346
353
* concurrently while iterating the batch ... or accept that they won't
347
354
*/
355
+
348
356
while (true ) {
349
- boolean shouldEmit = state .canEmitWithDecrement ();
357
+ boolean shouldEmit = localState .canEmitWithDecrement ();
350
358
if (!shouldEmit ) {
351
359
break ;
352
360
}
353
- Object o = originSubscriber . buffer .poll ();
361
+ Object o = localBuffer .poll ();
354
362
if (o == null ) {
355
363
// nothing in buffer so increment outstanding back again
356
- state .incrementOutstandingAfterFailedEmit ();
364
+ localState .incrementOutstandingAfterFailedEmit ();
357
365
break ;
358
366
}
359
367
360
- if (notifier .isCompleted (o )) {
361
- for (Subscriber <? super T > s : state .getSubscribers ()) {
362
- notifier .accept (s , o );
363
- }
364
-
365
- } else {
366
- for (Subscriber <? super T > s : state .getSubscribers ()) {
367
- notifier .accept (s , o );
368
+ for (Subscriber <? super T > s : localState .getSubscribers ()) {
369
+ AtomicLong req = localMap .get (s );
370
+ if (req != null ) { // null req indicates a concurrent unsubscription happened
371
+ nl .accept (s , o );
372
+ req .decrementAndGet ();
368
373
}
369
374
}
370
375
emitted ++;
0 commit comments