@@ -206,7 +206,6 @@ public void onNext(T t) {
206206 */
207207 private static class State <T > {
208208 private long outstandingRequests = -1 ;
209- private long emittedSinceRequest = 0 ;
210209 private OriginSubscriber <T > origin ;
211210 // using AtomicLong to simplify mutating it, not for thread-safety since we're synchronizing access to this class
212211 // 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) {
225224 public synchronized boolean canEmitWithDecrement () {
226225 if (outstandingRequests > 0 ) {
227226 outstandingRequests --;
228- emittedSinceRequest ++;
229227 return true ;
230228 }
231229 return false ;
232230 }
233231
234232 public synchronized void incrementOutstandingAfterFailedEmit () {
235233 outstandingRequests ++;
236- emittedSinceRequest --;
237234 }
238235
239236 public synchronized Subscriber <? super T >[] getSubscribers () {
@@ -243,50 +240,55 @@ public synchronized Subscriber<? super T>[] getSubscribers() {
243240 /**
244241 * @return long outstandingRequests
245242 */
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 );
248246 if (r == null ) {
249- ss .put (subscriber , new AtomicLong (request ));
247+ subs .put (subscriber , new AtomicLong (request ));
250248 } 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 ;
256253 }
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 );
258262 }
259263
260- return resetAfterSubscriberUpdate ();
264+ return resetAfterSubscriberUpdate (subs );
261265 }
262266
263267 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 );
266271 }
267272
268273 @ 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 ()];
271276 int i = 0 ;
272- for (Subscriber <? super T > s : ss .keySet ()) {
273- subscribers [i ++] = s ;
274- }
275-
276277 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 ();
280282 if (lowest == -1 || c < lowest ) {
281283 lowest = c ;
282284 }
283285 }
286+ this .subscribers = subscriberArray ;
284287 /*
285288 * when receiving a request from a subscriber we reset 'outstanding' to the lowest of all subscribers
286289 */
287290 outstandingRequests = lowest ;
288- emittedSinceRequest = 0 ;
289- return outstandingRequests ;
291+ return lowest ;
290292 }
291293 }
292294
@@ -299,7 +301,7 @@ private static class RequestHandler<T> {
299301 @ SuppressWarnings ("rawtypes" )
300302 static final AtomicLongFieldUpdater <RequestHandler > WIP = AtomicLongFieldUpdater .newUpdater (RequestHandler .class , "wip" );
301303
302- public void requestFromChildSubscriber (Subscriber <? super T > subscriber , Long request ) {
304+ public void requestFromChildSubscriber (Subscriber <? super T > subscriber , long request ) {
303305 state .requestFromSubscriber (subscriber , request );
304306 OriginSubscriber <T > originSubscriber = state .getOrigin ();
305307 if (originSubscriber != null ) {
@@ -333,6 +335,11 @@ private void requestMoreAfterEmission(int emitted) {
333335
334336 public void drainQueue (OriginSubscriber <T > originSubscriber ) {
335337 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+
336343 int emitted = 0 ;
337344 do {
338345 /*
@@ -345,26 +352,24 @@ public void drainQueue(OriginSubscriber<T> originSubscriber) {
345352 * If we want to batch this then we need to account for new subscribers arriving with a lower request count
346353 * concurrently while iterating the batch ... or accept that they won't
347354 */
355+
348356 while (true ) {
349- boolean shouldEmit = state .canEmitWithDecrement ();
357+ boolean shouldEmit = localState .canEmitWithDecrement ();
350358 if (!shouldEmit ) {
351359 break ;
352360 }
353- Object o = originSubscriber . buffer .poll ();
361+ Object o = localBuffer .poll ();
354362 if (o == null ) {
355363 // nothing in buffer so increment outstanding back again
356- state .incrementOutstandingAfterFailedEmit ();
364+ localState .incrementOutstandingAfterFailedEmit ();
357365 break ;
358366 }
359367
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 ();
368373 }
369374 }
370375 emitted ++;
0 commit comments