21
21
22
22
import rx .Scheduler ;
23
23
import rx .Subscription ;
24
- import rx .subscriptions .CompositeSubscription ;
25
24
import rx .subscriptions .MultipleAssignmentSubscription ;
25
+ import rx .util .functions .Func1 ;
26
26
import rx .util .functions .Func2 ;
27
27
28
28
/**
@@ -36,7 +36,17 @@ public static CurrentThreadScheduler getInstance() {
36
36
return INSTANCE ;
37
37
}
38
38
39
- private static final ThreadLocal <PriorityQueue <TimedAction >> QUEUE = new ThreadLocal <PriorityQueue <TimedAction >>();
39
+ private static final ThreadLocal <PriorityQueue <TimedAction >> QUEUE = new ThreadLocal <PriorityQueue <TimedAction >>() {
40
+ protected java .util .PriorityQueue <TimedAction > initialValue () {
41
+ return new PriorityQueue <TimedAction >();
42
+ };
43
+ };
44
+
45
+ private static final ThreadLocal <Boolean > PROCESSING = new ThreadLocal <Boolean >() {
46
+ protected Boolean initialValue () {
47
+ return Boolean .FALSE ;
48
+ };
49
+ };
40
50
41
51
/* package accessible for unit tests */ CurrentThreadScheduler () {
42
52
}
@@ -45,50 +55,57 @@ public static CurrentThreadScheduler getInstance() {
45
55
public <T > Subscription schedule (T state , Func2 <? super Scheduler , ? super T , ? extends Subscription > action ) {
46
56
// immediately move to the InnerCurrentThreadScheduler
47
57
InnerCurrentThreadScheduler innerScheduler = new InnerCurrentThreadScheduler ();
48
- DiscardableAction < T > discardableAction = new DiscardableAction <T >(state , action );
49
- enqueue (innerScheduler , discardableAction , now ());
58
+ innerScheduler . enqueue ( new DiscardableAction <T >(state , action ), now () );
59
+ enqueueFromOuter (innerScheduler , now ());
50
60
return innerScheduler ;
51
61
}
52
62
53
63
@ Override
54
64
public <T > Subscription schedule (T state , Func2 <? super Scheduler , ? super T , ? extends Subscription > action , long dueTime , TimeUnit unit ) {
55
65
long execTime = now () + unit .toMillis (dueTime );
56
66
57
- // immediately move to the InnerCurrentThreadScheduler
67
+ // create an inner scheduler and queue it for execution
58
68
InnerCurrentThreadScheduler innerScheduler = new InnerCurrentThreadScheduler ();
59
- DiscardableAction < T > discardableAction = new DiscardableAction <T >(state , new SleepingAction <T >(action , this , execTime ));
60
- enqueue (innerScheduler , discardableAction , execTime );
61
- return discardableAction ;
69
+ innerScheduler . enqueue ( new DiscardableAction <T >(state , new SleepingAction <T >(action , this , execTime )), execTime );
70
+ enqueueFromOuter (innerScheduler , execTime );
71
+ return innerScheduler ;
62
72
}
63
73
64
- private static void enqueue (Scheduler scheduler , DiscardableAction <?> action , long execTime ) {
74
+ /*
75
+ * This will accept InnerCurrentThreadScheduler instances and execute them in order they are received
76
+ * and on each of them will loop internally until each is complete.
77
+ */
78
+ private void enqueueFromOuter (final InnerCurrentThreadScheduler innerScheduler , long execTime ) {
79
+ // Note that everything here is single-threaded so we won't have race conditions
65
80
PriorityQueue <TimedAction > queue = QUEUE .get ();
66
- boolean exec = queue == null ;
81
+ queue . add ( new TimedAction ( new Func1 < Scheduler , Subscription >() {
67
82
68
- if ( exec ) {
69
- queue = new PriorityQueue < TimedAction >();
70
- QUEUE . set ( queue );
71
- }
72
-
73
- queue . add ( new TimedAction ( action , execTime , counter .incrementAndGet ()));
83
+ @ Override
84
+ public Subscription call ( Scheduler _) {
85
+ // when the InnerCurrentThreadScheduler gets scheduled we want to process its tasks
86
+ return innerScheduler . startProcessing ();
87
+ }
88
+ } , execTime , counter .incrementAndGet ()));
74
89
75
- if (exec ) {
90
+ // first time through starts the loop
91
+ if (!PROCESSING .get ()) {
92
+ PROCESSING .set (Boolean .TRUE );
76
93
while (!queue .isEmpty ()) {
77
- queue .poll ().action .call (scheduler );
94
+ queue .poll ().action .call (innerScheduler );
78
95
}
79
-
80
- QUEUE .set (null );
96
+ PROCESSING .set (Boolean .FALSE );
81
97
}
82
98
}
83
99
84
100
private static class InnerCurrentThreadScheduler extends Scheduler implements Subscription {
85
101
private final MultipleAssignmentSubscription childSubscription = new MultipleAssignmentSubscription ();
102
+ private final PriorityQueue <TimedAction > innerQueue = new PriorityQueue <TimedAction >();
86
103
87
104
@ Override
88
105
public <T > Subscription schedule (T state , Func2 <? super Scheduler , ? super T , ? extends Subscription > action ) {
89
106
DiscardableAction <T > discardableAction = new DiscardableAction <T >(state , action );
90
107
childSubscription .set (discardableAction );
91
- enqueue (this , discardableAction , now ());
108
+ enqueue (discardableAction , now ());
92
109
return childSubscription ;
93
110
}
94
111
@@ -98,10 +115,21 @@ public <T> Subscription schedule(T state, Func2<? super Scheduler, ? super T, ?
98
115
99
116
DiscardableAction <T > discardableAction = new DiscardableAction <T >(state , action );
100
117
childSubscription .set (discardableAction );
101
- enqueue (this , discardableAction , execTime );
118
+ enqueue (discardableAction , execTime );
102
119
return childSubscription ;
103
120
}
104
121
122
+ private void enqueue (Func1 <Scheduler , Subscription > action , long execTime ) {
123
+ innerQueue .add (new TimedAction (action , execTime , counter .incrementAndGet ()));
124
+ }
125
+
126
+ private Subscription startProcessing () {
127
+ while (!innerQueue .isEmpty ()) {
128
+ innerQueue .poll ().action .call (this );
129
+ }
130
+ return this ;
131
+ }
132
+
105
133
@ Override
106
134
public void unsubscribe () {
107
135
childSubscription .unsubscribe ();
@@ -113,11 +141,11 @@ public void unsubscribe() {
113
141
* Use time to sort items so delayed actions are sorted to their appropriate position in the queue.
114
142
*/
115
143
private static class TimedAction implements Comparable <TimedAction > {
116
- final DiscardableAction <? > action ;
144
+ final Func1 < Scheduler , Subscription > action ;
117
145
final Long execTime ;
118
146
final Long count ; // In case if time between enqueueing took less than 1ms
119
147
120
- private TimedAction (DiscardableAction <? > action , Long execTime , Long count ) {
148
+ private TimedAction (Func1 < Scheduler , Subscription > action , Long execTime , Long count ) {
121
149
this .action = action ;
122
150
this .execTime = execTime ;
123
151
this .count = count ;
0 commit comments