19
19
import java .util .concurrent .atomic .*;
20
20
21
21
import rx .Subscriber ;
22
+ import rx .functions .Func1 ;
23
+ import rx .internal .util .UtilityFunctions ;
22
24
23
25
/**
24
26
* Utility functions for use with backpressure.
@@ -140,6 +142,59 @@ public static long addCap(long a, long b) {
140
142
* @param actual the subscriber to receive the values
141
143
*/
142
144
public static <T > void postCompleteDone (AtomicLong requested , Queue <T > queue , Subscriber <? super T > actual ) {
145
+ postCompleteDone (requested , queue , actual , UtilityFunctions .<T >identity ());
146
+ }
147
+
148
+ /**
149
+ * Accumulates requests (validated) and handles the completed mode draining of the queue based on the requests.
150
+ *
151
+ * <p>
152
+ * Post-completion backpressure handles the case when a source produces values based on
153
+ * requests when it is active but more values are available even after its completion.
154
+ * In this case, the onCompleted() can't just emit the contents of the queue but has to
155
+ * coordinate with the requested amounts. This requires two distinct modes: active and
156
+ * completed. In active mode, requests flow through and the queue is not accessed but
157
+ * in completed mode, requests no-longer reach the upstream but help in draining the queue.
158
+ *
159
+ * @param <T> the value type to emit
160
+ * @param requested the holder of current requested amount
161
+ * @param n the value requested;
162
+ * @param queue the queue holding values to be emitted after completion
163
+ * @param actual the subscriber to receive the values
164
+ * @return true if in the active mode and the request amount of n can be relayed to upstream, false if
165
+ * in the post-completed mode and the queue is draining.
166
+ */
167
+ public static <T > boolean postCompleteRequest (AtomicLong requested , long n , Queue <T > queue , Subscriber <? super T > actual ) {
168
+ return postCompleteRequest (requested , n , queue , actual , UtilityFunctions .<T >identity ());
169
+ }
170
+
171
+ /**
172
+ * Signals the completion of the main sequence and switches to post-completion replay mode
173
+ * and allows exit transformation on the queued values.
174
+ *
175
+ * <p>
176
+ * Don't modify the queue after calling this method!
177
+ *
178
+ * <p>
179
+ * Post-completion backpressure handles the case when a source produces values based on
180
+ * requests when it is active but more values are available even after its completion.
181
+ * In this case, the onCompleted() can't just emit the contents of the queue but has to
182
+ * coordinate with the requested amounts. This requires two distinct modes: active and
183
+ * completed. In active mode, requests flow through and the queue is not accessed but
184
+ * in completed mode, requests no-longer reach the upstream but help in draining the queue.
185
+ * <p>
186
+ * The algorithm utilizes the most significant bit (bit 63) of a long value (AtomicLong) since
187
+ * request amount only goes up to Long.MAX_VALUE (bits 0-62) and negative values aren't
188
+ * allowed.
189
+ *
190
+ * @param <T> the value type in the queue
191
+ * @param <R> the value type to emit
192
+ * @param requested the holder of current requested amount
193
+ * @param queue the queue holding values to be emitted after completion
194
+ * @param actual the subscriber to receive the values
195
+ * @param exitTransform the transformation to apply on the dequeued value to get the value to be emitted
196
+ */
197
+ public static <T , R > void postCompleteDone (AtomicLong requested , Queue <T > queue , Subscriber <? super R > actual , Func1 <? super T , ? extends R > exitTransform ) {
143
198
for (;;) {
144
199
long r = requested .get ();
145
200
@@ -156,15 +211,16 @@ public static <T> void postCompleteDone(AtomicLong requested, Queue<T> queue, Su
156
211
// are requests available start draining the queue
157
212
if (r != 0L ) {
158
213
// if the switch happened when there was outstanding requests, start draining
159
- postCompleteDrain (requested , queue , actual );
214
+ postCompleteDrain (requested , queue , actual , exitTransform );
160
215
}
161
216
return ;
162
217
}
163
218
}
164
219
}
165
220
166
221
/**
167
- * Accumulates requests (validated) and handles the completed mode draining of the queue based on the requests.
222
+ * Accumulates requests (validated) and handles the completed mode draining of the queue based on the requests
223
+ * and allows exit transformation on the queued values.
168
224
*
169
225
* <p>
170
226
* Post-completion backpressure handles the case when a source produces values based on
@@ -174,15 +230,17 @@ public static <T> void postCompleteDone(AtomicLong requested, Queue<T> queue, Su
174
230
* completed. In active mode, requests flow through and the queue is not accessed but
175
231
* in completed mode, requests no-longer reach the upstream but help in draining the queue.
176
232
*
177
- * @param <T> the value type to emit
233
+ * @param <T> the value type in the queue
234
+ * @param <R> the value type to emit
178
235
* @param requested the holder of current requested amount
179
236
* @param n the value requested;
180
237
* @param queue the queue holding values to be emitted after completion
181
238
* @param actual the subscriber to receive the values
239
+ * @param exitTransform the transformation to apply on the dequeued value to get the value to be emitted
182
240
* @return true if in the active mode and the request amount of n can be relayed to upstream, false if
183
241
* in the post-completed mode and the queue is draining.
184
242
*/
185
- public static <T > boolean postCompleteRequest (AtomicLong requested , long n , Queue <T > queue , Subscriber <? super T > actual ) {
243
+ public static <T , R > boolean postCompleteRequest (AtomicLong requested , long n , Queue <T > queue , Subscriber <? super R > actual , Func1 <? super T , ? extends R > exitTransform ) {
186
244
if (n < 0L ) {
187
245
throw new IllegalArgumentException ("n >= 0 required but it was " + n );
188
246
}
@@ -209,7 +267,7 @@ public static <T> boolean postCompleteRequest(AtomicLong requested, long n, Queu
209
267
// if there was no outstanding request before and in
210
268
// the post-completed state, start draining
211
269
if (r == COMPLETED_MASK ) {
212
- postCompleteDrain (requested , queue , actual );
270
+ postCompleteDrain (requested , queue , actual , exitTransform );
213
271
return false ;
214
272
}
215
273
// returns true for active mode and false if the completed flag was set
@@ -219,16 +277,37 @@ public static <T> boolean postCompleteRequest(AtomicLong requested, long n, Queu
219
277
}
220
278
221
279
/**
222
- * Drains the queue based on the outstanding requests in post-completed mode (only!).
280
+ * Drains the queue based on the outstanding requests in post-completed mode (only!)
281
+ * and allows exit transformation on the queued values.
223
282
*
224
- * @param <T> the value type to emit
283
+ * @param <T> the value type in the queue
284
+ * @param <R> the value type to emit
225
285
* @param requested the holder of current requested amount
226
286
* @param queue the queue holding values to be emitted after completion
227
- * @param actual the subscriber to receive the values
287
+ * @param subscriber the subscriber to receive the values
288
+ * @param exitTransform the transformation to apply on the dequeued value to get the value to be emitted
228
289
*/
229
- static <T > void postCompleteDrain (AtomicLong requested , Queue <T > queue , Subscriber <? super T > subscriber ) {
290
+ static <T , R > void postCompleteDrain (AtomicLong requested , Queue <T > queue , Subscriber <? super R > subscriber , Func1 <? super T , ? extends R > exitTransform ) {
230
291
231
292
long r = requested .get ();
293
+
294
+ // Run on a fast-path if the downstream is unbounded
295
+ if (r == Long .MAX_VALUE ) {
296
+ for (;;) {
297
+ if (subscriber .isUnsubscribed ()) {
298
+ return ;
299
+ }
300
+
301
+ T v = queue .poll ();
302
+
303
+ if (v == null ) {
304
+ subscriber .onCompleted ();
305
+ return ;
306
+ }
307
+
308
+ subscriber .onNext (exitTransform .call (v ));
309
+ }
310
+ }
232
311
/*
233
312
* Since we are supposed to be in the post-complete state,
234
313
* requested will have its top bit set.
@@ -264,7 +343,7 @@ static <T> void postCompleteDrain(AtomicLong requested, Queue<T> queue, Subscrib
264
343
return ;
265
344
}
266
345
267
- subscriber .onNext (v );
346
+ subscriber .onNext (exitTransform . call ( v ) );
268
347
269
348
e ++;
270
349
}
0 commit comments