13
13
14
14
package io .reactivex .subjects ;
15
15
16
+ import io .reactivex .annotations .Experimental ;
16
17
import io .reactivex .annotations .Nullable ;
17
18
import io .reactivex .plugins .RxJavaPlugins ;
18
19
import java .util .concurrent .atomic .*;
@@ -53,6 +54,9 @@ public final class UnicastSubject<T> extends Subject<T> {
53
54
/** The optional callback when the Subject gets cancelled or terminates. */
54
55
final AtomicReference <Runnable > onTerminate ;
55
56
57
+ /** deliver onNext events before error event */
58
+ final boolean delayError ;
59
+
56
60
/** Indicates the single observer has cancelled. */
57
61
volatile boolean disposed ;
58
62
@@ -79,7 +83,7 @@ public final class UnicastSubject<T> extends Subject<T> {
79
83
*/
80
84
@ CheckReturnValue
81
85
public static <T > UnicastSubject <T > create () {
82
- return new UnicastSubject <T >(bufferSize ());
86
+ return new UnicastSubject <T >(bufferSize (), true );
83
87
}
84
88
85
89
/**
@@ -90,7 +94,7 @@ public static <T> UnicastSubject<T> create() {
90
94
*/
91
95
@ CheckReturnValue
92
96
public static <T > UnicastSubject <T > create (int capacityHint ) {
93
- return new UnicastSubject <T >(capacityHint );
97
+ return new UnicastSubject <T >(capacityHint , true );
94
98
}
95
99
96
100
/**
@@ -102,37 +106,91 @@ public static <T> UnicastSubject<T> create(int capacityHint) {
102
106
*
103
107
* @param <T> the value type
104
108
* @param capacityHint the hint to size the internal unbounded buffer
105
- * @param onCancelled the non null callback
109
+ * @param onTerminate the callback to run when the Subject is terminated or cancelled, null not allowed
110
+ * @return an UnicastSubject instance
111
+ */
112
+ @ CheckReturnValue
113
+ public static <T > UnicastSubject <T > create (int capacityHint , Runnable onTerminate ) {
114
+ return new UnicastSubject <T >(capacityHint , onTerminate , true );
115
+ }
116
+
117
+ /**
118
+ * Creates an UnicastSubject with the given internal buffer capacity hint, delay error flag and
119
+ * a callback for the case when the single Subscriber cancels its subscription.
120
+ *
121
+ * <p>The callback, if not null, is called exactly once and
122
+ * non-overlapped with any active replay.
123
+ *
124
+ * @param <T> the value type
125
+ * @param capacityHint the hint to size the internal unbounded buffer
126
+ * @param onTerminate the callback to run when the Subject is terminated or cancelled, null not allowed
127
+ * @param delayError deliver pending onNext events before onError
106
128
* @return an UnicastSubject instance
129
+ * @since 2.0.8 - experimental
107
130
*/
108
131
@ CheckReturnValue
109
- public static <T > UnicastSubject <T > create (int capacityHint , Runnable onCancelled ) {
110
- return new UnicastSubject <T >(capacityHint , onCancelled );
132
+ @ Experimental
133
+ public static <T > UnicastSubject <T > create (int capacityHint , Runnable onTerminate , boolean delayError ) {
134
+ return new UnicastSubject <T >(capacityHint , onTerminate , delayError );
111
135
}
112
136
113
137
/**
114
- * Creates an UnicastSubject with the given capacity hint.
138
+ * Creates an UnicastSubject with an internal buffer capacity hint 16 and given delay error flag.
139
+ *
140
+ * <p>The callback, if not null, is called exactly once and
141
+ * non-overlapped with any active replay.
142
+ *
143
+ * @param <T> the value type
144
+ * @param delayError deliver pending onNext events before onError
145
+ * @return an UnicastSubject instance
146
+ * @since 2.0.8 - experimental
147
+ */
148
+ @ CheckReturnValue
149
+ @ Experimental
150
+ public static <T > UnicastSubject <T > create (boolean delayError ) {
151
+ return new UnicastSubject <T >(bufferSize (), delayError );
152
+ }
153
+
154
+
155
+ /**
156
+ * Creates an UnicastSubject with the given capacity hint and delay error flag.
115
157
* @param capacityHint the capacity hint for the internal, unbounded queue
116
- * @since 2.0
158
+ * @param delayError deliver pending onNext events before onError
159
+ * @since 2.0.8 - experimental
117
160
*/
118
- UnicastSubject (int capacityHint ) {
161
+ UnicastSubject (int capacityHint , boolean delayError ) {
119
162
this .queue = new SpscLinkedArrayQueue <T >(ObjectHelper .verifyPositive (capacityHint , "capacityHint" ));
120
163
this .onTerminate = new AtomicReference <Runnable >();
164
+ this .delayError = delayError ;
121
165
this .actual = new AtomicReference <Observer <? super T >>();
122
166
this .once = new AtomicBoolean ();
123
167
this .wip = new UnicastQueueDisposable ();
124
168
}
125
169
126
170
/**
127
- * Creates an UnicastProcessor with the given capacity hint and callback
128
- * for when the Processor is terminated normally or its single Subscriber cancels.
171
+ * Creates an UnicastSubject with the given capacity hint and callback
172
+ * for when the Subject is terminated normally or its single Subscriber cancels.
129
173
* @param capacityHint the capacity hint for the internal, unbounded queue
130
- * @param onTerminate the callback to run when the Processor is terminated or cancelled, null not allowed
174
+ * @param onTerminate the callback to run when the Subject is terminated or cancelled, null not allowed
131
175
* @since 2.0
132
- */
176
+ *
177
+ * */
133
178
UnicastSubject (int capacityHint , Runnable onTerminate ) {
179
+ this (capacityHint , onTerminate , true );
180
+ }
181
+
182
+ /**
183
+ * Creates an UnicastSubject with the given capacity hint, delay error flag and callback
184
+ * for when the Subject is terminated normally or its single Subscriber cancels.
185
+ * @param capacityHint the capacity hint for the internal, unbounded queue
186
+ * @param onTerminate the callback to run when the Subject is terminated or cancelled, null not allowed
187
+ * @param delayError deliver pending onNext events before onError
188
+ * @since 2.0.8 - experimental
189
+ */
190
+ UnicastSubject (int capacityHint , Runnable onTerminate , boolean delayError ) {
134
191
this .queue = new SpscLinkedArrayQueue <T >(ObjectHelper .verifyPositive (capacityHint , "capacityHint" ));
135
192
this .onTerminate = new AtomicReference <Runnable >(ObjectHelper .requireNonNull (onTerminate , "onTerminate" ));
193
+ this .delayError = delayError ;
136
194
this .actual = new AtomicReference <Observer <? super T >>();
137
195
this .once = new AtomicBoolean ();
138
196
this .wip = new UnicastQueueDisposable ();
@@ -212,6 +270,8 @@ public void onComplete() {
212
270
void drainNormal (Observer <? super T > a ) {
213
271
int missed = 1 ;
214
272
SimpleQueue <T > q = queue ;
273
+ boolean failFast = !this .delayError ;
274
+ boolean canBeError = true ;
215
275
for (;;) {
216
276
for (;;) {
217
277
@@ -221,19 +281,23 @@ void drainNormal(Observer<? super T> a) {
221
281
return ;
222
282
}
223
283
224
- boolean d = done ;
284
+ boolean d = this . done ;
225
285
T v = queue .poll ();
226
286
boolean empty = v == null ;
227
287
228
- if (d && empty ) {
229
- actual .lazySet (null );
230
- Throwable ex = error ;
231
- if (ex != null ) {
232
- a .onError (ex );
233
- } else {
234
- a .onComplete ();
288
+ if (d ) {
289
+ if (failFast && canBeError ) {
290
+ if (failedFast (q , a )) {
291
+ return ;
292
+ } else {
293
+ canBeError = false ;
294
+ }
295
+ }
296
+
297
+ if (empty ) {
298
+ errorOrComplete (a );
299
+ return ;
235
300
}
236
- return ;
237
301
}
238
302
239
303
if (empty ) {
@@ -254,6 +318,7 @@ void drainFused(Observer<? super T> a) {
254
318
int missed = 1 ;
255
319
256
320
final SpscLinkedArrayQueue <T > q = queue ;
321
+ final boolean failFast = !delayError ;
257
322
258
323
for (;;) {
259
324
@@ -262,20 +327,18 @@ void drainFused(Observer<? super T> a) {
262
327
q .clear ();
263
328
return ;
264
329
}
265
-
266
330
boolean d = done ;
267
331
332
+ if (failFast && d ) {
333
+ if (failedFast (q , a )) {
334
+ return ;
335
+ }
336
+ }
337
+
268
338
a .onNext (null );
269
339
270
340
if (d ) {
271
- actual .lazySet (null );
272
-
273
- Throwable ex = error ;
274
- if (ex != null ) {
275
- a .onError (ex );
276
- } else {
277
- a .onComplete ();
278
- }
341
+ errorOrComplete (a );
279
342
return ;
280
343
}
281
344
@@ -286,6 +349,28 @@ void drainFused(Observer<? super T> a) {
286
349
}
287
350
}
288
351
352
+ void errorOrComplete (Observer <? super T > a ) {
353
+ actual .lazySet (null );
354
+ Throwable ex = error ;
355
+ if (ex != null ) {
356
+ a .onError (ex );
357
+ } else {
358
+ a .onComplete ();
359
+ }
360
+ }
361
+
362
+ boolean failedFast (final SimpleQueue <T > q , Observer <? super T > a ) {
363
+ Throwable ex = error ;
364
+ if (ex != null ) {
365
+ actual .lazySet (null );
366
+ q .clear ();
367
+ a .onError (ex );
368
+ return true ;
369
+ } else {
370
+ return false ;
371
+ }
372
+ }
373
+
289
374
void drain () {
290
375
if (wip .getAndIncrement () != 0 ) {
291
376
return ;
0 commit comments