16
16
import io .reactivex .annotations .CheckReturnValue ;
17
17
import java .util .concurrent .atomic .*;
18
18
19
+ import io .reactivex .annotations .Experimental ;
19
20
import io .reactivex .annotations .Nullable ;
20
21
import org .reactivestreams .*;
21
22
@@ -49,7 +50,10 @@ public final class UnicastProcessor<T> extends FlowableProcessor<T> {
49
50
50
51
final AtomicReference <Runnable > onTerminate ;
51
52
53
+ final boolean delayError ;
54
+
52
55
volatile boolean done ;
56
+
53
57
Throwable error ;
54
58
55
59
final AtomicReference <Subscriber <? super T >> actual ;
@@ -85,6 +89,19 @@ public static <T> UnicastProcessor<T> create(int capacityHint) {
85
89
return new UnicastProcessor <T >(capacityHint );
86
90
}
87
91
92
+ /**
93
+ * Creates an UnicastProcessor with default internal buffer capacity hint and delay error flag.
94
+ * @param <T> the value type
95
+ * @param delayError deliver pending onNext events before onError
96
+ * @return an UnicastProcessor instance
97
+ * @since 2.0.8 - experimental
98
+ */
99
+ @ CheckReturnValue
100
+ @ Experimental
101
+ public static <T > UnicastProcessor <T > create (boolean delayError ) {
102
+ return new UnicastProcessor <T >(bufferSize (), null , delayError );
103
+ }
104
+
88
105
/**
89
106
* Creates an UnicastProcessor with the given internal buffer capacity hint and a callback for
90
107
* the case when the single Subscriber cancels its subscription.
@@ -99,21 +116,38 @@ public static <T> UnicastProcessor<T> create(int capacityHint) {
99
116
*/
100
117
@ CheckReturnValue
101
118
public static <T > UnicastProcessor <T > create (int capacityHint , Runnable onCancelled ) {
119
+ ObjectHelper .requireNonNull (onCancelled , "onTerminate" );
102
120
return new UnicastProcessor <T >(capacityHint , onCancelled );
103
121
}
104
122
123
+ /**
124
+ * Creates an UnicastProcessor with the given internal buffer capacity hint, delay error flag and a callback for
125
+ * the case when the single Subscriber cancels its subscription.
126
+ *
127
+ * <p>The callback, if not null, is called exactly once and
128
+ * non-overlapped with any active replay.
129
+ *
130
+ * @param <T> the value type
131
+ * @param capacityHint the hint to size the internal unbounded buffer
132
+ * @param onCancelled the non null callback
133
+ * @param delayError deliver pending onNext events before onError
134
+ * @return an UnicastProcessor instance
135
+ * @since 2.0.8 - experimental
136
+ */
137
+ @ CheckReturnValue
138
+ @ Experimental
139
+ public static <T > UnicastProcessor <T > create (int capacityHint , Runnable onCancelled , boolean delayError ) {
140
+ ObjectHelper .requireNonNull (onCancelled , "onTerminate" );
141
+ return new UnicastProcessor <T >(capacityHint , onCancelled , delayError );
142
+ }
143
+
105
144
/**
106
145
* Creates an UnicastProcessor with the given capacity hint.
107
146
* @param capacityHint the capacity hint for the internal, unbounded queue
108
147
* @since 2.0
109
148
*/
110
149
UnicastProcessor (int capacityHint ) {
111
- this .queue = new SpscLinkedArrayQueue <T >(ObjectHelper .verifyPositive (capacityHint , "capacityHint" ));
112
- this .onTerminate = new AtomicReference <Runnable >();
113
- this .actual = new AtomicReference <Subscriber <? super T >>();
114
- this .once = new AtomicBoolean ();
115
- this .wip = new UnicastQueueSubscription ();
116
- this .requested = new AtomicLong ();
150
+ this (capacityHint ,null , true );
117
151
}
118
152
119
153
/**
@@ -124,8 +158,21 @@ public static <T> UnicastProcessor<T> create(int capacityHint, Runnable onCancel
124
158
* @since 2.0
125
159
*/
126
160
UnicastProcessor (int capacityHint , Runnable onTerminate ) {
161
+ this (capacityHint , onTerminate , true );
162
+ }
163
+
164
+ /**
165
+ * Creates an UnicastProcessor with the given capacity hint and callback
166
+ * for when the Processor is terminated normally or its single Subscriber cancels.
167
+ * @param capacityHint the capacity hint for the internal, unbounded queue
168
+ * @param onTerminate the callback to run when the Processor is terminated or cancelled, null not allowed
169
+ * @param delayError deliver pending onNext events before onError
170
+ * @since 2.0.8 - experimental
171
+ */
172
+ UnicastProcessor (int capacityHint , Runnable onTerminate , boolean delayError ) {
127
173
this .queue = new SpscLinkedArrayQueue <T >(ObjectHelper .verifyPositive (capacityHint , "capacityHint" ));
128
- this .onTerminate = new AtomicReference <Runnable >(ObjectHelper .requireNonNull (onTerminate , "onTerminate" ));
174
+ this .onTerminate = new AtomicReference <Runnable >(onTerminate );
175
+ this .delayError = delayError ;
129
176
this .actual = new AtomicReference <Subscriber <? super T >>();
130
177
this .once = new AtomicBoolean ();
131
178
this .wip = new UnicastQueueSubscription ();
@@ -143,7 +190,7 @@ void drainRegular(Subscriber<? super T> a) {
143
190
int missed = 1 ;
144
191
145
192
final SpscLinkedArrayQueue <T > q = queue ;
146
-
193
+ final boolean failFast = ! delayError ;
147
194
for (;;) {
148
195
149
196
long r = requested .get ();
@@ -155,7 +202,7 @@ void drainRegular(Subscriber<? super T> a) {
155
202
T t = q .poll ();
156
203
boolean empty = t == null ;
157
204
158
- if (checkTerminated (d , empty , a , q )) {
205
+ if (checkTerminated (failFast , d , empty , a , q )) {
159
206
return ;
160
207
}
161
208
@@ -168,7 +215,7 @@ void drainRegular(Subscriber<? super T> a) {
168
215
e ++;
169
216
}
170
217
171
- if (r == e && checkTerminated (done , q .isEmpty (), a , q )) {
218
+ if (r == e && checkTerminated (failFast , done , q .isEmpty (), a , q )) {
172
219
return ;
173
220
}
174
221
@@ -187,7 +234,7 @@ void drainFused(Subscriber<? super T> a) {
187
234
int missed = 1 ;
188
235
189
236
final SpscLinkedArrayQueue <T > q = queue ;
190
-
237
+ final boolean failFast = ! delayError ;
191
238
for (;;) {
192
239
193
240
if (cancelled ) {
@@ -198,6 +245,12 @@ void drainFused(Subscriber<? super T> a) {
198
245
199
246
boolean d = done ;
200
247
248
+ if (failFast && d && error != null ) {
249
+ q .clear ();
250
+ actual .lazySet (null );
251
+ a .onError (error );
252
+ return ;
253
+ }
201
254
a .onNext (null );
202
255
203
256
if (d ) {
@@ -246,21 +299,30 @@ void drain() {
246
299
}
247
300
}
248
301
249
- boolean checkTerminated (boolean d , boolean empty , Subscriber <? super T > a , SpscLinkedArrayQueue <T > q ) {
302
+ boolean checkTerminated (boolean failFast , boolean d , boolean empty , Subscriber <? super T > a , SpscLinkedArrayQueue <T > q ) {
250
303
if (cancelled ) {
251
304
q .clear ();
252
305
actual .lazySet (null );
253
306
return true ;
254
307
}
255
- if (d && empty ) {
256
- Throwable e = error ;
257
- actual .lazySet (null );
258
- if (e != null ) {
259
- a .onError (e );
260
- } else {
261
- a .onComplete ();
308
+
309
+ if (d ) {
310
+ if (failFast && error != null ) {
311
+ q .clear ();
312
+ actual .lazySet (null );
313
+ a .onError (error );
314
+ return true ;
315
+ }
316
+ if (empty ) {
317
+ Throwable e = error ;
318
+ actual .lazySet (null );
319
+ if (e != null ) {
320
+ a .onError (e );
321
+ } else {
322
+ a .onComplete ();
323
+ }
324
+ return true ;
262
325
}
263
- return true ;
264
326
}
265
327
266
328
return false ;
0 commit comments