15
15
16
16
import java .util .Queue ;
17
17
import java .util .concurrent .*;
18
+ import java .util .concurrent .atomic .AtomicReference ;
18
19
19
- import io .reactivex .rxjava3 .annotations .NonNull ;
20
+ import io .reactivex .rxjava3 .annotations .* ;
20
21
import io .reactivex .rxjava3 .core .Scheduler ;
21
- import io .reactivex .rxjava3 .disposables .* ;
22
+ import io .reactivex .rxjava3 .disposables .Disposable ;
22
23
import io .reactivex .rxjava3 .internal .disposables .EmptyDisposable ;
24
+ import io .reactivex .rxjava3 .plugins .RxJavaPlugins ;
23
25
24
26
/**
25
27
* A special, non thread-safe scheduler for testing operators that require
26
28
* a scheduler without introducing real concurrency and allows manually advancing
27
29
* a virtual time.
30
+ * <p>
31
+ * By default, the tasks submitted via the various {@code schedule} methods are not
32
+ * wrapped by the {@link RxJavaPlugins#onSchedule(Runnable)} hook. To enable this behavior,
33
+ * create a {@code TestScheduler} via {@link #TestScheduler(boolean)} or {@link #TestScheduler(long, TimeUnit, boolean)}.
28
34
*/
29
35
public final class TestScheduler extends Scheduler {
30
36
/** The ordered queue for the runnable tasks. */
31
37
final Queue <TimedRunnable > queue = new PriorityBlockingQueue <>(11 );
38
+ /** Use the {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. */
39
+ final boolean useOnScheduleHook ;
32
40
/** The per-scheduler global order counter. */
33
41
long counter ;
34
42
// Storing time in nanoseconds internally.
@@ -38,7 +46,20 @@ public final class TestScheduler extends Scheduler {
38
46
* Creates a new TestScheduler with initial virtual time of zero.
39
47
*/
40
48
public TestScheduler () {
41
- // No-op.
49
+ this (false );
50
+ }
51
+
52
+ /**
53
+ * Creates a new TestScheduler with the option to use the
54
+ * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks.
55
+ * @param useOnScheduleHook if {@code true}, the tasks submitted to this
56
+ * TestScheduler is wrapped via the
57
+ * {@link RxJavaPlugins#onSchedule(Runnable)} hook
58
+ * @since 3.0.10 - experimental
59
+ */
60
+ @ Experimental
61
+ public TestScheduler (boolean useOnScheduleHook ) {
62
+ this .useOnScheduleHook = useOnScheduleHook ;
42
63
}
43
64
44
65
/**
@@ -50,7 +71,27 @@ public TestScheduler() {
50
71
* the units of time that {@code delayTime} is expressed in
51
72
*/
52
73
public TestScheduler (long delayTime , TimeUnit unit ) {
74
+ this (delayTime , unit , false );
75
+ }
76
+
77
+ /**
78
+ * Creates a new TestScheduler with the specified initial virtual time
79
+ * and with the option to use the
80
+ * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks.
81
+ *
82
+ * @param delayTime
83
+ * the point in time to move the Scheduler's clock to
84
+ * @param unit
85
+ * the units of time that {@code delayTime} is expressed in
86
+ * @param useOnScheduleHook if {@code true}, the tasks submitted to this
87
+ * TestScheduler is wrapped via the
88
+ * {@link RxJavaPlugins#onSchedule(Runnable)} hook
89
+ * @since 3.0.10 - experimental
90
+ */
91
+ @ Experimental
92
+ public TestScheduler (long delayTime , TimeUnit unit , boolean useOnScheduleHook ) {
53
93
time = unit .toNanos (delayTime );
94
+ this .useOnScheduleHook = useOnScheduleHook ;
54
95
}
55
96
56
97
static final class TimedRunnable implements Comparable <TimedRunnable > {
@@ -163,10 +204,13 @@ public Disposable schedule(@NonNull Runnable run, long delayTime, @NonNull TimeU
163
204
if (disposed ) {
164
205
return EmptyDisposable .INSTANCE ;
165
206
}
207
+ if (useOnScheduleHook ) {
208
+ run = RxJavaPlugins .onSchedule (run );
209
+ }
166
210
final TimedRunnable timedAction = new TimedRunnable (this , time + unit .toNanos (delayTime ), run , counter ++);
167
211
queue .add (timedAction );
168
212
169
- return Disposable . fromRunnable ( new QueueRemove (timedAction ) );
213
+ return new QueueRemove (timedAction );
170
214
}
171
215
172
216
@ NonNull
@@ -175,26 +219,38 @@ public Disposable schedule(@NonNull Runnable run) {
175
219
if (disposed ) {
176
220
return EmptyDisposable .INSTANCE ;
177
221
}
222
+ if (useOnScheduleHook ) {
223
+ run = RxJavaPlugins .onSchedule (run );
224
+ }
178
225
final TimedRunnable timedAction = new TimedRunnable (this , 0 , run , counter ++);
179
226
queue .add (timedAction );
180
- return Disposable . fromRunnable ( new QueueRemove (timedAction ) );
227
+ return new QueueRemove (timedAction );
181
228
}
182
229
183
230
@ Override
184
231
public long now (@ NonNull TimeUnit unit ) {
185
232
return TestScheduler .this .now (unit );
186
233
}
187
234
188
- final class QueueRemove implements Runnable {
189
- final TimedRunnable timedAction ;
235
+ final class QueueRemove extends AtomicReference <TimedRunnable > implements Disposable {
236
+
237
+ private static final long serialVersionUID = -7874968252110604360L ;
190
238
191
239
QueueRemove (TimedRunnable timedAction ) {
192
- this .timedAction = timedAction ;
240
+ this .lazySet (timedAction );
241
+ }
242
+
243
+ @ Override
244
+ public void dispose () {
245
+ TimedRunnable tr = getAndSet (null );
246
+ if (tr != null ) {
247
+ queue .remove (tr );
248
+ }
193
249
}
194
250
195
251
@ Override
196
- public void run () {
197
- queue . remove ( timedAction ) ;
252
+ public boolean isDisposed () {
253
+ return get () == null ;
198
254
}
199
255
}
200
256
}
0 commit comments