Skip to content

Commit b6d59a4

Browse files
author
Aaron Tull
committed
Merge pull request #3491 from akarnokd/ScanUnboundedRequestFix1x
1.x: make scan's delayed Producer independent of event serialization
2 parents 16e55c3 + 91d3d3a commit b6d59a4

File tree

4 files changed

+156
-105
lines changed

4 files changed

+156
-105
lines changed

src/main/java/rx/internal/operators/OperatorScan.java

Lines changed: 55 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package rx.internal.operators;
1717

1818
import java.util.Queue;
19+
import java.util.concurrent.atomic.AtomicLong;
1920

2021
import rx.*;
2122
import rx.Observable.Operator;
@@ -175,12 +176,10 @@ static final class InitialProducer<R> implements Producer, Observer<R> {
175176
boolean missed;
176177
/** Missed a request. */
177178
long missedRequested;
178-
/** Missed a producer. */
179-
Producer missedProducer;
180179
/** The current requested amount. */
181-
long requested;
180+
final AtomicLong requested;
182181
/** The current producer. */
183-
Producer producer;
182+
volatile Producer producer;
184183

185184
volatile boolean done;
186185
Throwable error;
@@ -196,41 +195,7 @@ public InitialProducer(R initialValue, Subscriber<? super R> child) {
196195
}
197196
this.queue = q;
198197
q.offer(NotificationLite.instance().next(initialValue));
199-
}
200-
201-
@Override
202-
public void request(long n) {
203-
if (n < 0L) {
204-
throw new IllegalArgumentException("n >= required but it was " + n);
205-
} else
206-
if (n != 0L) {
207-
synchronized (this) {
208-
if (emitting) {
209-
long mr = missedRequested;
210-
long mu = mr + n;
211-
if (mu < 0L) {
212-
mu = Long.MAX_VALUE;
213-
}
214-
missedRequested = mu;
215-
return;
216-
}
217-
emitting = true;
218-
}
219-
220-
long r = requested;
221-
long u = r + n;
222-
if (u < 0L) {
223-
u = Long.MAX_VALUE;
224-
}
225-
requested = u;
226-
227-
Producer p = producer;
228-
if (p != null) {
229-
p.request(n);
230-
}
231-
232-
emitLoop();
233-
}
198+
this.requested = new AtomicLong();
234199
}
235200

236201
@Override
@@ -270,23 +235,51 @@ public void onCompleted() {
270235
emit();
271236
}
272237

