16
16
package rx .internal .operators ;
17
17
18
18
import java .util .concurrent .ConcurrentLinkedQueue ;
19
- import java .util .concurrent .atomic .AtomicIntegerFieldUpdater ;
20
19
import java .util .concurrent .atomic .AtomicReferenceFieldUpdater ;
21
20
22
21
import rx .Observer ;
23
22
import rx .Subscriber ;
24
23
import rx .functions .Action0 ;
24
+ import rx .observers .EmptyObserver ;
25
25
import rx .observers .Subscribers ;
26
26
import rx .subjects .Subject ;
27
27
import rx .subscriptions .Subscriptions ;
51
51
*/
52
52
public class BufferUntilSubscriber <T > extends Subject <T , T > {
53
53
54
+ @ SuppressWarnings ("rawtypes" )
55
+ private final static Observer EMPTY_OBSERVER = new EmptyObserver ();
56
+
54
57
/**
55
58
* @warn create() undescribed
56
59
* @return
@@ -62,25 +65,22 @@ public static <T> BufferUntilSubscriber<T> create() {
62
65
63
66
/** The common state. */
64
67
static final class State <T > {
65
- /** The first observer or the one which buffers until the first arrives. */
66
- volatile Observer <? super T > observerRef = new BufferedObserver <T >();
67
- /** Allow a single subscriber only. */
68
- volatile int first ;
68
+ volatile Observer <? super T > observerRef = null ;
69
69
/** Field updater for observerRef. */
70
70
@ SuppressWarnings ("rawtypes" )
71
71
static final AtomicReferenceFieldUpdater <State , Observer > OBSERVER_UPDATER
72
72
= AtomicReferenceFieldUpdater .newUpdater (State .class , Observer .class , "observerRef" );
73
- /** Field updater for first. */
74
- @ SuppressWarnings ("rawtypes" )
75
- static final AtomicIntegerFieldUpdater <State > FIRST_UPDATER
76
- = AtomicIntegerFieldUpdater .newUpdater (State .class , "first" );
77
-
78
- boolean casFirst (int expected , int next ) {
79
- return FIRST_UPDATER .compareAndSet (this , expected , next );
80
- }
81
- void setObserverRef (Observer <? super T > o ) {
82
- observerRef = o ;
73
+
74
+ boolean casObserverRef (Observer <? super T > expected , Observer <? super T > next ) {
75
+ return OBSERVER_UPDATER .compareAndSet (this , expected , next );
83
76
}
77
+
78
+ Object guard = new Object ();
79
+ /* protected by guard */
80
+ boolean emitting = false ;
81
+
82
+ final ConcurrentLinkedQueue <Object > buffer = new ConcurrentLinkedQueue <Object >();
83
+ final NotificationLite <T > nl = NotificationLite .instance ();
84
84
}
85
85
86
86
static final class OnSubscribeAction <T > implements OnSubscribe <T > {
@@ -92,122 +92,100 @@ public OnSubscribeAction(State<T> state) {
92
92
93
93
@ Override
94
94
public void call (final Subscriber <? super T > s ) {
95
- if (state .casFirst (0 , 1 )) {
96
- final NotificationLite <T > nl = NotificationLite .instance ();
97
- // drain queued notifications before subscription
98
- // we do this here before PassThruObserver so the consuming thread can do this before putting itself in the line of the producer
99
- BufferedObserver <? super T > buffered = (BufferedObserver <? super T >)state .observerRef ;
100
- Object o ;
101
- while ((o = buffered .buffer .poll ()) != null ) {
102
- nl .accept (s , o );
103
- }
104
- // register real observer for pass-thru ... and drain any further events received on first notification
105
- state .setObserverRef (new PassThruObserver <T >(s , buffered .buffer , state ));
95
+ if (state .casObserverRef (null , s )) {
106
96
s .add (Subscriptions .create (new Action0 () {
107
97
@ Override
108
98
public void call () {
109
- state .setObserverRef ( Subscribers . empty ()) ;
99
+ state .observerRef = EMPTY_OBSERVER ;
110
100
}
111
101
}));
102
+ boolean win = false ;
103
+ synchronized (state .guard ) {
104
+ if (!state .emitting ) {
105
+ state .emitting = true ;
106
+ win = true ;
107
+ }
108
+ }
109
+ if (win ) {
110
+ final NotificationLite <T > nl = NotificationLite .instance ();
111
+ while (true ) {
112
+ Object o ;
113
+ while ((o = state .buffer .poll ()) != null ) {
114
+ nl .accept (state .observerRef , o );
115
+ }
116
+ synchronized (state .guard ) {
117
+ if (state .buffer .isEmpty ()) {
118
+ // Although the buffer is empty, there is still a chance
119
+ // that further events may be put into the `buffer`.
120
+ // `emit(Object v)` should handle it.
121
+ state .emitting = false ;
122
+ break ;
123
+ }
124
+ }
125
+ }
126
+ }
112
127
} else {
113
128
s .onError (new IllegalStateException ("Only one subscriber allowed!" ));
114
129
}
115
130
}
116
131
117
132
}
118
133
final State <T > state ;
119
-
134
+
135
+ private boolean forward = false ;
136
+
120
137
private BufferUntilSubscriber (State <T > state ) {
121
138
super (new OnSubscribeAction <T >(state ));
122
139
this .state = state ;
123
140
}
124
141
125
- @ Override
126
- public void onCompleted () {
127
- state .observerRef .onCompleted ();
128
- }
129
-
130
- @ Override
131
- public void onError (Throwable e ) {
132
- state .observerRef .onError (e );
142
+ private void emit (Object v ) {
143
+ synchronized (state .guard ) {
144
+ state .buffer .add (v );
145
+ if (state .observerRef != null && !state .emitting ) {
146
+ // Have an observer and nobody is emitting,
147
+ // should drain the `buffer`
148
+ forward = true ;
149
+ state .emitting = true ;
150
+ }
151
+ }
152
+ if (forward ) {
153
+ Object o ;
154
+ while ((o = state .buffer .poll ()) != null ) {
155
+ state .nl .accept (state .observerRef , o );
156
+ }
157
+ // Because `emit(Object v)` will be called in sequence,
158
+ // no event will be put into `buffer` after we drain it.
159
+ }
133
160
}
134
161
135
162
@ Override
136
- public void onNext (T t ) {
137
- state .observerRef .onNext (t );
138
- }
139
-
140
- /**
141
- * This is a temporary observer between buffering and the actual that gets into the line of notifications
142
- * from the producer and will drain the queue of any items received during the race of the initial drain and
143
- * switching this.
144
- *
145
- * It will then immediately swap itself out for the actual (after a single notification), but since this is
146
- * now being done on the same producer thread no further buffering will occur.
147
- */
148
- private static final class PassThruObserver <T > extends Subscriber <T > {
149
-
150
- private final Observer <? super T > actual ;
151
- // this assumes single threaded synchronous notifications (the Rx contract for a single Observer)
152
- private final ConcurrentLinkedQueue <Object > buffer ;
153
- private final State <T > state ;
154
-
155
- PassThruObserver (Observer <? super T > actual , ConcurrentLinkedQueue <Object > buffer ,
156
- State <T > state ) {
157
- this .actual = actual ;
158
- this .buffer = buffer ;
159
- this .state = state ;
163
+ public void onCompleted () {
164
+ if (forward ) {
165
+ state .observerRef .onCompleted ();
160
166
}
161
-
162
- @ Override
163
- public void onCompleted () {
164
- drainIfNeededAndSwitchToActual ();
165
- actual .onCompleted ();
167
+ else {
168
+ emit (state .nl .completed ());
166
169
}
170
+ }
167
171
168
- @ Override
169
- public void onError (Throwable e ) {
170
- drainIfNeededAndSwitchToActual ();
171
- actual .onError (e );
172
+ @ Override
173
+ public void onError (Throwable e ) {
174
+ if ( forward ) {
175
+ state . observerRef .onError (e );
172
176
}
173
-
174
- @ Override
175
- public void onNext (T t ) {
176
- drainIfNeededAndSwitchToActual ();
177
- actual .onNext (t );
177
+ else {
178
+ emit (state .nl .error (e ));
178
179
}
179
-
180
- private void drainIfNeededAndSwitchToActual () {
181
- final NotificationLite <T > nl = NotificationLite .instance ();
182
- Object o ;
183
- while ((o = buffer .poll ()) != null ) {
184
- nl .accept (this , o );
185
- }
186
- // now we can safely change over to the actual and get rid of the pass-thru
187
- // but only if not unsubscribed
188
- state .setObserverRef (actual );
189
- }
190
-
191
180
}
192
181
193
- private static final class BufferedObserver <T > extends Subscriber <T > {
194
- private final ConcurrentLinkedQueue <Object > buffer = new ConcurrentLinkedQueue <Object >();
195
- private static final NotificationLite <Object > nl = NotificationLite .instance ();
196
-
197
- @ Override
198
- public void onCompleted () {
199
- buffer .add (nl .completed ());
200
- }
201
-
202
- @ Override
203
- public void onError (Throwable e ) {
204
- buffer .add (nl .error (e ));
182
+ @ Override
183
+ public void onNext (T t ) {
184
+ if (forward ) {
185
+ state .observerRef .onNext (t );
205
186
}
206
-
207
- @ Override
208
- public void onNext (T t ) {
209
- buffer .add (nl .next (t ));
187
+ else {
188
+ emit (state .nl .next (t ));
210
189
}
211
-
212
190
}
213
191
}
0 commit comments