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,97 +56,122 @@ 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
71
71
public Subscription schedule (final Action0 action ) {
72
- return schedule (action , null );
72
+ return schedule (action , 0 , null );
73
73
}
74
74
75
- /* package */ Subscription schedule (final Action0 action , final OnActionComplete onComplete ) {
75
+ @ Override
76
+ public Subscription schedule (final Action0 action , long delayTime , TimeUnit unit ) {
76
77
if (innerSubscription .isUnsubscribed ()) {
77
- // don't schedule, we are unsubscribed
78
78
return Subscriptions .empty ();
79
79
}
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 ;
80
+ return scheduleActual (action , delayTime , unit );
107
81
}
108
82
109
- @ Override
110
- public Subscription schedule (final Action0 action , long delayTime , TimeUnit unit ) {
111
- return schedule (action , delayTime , unit , null );
83
+ /* package */ ScheduledAction scheduleActual (final Action0 action , long delayTime , TimeUnit unit ) {
84
+ ScheduledAction run = new ScheduledAction (action , innerSubscription );
85
+ Future <?> f ;
86
+ if (delayTime <= 0 ) {
87
+ f = executor .submit (run );
88
+ } else {
89
+ f = executor .schedule (run , delayTime , unit );
90
+ }
91
+ run .add (Subscriptions .from (f ));
92
+
93
+ return run ;
94
+ }
95
+
96
+ /** Remove a child subscription from a composite when unsubscribing. */
97
+ private static final class Remover implements Subscription {
98
+ final Subscription s ;
99
+ final CompositeSubscription parent ;
100
+ final AtomicBoolean once ;
101
+
102
+ public Remover (Subscription s , CompositeSubscription parent ) {
103
+ this .s = s ;
104
+ this .parent = parent ;
105
+ this .once = new AtomicBoolean ();
106
+ }
107
+
108
+ @ Override
109
+ public boolean isUnsubscribed () {
110
+ return s .isUnsubscribed ();
111
+ }
112
+
113
+ @ Override
114
+ public void unsubscribe () {
115
+ if (once .compareAndSet (false , true )) {
116
+ parent .remove (s );
117
+ }
118
+ }
119
+
112
120
}
121
+ /**
122
+ * A runnable that executes an Action0 and can be cancelled
123
+ * The analogue is the Subscriber in respect of an Observer.
124
+ */
125
+ public static final class ScheduledAction implements Runnable , Subscription {
126
+ final CompositeSubscription cancel ;
127
+ final Action0 action ;
128
+ final CompositeSubscription parent ;
129
+ final AtomicBoolean once ;
130
+
131
+ public ScheduledAction (Action0 action , CompositeSubscription parent ) {
132
+ this .action = action ;
133
+ this .parent = parent ;
134
+ this .cancel = new CompositeSubscription ();
135
+ this .once = new AtomicBoolean ();
136
+ }
113
137
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
- }
138
+ @ Override
139
+ public void run () {
140
+ try {
141
+ action .call ();
142
+ } finally {
143
+ unsubscribe ();
138
144
}
139
- }, delayTime , unit );
145
+ }
140
146
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 ;
147
+ @ Override
148
+ public boolean isUnsubscribed () {
149
+ return cancel .isUnsubscribed ();
150
+ }
151
+
152
+ @ Override
153
+ public void unsubscribe () {
154
+ if (once .compareAndSet (false , true )) {
155
+ cancel .unsubscribe ();
156
+ parent .remove (this );
157
+ }
158
+ }
159
+ public void add (Subscription s ) {
160
+ cancel .add (s );
161
+ }
162
+ /**
163
+ * Adds a parent to this ScheduledAction so when it is
164
+ * cancelled or terminates, it can remove itself from this parent.
165
+ * @param parent
166
+ */
167
+ public void addParent (CompositeSubscription parent ) {
168
+ cancel .add (new Remover (this , parent ));
169
+ }
146
170
}
147
171
148
172
@ Override
149
173
public void unsubscribe () {
174
+ executor .shutdown ();
150
175
innerSubscription .unsubscribe ();
151
176
}
152
177
@@ -156,11 +181,4 @@ public boolean isUnsubscribed() {
156
181
}
157
182
158
183
}
159
-
160
- /* package */ static interface OnActionComplete {
161
-
162
- public void complete (Subscription s );
163
-
164
- }
165
-
166
184
}
0 commit comments