238+
@Override
239+
public void request(long n) {
240+
if (n < 0L) {
241+
throw new IllegalArgumentException("n >= required but it was " + n);
242+
} else
243+
if (n != 0L) {
244+
BackpressureUtils.getAndAddRequest(requested, n);
245+
Producer p = producer;
246+
if (p == null) {
247+
// not synchronizing on this to avoid clash with emit()
248+
synchronized (requested) {
249+
p = producer;
250+
if (p == null) {
251+
long mr = missedRequested;
252+
missedRequested = BackpressureUtils.addCap(mr, n);
253+
}
254+
}
255+
}
256+
if (p != null) {
257+
p.request(n);
258+
}
259+
emit();
260+
}
261+
}
262+
273263
public void setProducer(Producer p) {
274264
if (p == null) {
275265
throw new NullPointerException();
276266
}
277-
synchronized (this) {
278-
if (emitting) {
279-
missedProducer = p;
280-
return;
267+
long mr;
268+
// not synchronizing on this to avoid clash with emit()
269+
synchronized (requested) {
270+
if (producer != null) {
271+
throw new IllegalStateException("Can't set more than one Producer!");
281272
}
282-
emitting = true;
273+
// request one less because of the initial value, this happens once
274+
mr = missedRequested - 1;
275+
missedRequested = 0L;
276+
producer = p;
283277
}
284-
producer = p;
285-
long r = requested;
286-
if (r != 0L) {
287-
p.request(r);
278+
279+
if (mr > 0L) {
280+
p.request(mr);
288281
}
289-
emitLoop();
282+
emit();
290283
}
291284

292285
void emit() {
@@ -304,14 +297,17 @@ void emitLoop() {
304297
final Subscriber<? super R> child = this.child;
305298
final Queue<Object> queue = this.queue;
306299
final NotificationLite<R> nl = NotificationLite.instance();
307-
long r = requested;
300+
AtomicLong requested = this.requested;
301+
302+
long r = requested.get();
308303
for (;;) {
309304
boolean max = r == Long.MAX_VALUE;
310305
boolean d = done;
311306
boolean empty = queue.isEmpty();
312307
if (checkTerminated(d, empty, child)) {
313308
return;
314309
}
310+
long e = 0L;
315311
while (r != 0L) {
316312
d = done;
317313
Object o = queue.poll();
@@ -325,52 +321,25 @@ void emitLoop() {
325321
R v = nl.getValue(o);
326322
try {
327323
child.onNext(v);
328-
} catch (Throwable e) {
329-
Exceptions.throwIfFatal(e);
330-
child.onError(OnErrorThrowable.addValueAsLastCause(e, v));
324+
} catch (Throwable ex) {
325+
Exceptions.throwIfFatal(ex);
326+
child.onError(OnErrorThrowable.addValueAsLastCause(ex, v));
331327
return;
332328
}
333-
if (!max) {
334-
r--;
335-
}
329+
r--;
330+
e--;
336331
}
337-
if (!max) {
338-
requested = r;
332+
333+
if (e != 0 && !max) {
334+
r = requested.addAndGet(e);
339335
}
340336

341-
Producer p;
342-
long mr;
343337
synchronized (this) {
344-
p = missedProducer;
345-
mr = missedRequested;
346-
if (!missed && p == null && mr == 0L) {
338+
if (!missed) {
347339
emitting = false;
348340
return;
349341
}
350342
missed = false;
351-
missedProducer = null;
352-
missedRequested = 0L;
353-
}
354-
355-
if (mr != 0L && !max) {
356-
long u = r + mr;
357-
if (u < 0L) {
358-
u = Long.MAX_VALUE;
359-
}
360-
requested = u;
361-
r = u;
362-
}
363-
364-
if (p != null) {
365-
producer = p;
366-
if (r != 0L) {
367-
p.request(r);
368-
}
369-
} else {
370-
p = producer;
371-
if (p != null && mr != 0L) {
372-
p.request(mr);
373-
}
374343
}
375344
}
376345
}

src/main/java/rx/internal/producers/ProducerObserverArbiter.java

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import rx.*;
2121
import rx.Observer;
2222
import rx.exceptions.*;
23+
import rx.internal.operators.BackpressureUtils;
2324

2425
/**
2526
* Producer that serializes any event emission with requesting and producer changes.
@@ -135,6 +136,7 @@ public void request(long n) {
135136
}
136137
emitting = true;
137138
}
139+
Producer p = currentProducer;
138140
boolean skipFinal = false;
139141
try {
140142
long r = requested;
@@ -143,12 +145,7 @@ public void request(long n) {
143145
u = Long.MAX_VALUE;
144146
}
145147
requested = u;
146-
147-
Producer p = currentProducer;
148-
if (p != null) {
149-
p.request(n);
150-
}
151-
148+
152149
emitLoop();
153150
skipFinal = true;
154151
} finally {
@@ -158,6 +155,9 @@ public void request(long n) {
158155
}
159156
}
160157
}
158+
if (p != null) {
159+
p.request(n);
160+
}
161161
}
162162

163163
public void setProducer(Producer p) {
@@ -169,12 +169,9 @@ public void setProducer(Producer p) {
169169
emitting = true;
170170
}
171171
boolean skipFinal = false;
172+
currentProducer = p;
173+
long r = requested;
172174
try {
173-
currentProducer = p;
174-
long r = requested;
175-
if (p != null && r != 0) {
176-
p.request(r);
177-
}
178175
emitLoop();
179176
skipFinal = true;
180177
} finally {
@@ -184,17 +181,24 @@ public void setProducer(Producer p) {
184181
}
185182
}
186183
}
184+
if (p != null && r != 0) {
185+
p.request(r);
186+
}
187187
}
188188

189189
void emitLoop() {
190190
final Subscriber<? super T> c = child;
191191

192+
long toRequest = 0L;
193+
Producer requestFrom = null;
194+
192195
outer:
193196
for (;;) {
194197
long localRequested;
195198
Producer localProducer;
196199
Object localTerminal;
197200
List<T> q;
201+
boolean quit = false;
198202
synchronized (this) {
199203
localRequested = missedRequested;
200204
localProducer = missedProducer;
@@ -203,13 +207,21 @@ void emitLoop() {
203207
if (localRequested == 0L && localProducer == null && q == null
204208
&& localTerminal == null) {
205209
emitting = false;
206-
return;
210+
quit = true;
211+
} else {
212+
missedRequested = 0L;
213+
missedProducer = null;
214+
queue = null;
215+
missedTerminal = null;
207216
}
208-
missedRequested = 0L;
209-
missedProducer = null;
210-
queue = null;
211-
missedTerminal = null;
212217
}
218+
if (quit) {
219+
if (toRequest != 0L && requestFrom != null) {
220+
requestFrom.request(toRequest);
221+
}
222+
return;
223+
}
224+
213225
boolean empty = q == null || q.isEmpty();
214226
if (localTerminal != null) {
215227
if (localTerminal != Boolean.TRUE) {
@@ -266,13 +278,15 @@ void emitLoop() {
266278
} else {
267279
currentProducer = localProducer;
268280
if (r != 0L) {
269-
localProducer.request(r);
281+
toRequest = BackpressureUtils.addCap(toRequest, r);
282+
requestFrom = localProducer;
270283
}
271284
}
272285
} else {
273286
Producer p = currentProducer;
274287
if (p != null && localRequested != 0L) {
275-
p.request(localRequested);
288+
toRequest = BackpressureUtils.addCap(toRequest, localRequested);
289+
requestFrom = p;
276290
}
277291
}
278292
}

src/test/java/rx/internal/operators/OperatorScanTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,4 +426,24 @@ public Integer call(Integer t1, Integer t2) {
426426
ts.assertNoErrors();
427427
ts.assertCompleted();
428428
}
429+
430+
@Test(timeout = 1000)
431+
public void testUnboundedSource() {
432+
Observable.range(0, Integer.MAX_VALUE)
433+
.scan(0, new Func2<Integer, Integer, Integer>() {
434+
@Override
435+
public Integer call(Integer a, Integer b) {
436+
return 0;
437+
}
438+
})
439+
.subscribe(new TestSubscriber<Integer>() {
440+
int count;
441+
@Override
442+
public void onNext(Integer t) {
443+
if (++count == 2) {
444+
unsubscribe();
445+
}
446+
}
447+
});
448+
}
429449
}

0 commit comments

Comments
 (0)