Skip to content

Commit 171c846

Browse files
SergejIsbrechtSergej Isbrecht
and
Sergej Isbrecht
authored
3.x: Introduce property rx3.scheduler.use-nanotime (#7169)
Issue-Id: #7154 Co-authored-by: Sergej Isbrecht <[email protected]>
1 parent a99b2e0 commit 171c846

File tree

3 files changed

+76
-6
lines changed

3 files changed

+76
-6
lines changed

src/main/java/io/reactivex/rxjava3/core/Scheduler.java

+36-6
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@
6060
* interface which can grant access to the original or hooked {@code Runnable}, thus, a repeated {@code RxJavaPlugins.onSchedule}
6161
* can detect the earlier hook and not apply a new one over again.
6262
* <p>
63-
* The default implementation of {@link #now(TimeUnit)} and {@link Worker#now(TimeUnit)} methods to return current
64-
* {@link System#currentTimeMillis()} value in the desired time unit. Custom {@code Scheduler} implementations can override this
63+
* The default implementation of {@link #now(TimeUnit)} and {@link Worker#now(TimeUnit)} methods to return current {@link System#currentTimeMillis()}
64+
* value in the desired time unit, unless {@code rx3.scheduler.use-nanotime} (boolean) is set. When the property is set to
65+
* {@code true}, the method uses {@link System#nanoTime()} as its basis instead. Custom {@code Scheduler} implementations can override this
6566
* to provide specialized time accounting (such as virtual time to be advanced programmatically).
6667
* Note that operators requiring a {@code Scheduler} may rely on either of the {@code now()} calls provided by
6768
* {@code Scheduler} or {@code Worker} respectively, therefore, it is recommended they represent a logically
@@ -88,6 +89,34 @@
8889
* All methods on the {@code Scheduler} and {@code Worker} classes should be thread safe.
8990
*/
9091
public abstract class Scheduler {
92+
/**
93+
* Value representing whether to use {@link System#nanoTime()}, or default as clock for {@link #now(TimeUnit)}
94+
* and {@link Scheduler.Worker#now(TimeUnit)}
95+
* <p>
96+
* Associated system parameter:
97+
* <ul>
98+
* <li>{@code rx3.scheduler.use-nanotime}, boolean, default {@code false}
99+
* </ul>
100+
*/
101+
static boolean IS_DRIFT_USE_NANOTIME = Boolean.getBoolean("rx3.scheduler.use-nanotime");
102+
103+
/**
104+
* Returns the current clock time depending on state of {@link Scheduler#IS_DRIFT_USE_NANOTIME} in given {@code unit}
105+
* <p>
106+
* By default {@link System#currentTimeMillis()} will be used as the clock. When the property is set
107+
* {@link System#nanoTime()} will be used.
108+
* <p>
109+
* @param unit the time unit
110+
* @return the 'current time' in given unit
111+
* @throws NullPointerException if {@code unit} is {@code null}
112+
*/
113+
static long computeNow(TimeUnit unit) {
114+
if(!IS_DRIFT_USE_NANOTIME) {
115+
return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
116+
}
117+
return unit.convert(System.nanoTime(), TimeUnit.NANOSECONDS);
118+
}
119+
91120
/**
92121
* The tolerance for a clock drift in nanoseconds where the periodic scheduler will rebase.
93122
* <p>
@@ -156,7 +185,7 @@ public static long clockDriftTolerance() {
156185
* @since 2.0
157186
*/
158187
public long now(@NonNull TimeUnit unit) {
159-
return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
188+
return computeNow(unit);
160189
}
161190

162191
/**
@@ -362,8 +391,9 @@ public <S extends Scheduler & Disposable> S when(@NonNull Function<Flowable<Flow
362391
* track the individual {@code Runnable} tasks while they are waiting to be executed (with or without delay) so that
363392
* {@link #dispose()} can prevent their execution or potentially interrupt them if they are currently running.
364393
* <p>
365-
* The default implementation of the {@link #now(TimeUnit)} method returns current
366-
* {@link System#currentTimeMillis()} value in the desired time unit. Custom {@code Worker} implementations can override this
394+
* The default implementation of the {@link #now(TimeUnit)} method returns current {@link System#currentTimeMillis()}
395+
* value in the desired time unit, unless {@code rx3.scheduler.use-nanotime} (boolean) is set. When the property is set to
396+
* {@code true}, the method uses {@link System#nanoTime()} as its basis instead. Custom {@code Worker} implementations can override this
367397
* to provide specialized time accounting (such as virtual time to be advanced programmatically).
368398
* Note that operators requiring a scheduler may rely on either of the {@code now()} calls provided by
369399
* {@code Scheduler} or {@code Worker} respectively, therefore, it is recommended they represent a logically
@@ -482,7 +512,7 @@ public Disposable schedulePeriodically(@NonNull Runnable run, final long initial
482512
* @since 2.0
483513
*/
484514
public long now(@NonNull TimeUnit unit) {
485-
return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
515+
return computeNow(unit);
486516
}
487517

488518
/**

src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
* <li>{@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}</li>
4141
* <li>{@code rx3.purge-enabled} (boolean): enables periodic purging of all {@code Scheduler}'s backing thread pools, default is {@code false}</li>
4242
* <li>{@code rx3.purge-period-seconds} (int): specifies the periodic purge interval of all {@code Scheduler}'s backing thread pools, default is 1 second</li>
43+
* <li>{@code rx3.scheduler.use-nanotime} (boolean): {@code true} instructs {@code Scheduler} to use {@link System#nanoTime()} for {@link Scheduler#now(TimeUnit)},
44+
* instead of default {@link System#currentTimeMillis()} ({@code false})</li>
4345
* </ul>
4446
*/
4547
public final class Schedulers {

src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java

+38
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,48 @@
1616
package io.reactivex.rxjava3.core;
1717

1818
import static org.junit.Assert.assertEquals;
19+
import static org.junit.Assert.assertFalse;
20+
import static org.junit.Assert.assertTrue;
1921

22+
import org.junit.After;
2023
import org.junit.Test;
2124

25+
import java.util.concurrent.TimeUnit;
26+
2227
public class SchedulerTest {
28+
private static final String DRIFT_USE_NANOTIME = "rx3.scheduler.use-nanotime";
29+
30+
@After
31+
public void cleanup() {
32+
// reset value to default in order to not influence other tests
33+
Scheduler.IS_DRIFT_USE_NANOTIME = false;
34+
}
35+
36+
@Test
37+
public void driftUseNanoTimeNotSetByDefault() {
38+
assertFalse(Scheduler.IS_DRIFT_USE_NANOTIME);
39+
assertFalse(Boolean.getBoolean(DRIFT_USE_NANOTIME));
40+
}
41+
42+
@Test
43+
public void computeNow_currentTimeMillis() {
44+
TimeUnit unit = TimeUnit.MILLISECONDS;
45+
assertTrue(isInRange(System.currentTimeMillis(), Scheduler.computeNow(unit), unit, 250, TimeUnit.MILLISECONDS));
46+
}
47+
48+
@Test
49+
public void computeNow_nanoTime() {
50+
TimeUnit unit = TimeUnit.NANOSECONDS;
51+
Scheduler.IS_DRIFT_USE_NANOTIME = true;
52+
53+
assertFalse(isInRange(System.currentTimeMillis(), Scheduler.computeNow(unit), unit, 250, TimeUnit.MILLISECONDS));
54+
assertTrue(isInRange(System.nanoTime(), Scheduler.computeNow(unit), TimeUnit.NANOSECONDS, 250, TimeUnit.MILLISECONDS));
55+
}
56+
57+
private boolean isInRange(long start, long stop, TimeUnit source, long maxDiff, TimeUnit diffUnit) {
58+
long diff = Math.abs(stop - start);
59+
return diffUnit.convert(diff, source) <= maxDiff;
60+
}
2361

2462
@Test
2563
public void clockDriftCalculation() {

0 commit comments

Comments
 (0)