17
17
18
18
import java .util .Queue ;
19
19
import java .util .concurrent .ConcurrentLinkedQueue ;
20
- import java .util .concurrent .atomic .AtomicBoolean ;
21
20
import java .util .concurrent .atomic .AtomicLong ;
22
21
23
22
import rx .Observable .Operator ;
24
23
import rx .Producer ;
25
24
import rx .Subscriber ;
26
- import rx .exceptions .MissingBackpressureException ;
27
- import rx .functions .Action0 ;
28
25
29
26
public class OperatorOnBackpressureBuffer <T > implements Operator <T , T > {
30
27
31
28
private final NotificationLite <T > on = NotificationLite .instance ();
32
29
33
- private final Long capacity ;
34
- private final Action0 onOverflow ;
35
-
36
- public OperatorOnBackpressureBuffer () {
37
- this .capacity = null ;
38
- this .onOverflow = null ;
39
- }
40
-
41
- public OperatorOnBackpressureBuffer (long capacity ) {
42
- this (capacity , null );
43
- }
44
-
45
- public OperatorOnBackpressureBuffer (long capacity , Action0 onOverflow ) {
46
- if (capacity <= 0 ) {
47
- throw new IllegalArgumentException ("Buffer capacity must be > 0" );
48
- }
49
- this .capacity = capacity ;
50
- this .onOverflow = onOverflow ;
51
- }
52
-
53
30
@ Override
54
31
public Subscriber <? super T > call (final Subscriber <? super T > child ) {
55
32
// TODO get a different queue implementation
33
+ // TODO start with size hint
56
34
final ConcurrentLinkedQueue <Object > queue = new ConcurrentLinkedQueue <Object >();
57
- final AtomicLong capacity = (this .capacity == null ) ? null : new AtomicLong (this .capacity );
58
35
final AtomicLong wip = new AtomicLong ();
59
36
final AtomicLong requested = new AtomicLong ();
60
37
@@ -63,17 +40,14 @@ public Subscriber<? super T> call(final Subscriber<? super T> child) {
63
40
@ Override
64
41
public void request (long n ) {
65
42
if (requested .getAndAdd (n ) == 0 ) {
66
- pollQueue (wip , requested , capacity , queue , child );
43
+ pollQueue (wip , requested , queue , child );
67
44
}
68
45
}
69
46
70
47
});
71
48
// don't pass through subscriber as we are async and doing queue draining
72
49
// a parent being unsubscribed should not affect the children
73
50
Subscriber <T > parent = new Subscriber <T >() {
74
-
75
- private AtomicBoolean saturated = new AtomicBoolean (false );
76
-
77
51
@ Override
78
52
public void onStart () {
79
53
request (Long .MAX_VALUE );
@@ -82,47 +56,21 @@ public void onStart() {
82
56
@ Override
83
57
public void onCompleted () {
84
58
queue .offer (on .completed ());
85
- pollQueue (wip , requested , capacity , queue , child );
59
+ pollQueue (wip , requested , queue , child );
86
60
}
87
61
88
62
@ Override
89
63
public void onError (Throwable e ) {
90
64
queue .offer (on .error (e ));
91
- pollQueue (wip , requested , capacity , queue , child );
65
+ pollQueue (wip , requested , queue , child );
92
66
}
93
67
94
68
@ Override
95
69
public void onNext (T t ) {
96
- if (!ensureCapacity ()) {
97
- return ;
98
- }
99
70
queue .offer (on .next (t ));
100
- pollQueue (wip , requested , capacity , queue , child );
71
+ pollQueue (wip , requested , queue , child );
101
72
}
102
73
103
- private boolean ensureCapacity () {
104
- if (capacity == null ) {
105
- return true ;
106
- }
107
-
108
- long currCapacity ;
109
- do {
110
- currCapacity = capacity .get ();
111
- if (currCapacity <= 0 ) {
112
- if (saturated .compareAndSet (false , true )) {
113
- // ensure single completion contract
114
- child .onError (new MissingBackpressureException ("Overflowed buffer of " + OperatorOnBackpressureBuffer .this .capacity ));
115
- unsubscribe ();
116
- if (onOverflow != null ) {
117
- onOverflow .call ();
118
- }
119
- }
120
- return false ;
121
- }
122
- // ensure no other thread stole our slot, or retry
123
- } while (!capacity .compareAndSet (currCapacity , currCapacity - 1 ));
124
- return true ;
125
- }
126
74
};
127
75
128
76
// if child unsubscribes it should unsubscribe the parent, but not the other way around
@@ -131,7 +79,7 @@ private boolean ensureCapacity() {
131
79
return parent ;
132
80
}
133
81
134
- private void pollQueue (AtomicLong wip , AtomicLong requested , AtomicLong capacity , Queue <Object > queue , Subscriber <? super T > child ) {
82
+ private void pollQueue (AtomicLong wip , AtomicLong requested , Queue <Object > queue , Subscriber <? super T > child ) {
135
83
// TODO can we do this without putting everything in the queue first so we can fast-path the case when we don't need to queue?
136
84
if (requested .get () > 0 ) {
137
85
// only one draining at a time
@@ -148,9 +96,6 @@ private void pollQueue(AtomicLong wip, AtomicLong requested, AtomicLong capacity
148
96
requested .incrementAndGet ();
149
97
return ;
150
98
}
151
- if (capacity != null ) { // it's bounded
152
- capacity .incrementAndGet ();
153
- }
154
99
on .accept (child , o );
155
100
} else {
156
101
// we hit the end ... so increment back to 0 again
0 commit comments