15
15
*/
16
16
package rx .schedulers ;
17
17
18
- import java .util .concurrent .ConcurrentLinkedQueue ;
19
- import java .util .concurrent .Executor ;
20
- import java .util .concurrent .Future ;
21
- import java .util .concurrent .RejectedExecutionException ;
22
- import java .util .concurrent .ScheduledExecutorService ;
23
- import java .util .concurrent .TimeUnit ;
18
+ import java .util .concurrent .*;
24
19
import java .util .concurrent .atomic .AtomicInteger ;
25
- import java .util .concurrent .atomic .AtomicIntegerFieldUpdater ;
26
- import rx .Scheduler ;
27
- import rx .Subscription ;
20
+
21
+ import rx .*;
28
22
import rx .functions .Action0 ;
23
+ import rx .internal .schedulers .ScheduledAction ;
29
24
import rx .plugins .RxJavaPlugins ;
30
- import rx .subscriptions .CompositeSubscription ;
31
- import rx .subscriptions .MultipleAssignmentSubscription ;
32
- import rx .subscriptions .Subscriptions ;
25
+ import rx .subscriptions .*;
33
26
34
27
/**
35
28
* Scheduler that wraps an Executor instance and establishes the Scheduler contract upon it.
@@ -58,12 +51,12 @@ static final class ExecutorSchedulerWorker extends Scheduler.Worker implements R
58
51
// TODO: use a better performing structure for task tracking
59
52
final CompositeSubscription tasks ;
60
53
// TODO: use MpscLinkedQueue once available
61
- final ConcurrentLinkedQueue <ExecutorAction > queue ;
54
+ final ConcurrentLinkedQueue <ScheduledAction > queue ;
62
55
final AtomicInteger wip ;
63
56
64
57
public ExecutorSchedulerWorker (Executor executor ) {
65
58
this .executor = executor ;
66
- this .queue = new ConcurrentLinkedQueue <ExecutorAction >();
59
+ this .queue = new ConcurrentLinkedQueue <ScheduledAction >();
67
60
this .wip = new AtomicInteger ();
68
61
this .tasks = new CompositeSubscription ();
69
62
}
@@ -73,11 +66,15 @@ public Subscription schedule(Action0 action) {
73
66
if (isUnsubscribed ()) {
74
67
return Subscriptions .unsubscribed ();
75
68
}
76
- ExecutorAction ea = new ExecutorAction (action , tasks );
69
+ ScheduledAction ea = new ScheduledAction (action , tasks );
77
70
tasks .add (ea );
78
71
queue .offer (ea );
79
72
if (wip .getAndIncrement () == 0 ) {
80
73
try {
74
+ // note that since we schedule the emission of potentially multiple tasks
75
+ // there is no clear way to cancel this schedule from individual tasks
76
+ // so even if executor is an ExecutorService, we can't associate the future
77
+ // returned by submit() with any particular ScheduledAction
81
78
executor .execute (this );
82
79
} catch (RejectedExecutionException t ) {
83
80
// cleanup if rejected
@@ -96,7 +93,10 @@ public Subscription schedule(Action0 action) {
96
93
@ Override
97
94
public void run () {
98
95
do {
99
- queue .poll ().run ();
96
+ ScheduledAction sa = queue .poll ();
97
+ if (!sa .isUnsubscribed ()) {
98
+ sa .run ();
99
+ }
100
100
} while (wip .decrementAndGet () > 0 );
101
101
}
102
102
@@ -115,21 +115,26 @@ public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit
115
115
service = GenericScheduledExecutorService .getInstance ();
116
116
}
117
117
118
+ final MultipleAssignmentSubscription first = new MultipleAssignmentSubscription ();
118
119
final MultipleAssignmentSubscription mas = new MultipleAssignmentSubscription ();
119
- // tasks.add(mas); // Needs a removal without unsubscription
120
+ mas .set (first );
121
+ tasks .add (mas ); // Needs a removal without unsubscription
120
122
121
- try {
122
- Future <?> f = service .schedule (new Runnable () {
123
- @ Override
124
- public void run () {
125
- if (mas .isUnsubscribed ()) {
126
- return ;
127
- }
128
- mas .set (schedule (action ));
129
- // tasks.delete(mas); // Needs a removal without unsubscription
123
+ ScheduledAction ea = new ScheduledAction (new Action0 () {
124
+ @ Override
125
+ public void call () {
126
+ if (mas .isUnsubscribed ()) {
127
+ return ;
130
128
}
131
- }, delayTime , unit );
132
- mas .set (Subscriptions .from (f ));
129
+ mas .set (schedule (action ));
130
+ }
131
+ }, tasks );
132
+ first .set (ea );
133
+ tasks .add (ea );
134
+
135
+ try {
136
+ Future <?> f = service .schedule (ea , delayTime , unit );
137
+ ea .add (f );
133
138
} catch (RejectedExecutionException t ) {
134
139
// report the rejection to plugins
135
140
RxJavaPlugins .getInstance ().getErrorHandler ().handleError (t );
@@ -150,46 +155,4 @@ public void unsubscribe() {
150
155
}
151
156
152
157
}
153
-
154
- /** Runs the actual action and maintains an unsubscription state. */
155
- static final class ExecutorAction implements Runnable , Subscription {
156
- final Action0 actual ;
157
- final CompositeSubscription parent ;
158
- volatile int unsubscribed ;
159
- static final AtomicIntegerFieldUpdater <ExecutorAction > UNSUBSCRIBED_UPDATER
160
- = AtomicIntegerFieldUpdater .newUpdater (ExecutorAction .class , "unsubscribed" );
161
-
162
- public ExecutorAction (Action0 actual , CompositeSubscription parent ) {
163
- this .actual = actual ;
164
- this .parent = parent ;
165
- }
166
-
167
- @ Override
168
- public void run () {
169
- if (isUnsubscribed ()) {
170
- return ;
171
- }
172
- try {
173
- actual .call ();
174
- } catch (Throwable t ) {
175
- RxJavaPlugins .getInstance ().getErrorHandler ().handleError (t );
176
- Thread thread = Thread .currentThread ();
177
- thread .getUncaughtExceptionHandler ().uncaughtException (thread , t );
178
- } finally {
179
- unsubscribe ();
180
- }
181
- }
182
- @ Override
183
- public boolean isUnsubscribed () {
184
- return unsubscribed != 0 ;
185
- }
186
-
187
- @ Override
188
- public void unsubscribe () {
189
- if (UNSUBSCRIBED_UPDATER .compareAndSet (this , 0 , 1 )) {
190
- parent .remove (this );
191
- }
192
- }
193
-
194
- }
195
158
}
0 commit comments