17
17
18
18
import java .util .concurrent .ConcurrentLinkedQueue ;
19
19
import java .util .concurrent .atomic .AtomicIntegerFieldUpdater ;
20
+ import java .util .concurrent .atomic .AtomicLongFieldUpdater ;
21
+
20
22
import rx .Observable ;
21
23
import rx .Observable .Operator ;
24
+ import rx .Producer ;
22
25
import rx .Subscriber ;
23
26
import rx .functions .Action0 ;
24
27
import rx .observers .SerializedSubscriber ;
30
33
* <p>
31
34
* <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/concat.png" alt="">
32
35
*
33
- * @param <T> the source and result value type
36
+ * @param <T>
37
+ * the source and result value type
34
38
*/
35
39
public final class OperatorConcat <T > implements Operator <T , Observable <? extends T >> {
36
40
@ Override
37
41
public Subscriber <? super Observable <? extends T >> call (final Subscriber <? super T > child ) {
38
42
final SerializedSubscriber <T > s = new SerializedSubscriber <T >(child );
39
43
final SerialSubscription current = new SerialSubscription ();
40
44
child .add (current );
41
- return new ConcatSubscriber <T >(s , current );
45
+ ConcatSubscriber <T > cs = new ConcatSubscriber <T >(s , current );
46
+ ConcatProducer <T > cp = new ConcatProducer <T >(cs );
47
+ child .setProducer (cp );
48
+ return cs ;
42
49
}
43
-
50
+
51
+ static final class ConcatProducer <T > implements Producer {
52
+ final ConcatSubscriber <T > cs ;
53
+
54
+ ConcatProducer (ConcatSubscriber <T > cs ) {
55
+ this .cs = cs ;
56
+ }
57
+
58
+ @ Override
59
+ public void request (long n ) {
60
+ cs .requestFromChild (n );
61
+ }
62
+
63
+ }
64
+
44
65
static final class ConcatSubscriber <T > extends Subscriber <Observable <? extends T >> {
45
66
final NotificationLite <Observable <? extends T >> nl = NotificationLite .instance ();
46
- private final Subscriber <T > s ;
67
+ private final Subscriber <T > child ;
47
68
private final SerialSubscription current ;
48
69
final ConcurrentLinkedQueue <Object > queue ;
70
+
71
+ volatile ConcatInnerSubscriber <T > currentSubscriber ;
72
+
49
73
volatile int wip ;
50
74
@ SuppressWarnings ("rawtypes" )
51
- static final AtomicIntegerFieldUpdater <ConcatSubscriber > WIP_UPDATER
52
- = AtomicIntegerFieldUpdater .newUpdater (ConcatSubscriber .class , "wip" );
53
-
75
+ static final AtomicIntegerFieldUpdater <ConcatSubscriber > WIP_UPDATER = AtomicIntegerFieldUpdater .newUpdater (ConcatSubscriber .class , "wip" );
76
+
77
+ // accessed by REQUESTED_UPDATER
78
+ private volatile long requested ;
79
+ @ SuppressWarnings ("rawtypes" )
80
+ private static final AtomicLongFieldUpdater <ConcatSubscriber > REQUESTED_UPDATER = AtomicLongFieldUpdater .newUpdater (ConcatSubscriber .class , "requested" );
81
+
54
82
public ConcatSubscriber (Subscriber <T > s , SerialSubscription current ) {
55
83
super (s );
56
- this .s = s ;
84
+ this .child = s ;
57
85
this .current = current ;
58
86
this .queue = new ConcurrentLinkedQueue <Object >();
59
87
add (Subscriptions .create (new Action0 () {
@@ -71,20 +99,42 @@ public void onStart() {
71
99
request (2 );
72
100
}
73
101
102
+ private void requestFromChild (long n ) {
103
+ // we track 'requested' so we know whether we should subscribe the next or not
104
+ if (REQUESTED_UPDATER .getAndAdd (this , n ) == 0 ) {
105
+ if (currentSubscriber == null && wip > 0 ) {
106
+ // this means we may be moving from one subscriber to another after having stopped processing
107
+ // so need to kick off the subscribe via this request notification
108
+ subscribeNext ();
109
+ // return here as we don't want to do the requestMore logic below (which would double request)
110
+ return ;
111
+ }
112
+ }
113
+
114
+ if (currentSubscriber != null ) {
115
+ // otherwise we are just passing it through to the currentSubscriber
116
+ currentSubscriber .requestMore (n );
117
+ }
118
+ }
119
+
120
+ private void decrementRequested () {
121
+ REQUESTED_UPDATER .decrementAndGet (this );
122
+ }
123
+
74
124
@ Override
75
125
public void onNext (Observable <? extends T > t ) {
76
126
queue .add (nl .next (t ));
77
127
if (WIP_UPDATER .getAndIncrement (this ) == 0 ) {
78
128
subscribeNext ();
79
129
}
80
130
}
81
-
131
+
82
132
@ Override
83
133
public void onError (Throwable e ) {
84
- s .onError (e );
134
+ child .onError (e );
85
135
unsubscribe ();
86
136
}
87
-
137
+
88
138
@ Override
89
139
public void onCompleted () {
90
140
queue .add (nl .completed ());
@@ -95,39 +145,65 @@ public void onCompleted() {
95
145
96
146
void completeInner () {
97
147
request (1 );
148
+ currentSubscriber = null ;
98
149
if (WIP_UPDATER .decrementAndGet (this ) > 0 ) {
99
150
subscribeNext ();
100
151
}
101
152
}
102
153
103
154
void subscribeNext () {
104
- Object o = queue .poll ();
105
- if (nl .isCompleted (o )) {
106
- s .onCompleted ();
107
- } else if (o != null ) {
108
- Observable <? extends T > obs = nl .getValue (o );
109
- Subscriber <T > sourceSub = new Subscriber <T >() {
110
-
111
- @ Override
112
- public void onNext (T t ) {
113
- // TODO need to support backpressure here https://github.com/Netflix/RxJava/issues/1480
114
- s .onNext (t );
115
- }
116
-
117
- @ Override
118
- public void onError (Throwable e ) {
119
- ConcatSubscriber .this .onError (e );
120
- }
121
-
122
- @ Override
123
- public void onCompleted () {
124
- completeInner ();
125
- }
126
-
127
- };
128
- current .set (sourceSub );
129
- obs .unsafeSubscribe (sourceSub );
155
+ if (requested > 0 ) {
156
+ Object o = queue .poll ();
157
+ if (nl .isCompleted (o )) {
158
+ child .onCompleted ();
159
+ } else if (o != null ) {
160
+ Observable <? extends T > obs = nl .getValue (o );
161
+ currentSubscriber = new ConcatInnerSubscriber <T >(this , child , requested );
162
+ current .set (currentSubscriber );
163
+ obs .unsafeSubscribe (currentSubscriber );
164
+ }
165
+ } else {
166
+ // requested == 0, so we'll peek to see if we are completed, otherwise wait until another request
167
+ Object o = queue .peek ();
168
+ if (nl .isCompleted (o )) {
169
+ child .onCompleted ();
170
+ }
130
171
}
131
172
}
132
173
}
174
+
175
+ static class ConcatInnerSubscriber <T > extends Subscriber <T > {
176
+
177
+ private final Subscriber <T > child ;
178
+ private final ConcatSubscriber <T > parent ;
179
+
180
+ public ConcatInnerSubscriber (ConcatSubscriber <T > parent , Subscriber <T > child , long initialRequest ) {
181
+ this .parent = parent ;
182
+ this .child = child ;
183
+ request (initialRequest );
184
+ }
185
+
186
+ void requestMore (long n ) {
187
+ request (n );
188
+ }
189
+
190
+ @ Override
191
+ public void onNext (T t ) {
192
+ parent .decrementRequested ();
193
+ child .onNext (t );
194
+ }
195
+
196
+ @ Override
197
+ public void onError (Throwable e ) {
198
+ // terminal error through parent so everything gets cleaned up, including this inner
199
+ parent .onError (e );
200
+ }
201
+
202
+ @ Override
203
+ public void onCompleted () {
204
+ // terminal completion to parent so it continues to the next
205
+ parent .completeInner ();
206
+ }
207
+
208
+ };
133
209
}
0 commit comments