@@ -112,12 +112,25 @@ public void subscribe(CoreSubscriber<? super T> actual) {
112
112
final ReconnectInner <T > inner = new ReconnectInner <>(actual , this );
113
113
actual .onSubscribe (inner );
114
114
115
- final int state = this .add (inner );
115
+ for (; ; ) {
116
+ final int state = this .add (inner );
117
+
118
+ T value = this .value ;
119
+
120
+ if (state == READY_STATE ) {
121
+ if (value != null ) {
122
+ inner .complete (value );
123
+ return ;
124
+ }
125
+ // value == null means racing between invalidate and this subscriber
126
+ // thus, we have to loop again
127
+ continue ;
128
+ } else if (state == TERMINATED_STATE ) {
129
+ inner .onError (this .t );
130
+ return ;
131
+ }
116
132
117
- if (state == READY_STATE ) {
118
- inner .complete (this .value );
119
- } else if (state == TERMINATED_STATE ) {
120
- inner .onError (this .t );
133
+ return ;
121
134
}
122
135
}
123
136
@@ -150,7 +163,14 @@ public T block(@Nullable Duration timeout) {
150
163
try {
151
164
ReconnectInner <T >[] subscribers = this .subscribers ;
152
165
if (subscribers == READY ) {
153
- return this .value ;
166
+ final T value = this .value ;
167
+ if (value != null ) {
168
+ return value ;
169
+ } else {
170
+ // value == null means racing between invalidate and this block
171
+ // thus, we have to update the state again and see what happened
172
+ subscribers = this .subscribers ;
173
+ }
154
174
}
155
175
156
176
if (subscribers == TERMINATED ) {
@@ -175,7 +195,14 @@ public T block(@Nullable Duration timeout) {
175
195
ReconnectInner <T >[] inners = this .subscribers ;
176
196
177
197
if (inners == READY ) {
178
- return this .value ;
198
+ final T value = this .value ;
199
+ if (value != null ) {
200
+ return value ;
201
+ } else {
202
+ // value == null means racing between invalidate and this block
203
+ // thus, we have to update the state again and see what happened
204
+ inners = this .subscribers ;
205
+ }
179
206
}
180
207
if (inners == TERMINATED ) {
181
208
RuntimeException re = Exceptions .propagate (this .t );
@@ -282,13 +309,31 @@ public void invalidate() {
282
309
283
310
final ReconnectInner <T >[] subscribers = this .subscribers ;
284
311
285
- if (subscribers == READY && SUBSCRIBERS .compareAndSet (this , READY , EMPTY_UNSUBSCRIBED )) {
312
+ if (subscribers == READY ) {
313
+ // guarded section to ensure we expire value exactly once if there is racing
314
+ if (WIP .getAndIncrement (this ) != 0 ) {
315
+ return ;
316
+ }
317
+
286
318
final T value = this .value ;
287
319
this .value = null ;
288
-
289
320
if (value != null ) {
290
321
this .onValueExpired .accept (value );
291
322
}
323
+
324
+ int m = 1 ;
325
+ for (; ; ) {
326
+ if (isDisposed ()) {
327
+ return ;
328
+ }
329
+
330
+ m = WIP .addAndGet (this , -m );
331
+ if (m == 0 ) {
332
+ break ;
333
+ }
334
+ }
335
+
336
+ SUBSCRIBERS .compareAndSet (this , READY , EMPTY_UNSUBSCRIBED );
292
337
}
293
338
}
294
339
@@ -355,6 +400,11 @@ void remove(ReconnectInner<T> ps) {
355
400
}
356
401
}
357
402
403
+ /**
404
+ * Subscriber that subscribes to the source {@link Mono} to receive its value. <br>
405
+ * Note that the source is not expected to complete empty, and if this happens, execution will
406
+ * terminate with an {@code IllegalStateException}.
407
+ */
358
408
static final class ReconnectMainSubscriber <T > implements CoreSubscriber <T > {
359
409
360
410
final ReconnectMono <T > parent ;
@@ -389,7 +439,7 @@ public void onComplete() {
389
439
}
390
440
391
441
if (value == null ) {
392
- p .terminate (new IllegalStateException ("Unexpected Completion of the Upstream " ));
442
+ p .terminate (new IllegalStateException ("Source completed empty " ));
393
443
} else {
394
444
p .complete ();
395
445
}
0 commit comments