34
34
import static rx .Observable .create ;
35
35
36
36
import java .util .concurrent .atomic .AtomicBoolean ;
37
+ import java .util .concurrent .atomic .AtomicLong ;
38
+ import java .util .concurrent .atomic .AtomicLongFieldUpdater ;
39
+ import java .util .concurrent .atomic .AtomicReference ;
37
40
38
41
import rx .Notification ;
39
42
import rx .Observable ;
40
43
import rx .Observable .OnSubscribe ;
41
44
import rx .Observable .Operator ;
45
+ import rx .Producer ;
42
46
import rx .Scheduler ;
43
47
import rx .Subscriber ;
44
48
import rx .functions .Action0 ;
@@ -154,32 +158,40 @@ public static <T> Observable<T> redo(Observable<T> source, Func1<? super Observa
154
158
}
155
159
156
160
private Observable <T > source ;
157
- private final Func1 <? super Observable <? extends Notification <?>>, ? extends Observable <?>> f ;
161
+ private final Func1 <? super Observable <? extends Notification <?>>, ? extends Observable <?>> controlHandlerFunction ;
158
162
private boolean stopOnComplete ;
159
163
private boolean stopOnError ;
160
164
private final Scheduler scheduler ;
161
165
private final AtomicBoolean isLocked = new AtomicBoolean (true );
166
+ private final AtomicBoolean isStarted = new AtomicBoolean (false );
167
+ // incremented when requests are made, decremented when requests are fulfilled
168
+ private final AtomicLong consumerCapacity = new AtomicLong (0l );
169
+ private final AtomicReference <Producer > currentProducer = new AtomicReference <Producer >();
162
170
163
171
private OnSubscribeRedo (Observable <T > source , Func1 <? super Observable <? extends Notification <?>>, ? extends Observable <?>> f , boolean stopOnComplete , boolean stopOnError ,
164
172
Scheduler scheduler ) {
165
173
this .source = source ;
166
- this .f = f ;
174
+ this .controlHandlerFunction = f ;
167
175
this .stopOnComplete = stopOnComplete ;
168
176
this .stopOnError = stopOnError ;
169
177
this .scheduler = scheduler ;
170
178
}
171
179
172
180
@ Override
173
181
public void call (final Subscriber <? super T > child ) {
182
+ isStarted .set (false );
183
+ isLocked .set (true );
184
+ consumerCapacity .set (0l );
185
+ currentProducer .set (null );
186
+
174
187
final Scheduler .Worker inner = scheduler .createWorker ();
175
188
child .add (inner );
176
189
177
190
final CompositeSubscription sourceSubscriptions = new CompositeSubscription ();
178
191
child .add (sourceSubscriptions );
179
-
192
+
180
193
final PublishSubject <Notification <?>> terminals = PublishSubject .create ();
181
194
182
-
183
195
final Action0 subscribeToSource = new Action0 () {
184
196
@ Override
185
197
public void call () {
@@ -198,8 +210,15 @@ public void onError(Throwable e) {
198
210
199
211
@ Override
200
212
public void onNext (T v ) {
213
+ consumerCapacity .decrementAndGet ();
201
214
child .onNext (v );
202
215
}
216
+
217
+ @ Override
218
+ public void setProducer (Producer producer ) {
219
+ currentProducer .set (producer );
220
+ producer .request (consumerCapacity .get ());
221
+ }
203
222
};
204
223
// new subscription each time so if it unsubscribes itself it does not prevent retries
205
224
// by unsubscribing the child subscription
@@ -208,8 +227,10 @@ public void onNext(T v) {
208
227
}
209
228
};
210
229
211
- final Observable <?> restarts = f .call (
212
- // lifting in a custom operator to kind of do a merge/map/filter thing.
230
+ // the observable received by the control handler function will receive notifications of onCompleted in the case of 'repeat'
231
+ // type operators or notifications of onError for 'retry' this is done by lifting in a custom operator to selectively divert
232
+ // the retry/repeat relevant values to the control handler
233
+ final Observable <?> restarts = controlHandlerFunction .call (
213
234
terminals .lift (new Operator <Notification <?>, Notification <?>>() {
214
235
@ Override
215
236
public Subscriber <? super Notification <?>> call (final Subscriber <? super Notification <?>> filteredTerminals ) {
@@ -233,6 +254,11 @@ public void onNext(Notification<?> t) {
233
254
filteredTerminals .onNext (t );
234
255
}
235
256
}
257
+
258
+ @ Override
259
+ public void setProducer (Producer producer ) {
260
+ producer .request (Long .MAX_VALUE );
261
+ }
236
262
};
237
263
}
238
264
}));
@@ -255,15 +281,31 @@ public void onError(Throwable e) {
255
281
@ Override
256
282
public void onNext (Object t ) {
257
283
if (!isLocked .get () && !child .isUnsubscribed ()) {
258
- // if (!child.isUnsubscribed()) {
259
284
child .add (inner .schedule (subscribeToSource ));
260
285
}
261
286
}
287
+
288
+ @ Override
289
+ public void setProducer (Producer producer ) {
290
+ producer .request (Long .MAX_VALUE );
291
+ }
262
292
});
263
293
}
264
294
}));
265
- if (!child .isUnsubscribed ()) {
266
- child .add (inner .schedule (subscribeToSource ));
267
- }
295
+
296
+ child .setProducer (new Producer () {
297
+
298
+ @ Override
299
+ public void request (long n ) {
300
+ if (isStarted .compareAndSet (false , true )) {
301
+ consumerCapacity .set (n );
302
+ if (!child .isUnsubscribed ()) child .add (inner .schedule (subscribeToSource ));
303
+ } else if (currentProducer .get () != null ) {
304
+ consumerCapacity .getAndAdd (n );
305
+ currentProducer .get ().request (n );
306
+ }
307
+ }
308
+ });
309
+
268
310
}
269
311
}
0 commit comments