15
15
*/
16
16
package rx .schedulers ;
17
17
18
- import java .util .concurrent .ExecutorService ;
19
18
import java .util .concurrent .Executors ;
20
- import java .util .concurrent .ScheduledFuture ;
19
+ import java .util .concurrent .Future ;
20
+ import java .util .concurrent .ScheduledExecutorService ;
21
21
import java .util .concurrent .ThreadFactory ;
22
22
import java .util .concurrent .TimeUnit ;
23
+ import java .util .concurrent .atomic .AtomicBoolean ;
23
24
import java .util .concurrent .atomic .AtomicLong ;
24
- import java .util .concurrent .atomic .AtomicReference ;
25
25
26
26
import rx .Scheduler ;
27
27
import rx .Subscription ;
@@ -56,15 +56,15 @@ private NewThreadScheduler() {
56
56
57
57
@ Override
58
58
public Worker createWorker () {
59
- return new EventLoopScheduler (THREAD_FACTORY );
59
+ return new NewThreadWorker (THREAD_FACTORY );
60
60
}
61
61
62
- /* package */ static class EventLoopScheduler extends Scheduler .Worker implements Subscription {
62
+ /* package */ static class NewThreadWorker extends Scheduler .Worker implements Subscription {
63
63
private final CompositeSubscription innerSubscription = new CompositeSubscription ();
64
- private final ExecutorService executor ;
64
+ private final ScheduledExecutorService executor ;
65
65
66
- /* package */ EventLoopScheduler (ThreadFactory threadFactory ) {
67
- executor = Executors .newSingleThreadExecutor ( threadFactory );
66
+ /* package */ NewThreadWorker (ThreadFactory threadFactory ) {
67
+ executor = Executors .newScheduledThreadPool ( 1 , threadFactory );
68
68
}
69
69
70
70
@ Override
@@ -73,80 +73,109 @@ public Subscription schedule(final Action0 action) {
73
73
}
74
74
75
75
/* package */ Subscription schedule (final Action0 action , final OnActionComplete onComplete ) {
76
+ return scheduleActual (action , 0 , null );
77
+ }
78
+
79
+ @ Override
80
+ public Subscription schedule (final Action0 action , long delayTime , TimeUnit unit ) {
76
81
if (innerSubscription .isUnsubscribed ()) {
77
- // don't schedule, we are unsubscribed
78
82
return Subscriptions .empty ();
79
83
}
80
-
81
- final AtomicReference <Subscription > sf = new AtomicReference <Subscription >();
82
- Subscription s = Subscriptions .from (executor .submit (new Runnable () {
83
-
84
- @ Override
85
- public void run () {
86
- try {
87
- if (innerSubscription .isUnsubscribed ()) {
88
- return ;
89
- }
90
- action .call ();
91
- } finally {
92
- // remove the subscription now that we're completed
93
- Subscription s = sf .get ();
94
- if (s != null ) {
95
- innerSubscription .remove (s );
96
- }
97
- if (onComplete != null ) {
98
- onComplete .complete (s );
99
- }
100
- }
101
- }
102
- }));
103
-
104
- sf .set (s );
105
- innerSubscription .add (s );
106
- return s ;
84
+ return scheduleActual (action , delayTime , unit );
107
85
}
108
86
109
- @ Override
110
- public Subscription schedule (final Action0 action , long delayTime , TimeUnit unit ) {
111
- return schedule (action , delayTime , unit , null );
87
+ /* package */ ScheduledAction scheduleActual (final Action0 action , long delayTime , TimeUnit unit ) {
88
+ ScheduledAction run = new ScheduledAction (action , innerSubscription );
89
+ Future <?> f ;
90
+ if (delayTime <= 0 ) {
91
+ f = executor .submit (run );
92
+ } else {
93
+ f = executor .schedule (run , delayTime , unit );
94
+ }
95
+ run .add (Subscriptions .from (f ));
96
+
97
+ return run ;
112
98
}
99
+
100
+ /** Remove a child subscription from a composite when unsubscribing. */
101
+ public static final class Remover implements Subscription {
102
+ final Subscription s ;
103
+ final CompositeSubscription parent ;
104
+ final AtomicBoolean once ;
105
+
106
+ public Remover (Subscription s , CompositeSubscription parent ) {
107
+ this .s = s ;
108
+ this .parent = parent ;
109
+ this .once = new AtomicBoolean ();
110
+ }
111
+
112
+ @ Override
113
+ public boolean isUnsubscribed () {
114
+ return s .isUnsubscribed ();
115
+ }
116
+
117
+ @ Override
118
+ public void unsubscribe () {
119
+ if (once .compareAndSet (false , true )) {
120
+ parent .remove (s );
121
+ }
122
+ }
123
+
124
+ }
125
+ /**
126
+ * A runnable that executes an Action0 and can be cancelled
127
+ * The analogue is the Subscriber in respect of an Observer.
128
+ */
129
+ public static final class ScheduledAction implements Runnable , Subscription {
130
+ final CompositeSubscription cancel ;
131
+ final Action0 action ;
132
+ final CompositeSubscription parent ;
133
+ final AtomicBoolean once ;
134
+
135
+ public ScheduledAction (Action0 action , CompositeSubscription parent ) {
136
+ this .action = action ;
137
+ this .parent = parent ;
138
+ this .cancel = new CompositeSubscription ();
139
+ this .once = new AtomicBoolean ();
140
+ }
113
141
114
- /* package */ Subscription schedule (final Action0 action , long delayTime , TimeUnit unit , final OnActionComplete onComplete ) {
115
- final AtomicReference <Subscription > sf = new AtomicReference <Subscription >();
116
- // we will use the system scheduler since it doesn't make sense to launch a new Thread and then sleep
117
- // we will instead schedule the event then launch the thread after the delay has passed
118
- ScheduledFuture <?> f = GenericScheduledExecutorService .getInstance ().schedule (new Runnable () {
119
-
120
- @ Override
121
- public void run () {
122
- try {
123
- if (innerSubscription .isUnsubscribed ()) {
124
- return ;
125
- }
126
- // now that the delay is past schedule the work to be done for real on the UI thread
127
- schedule (action );
128
- } finally {
129
- // remove the subscription now that we're completed
130
- Subscription s = sf .get ();
131
- if (s != null ) {
132
- innerSubscription .remove (s );
133
- }
134
- if (onComplete != null ) {
135
- onComplete .complete (s );
136
- }
137
- }
142
+ @ Override
143
+ public void run () {
144
+ try {
145
+ action .call ();
146
+ } finally {
147
+ unsubscribe ();
138
148
}
139
- }, delayTime , unit );
149
+ }
140
150
141
- // add the ScheduledFuture as a subscription so we can cancel the scheduled action if an unsubscribe happens
142
- Subscription s = Subscriptions .from (f );
143
- sf .set (s );
144
- innerSubscription .add (s );
145
- return s ;
151
+ @ Override
152
+ public boolean isUnsubscribed () {
153
+ return cancel .isUnsubscribed ();
154
+ }
155
+
156
+ @ Override
157
+ public void unsubscribe () {
158
+ if (once .compareAndSet (false , true )) {
159
+ cancel .unsubscribe ();
160
+ parent .remove (this );
161
+ }
162
+ }
163
+ public void add (Subscription s ) {
164
+ cancel .add (s );
165
+ }
166
+ /**
167
+ * Adds a parent to this ScheduledAction so when it is
168
+ * cancelled or terminates, it can remove itself from this parent.
169
+ * @param parent
170
+ */
171
+ public void addParent (CompositeSubscription parent ) {
172
+ cancel .add (new Remover (this , parent ));
173
+ }
146
174
}
147
175
148
176
@ Override
149
177
public void unsubscribe () {
178
+ executor .shutdown ();
150
179
innerSubscription .unsubscribe ();
151
180
}
152
181
@@ -163,4 +192,4 @@ public boolean isUnsubscribed() {
163
192
164
193
}
165
194
166
- }
195
+ }
0 commit comments