Skip to content

Commit 0e9f2d9

Browse files
authored
0.42.1
- Refactor DSRuntime to use nanoTime rather than currentTimeMillis - Add runAfterDelay public method to DSRuntime - Add unit tests for DSRuntime
1 parent 41fe46a commit 0e9f2d9

File tree

5 files changed

+244
-55
lines changed

5 files changed

+244
-55
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ subprojects {
55
apply plugin: 'maven'
66

77
group 'org.iot-dsa'
8-
version '0.42.0'
8+
version '0.42.1'
99

1010
sourceCompatibility = 1.6
1111
targetCompatibility = 1.6

dslink-v2/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ artifacts {
44
}
55

66
dependencies {
7-
testImplementation 'org.testng:testng:[6.14.3,)'
7+
testImplementation 'org.testng:testng:6.14.3'
88
}
99

1010
test {

dslink-v2/src/main/java/org/iot/dsa/DSRuntime.java

Lines changed: 87 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public class DSRuntime {
1414
///////////////////////////////////////////////////////////////////////////
1515

1616
private static boolean alive = true;
17-
private static long nextCycle = -1;
17+
private static long nextCycle = 0;
18+
private static boolean hasStarted = false;
1819
private static RuntimeThread runtimeThread;
1920
private static Timer timerHead;
2021
private static Timer timerTail;
@@ -35,27 +36,30 @@ private DSRuntime() {
3536
* Returns the next time execution is needed.
3637
*/
3738
private static void executeTimers() {
38-
long now = System.currentTimeMillis();
39-
long nextCycleTmp = now + 60000;
39+
long now = System.nanoTime();
40+
long nextCycleTmp = now + 60000000000l;
4041
Timer current = null;
4142
Timer next = null;
4243
Timer keepHead = null;
4344
Timer keepTail = null;
4445
long tmp;
46+
boolean futureWork;
4547
//Take the task link list.
4648
synchronized (DSRuntime.class) {
4749
nextCycle = nextCycleTmp;
50+
hasStarted = true;
4851
current = timerHead;
4952
timerHead = null;
5053
timerTail = null;
5154
}
5255
//Execute items (if needed) and retains tasks with future work.
5356
while (alive && (current != null)) {
54-
tmp = current.run(now);
57+
futureWork = current.run(now);
5558
next = current.next;
5659
current.next = null;
57-
if (tmp > 0) {
58-
if (tmp < nextCycleTmp) {
60+
if (futureWork) {
61+
tmp = current.nextRunNanos();
62+
if (tmp - nextCycleTmp < 0) {
5963
nextCycleTmp = tmp;
6064
}
6165
if (keepHead == null) {
@@ -70,7 +74,7 @@ private static void executeTimers() {
7074
}
7175
//Add the tasks that have future work back to the main linked list.
7276
synchronized (DSRuntime.class) {
73-
if (nextCycleTmp < nextCycle) {
77+
if (nextCycleTmp - nextCycle < 0) {
7478
nextCycle = nextCycleTmp;
7579
}
7680
if (keepHead != null) {
@@ -97,11 +101,27 @@ public static void run(Runnable arg) {
97101
*
98102
* @param arg What to runAt.
99103
* @param start First absolute execution time, or if less or equal to 0, start immediately.
100-
* @param interval The millisecond interval at which to run.
104+
* @param intervalMillis The millisecond interval at which to run.
101105
* @return For inspecting and cancel execution.
102106
*/
103-
public static Timer run(Runnable arg, long start, long interval) {
104-
Timer f = new Timer(arg, start, interval);
107+
public static Timer run(Runnable arg, long start, long intervalMillis) {
108+
long delayMillis = start - System.currentTimeMillis();
109+
return runAfterDelay(arg, delayMillis < 0 ? 0 : delayMillis, intervalMillis);
110+
}
111+
112+
/**
113+
* Run periodically starting after the given millisecond delay and repeat at the given millisecond interval.
114+
*
115+
* @param arg What to runAt.
116+
* @param delayMillis The number of millis to wait before first execution.
117+
* @param intervalMillis The millisecond interval at which to run.
118+
* @return For inspecting and cancel execution.
119+
*/
120+
public static Timer runAfterDelay(Runnable arg, long delayMillis, long intervalMillis) {
121+
long intervalNanos = intervalMillis * DSTime.NANOS_IN_MS;
122+
long delayNanos = delayMillis * DSTime.NANOS_IN_MS;
123+
long startNanos = System.nanoTime() + delayNanos;
124+
Timer f = new Timer(arg, startNanos, intervalNanos);
105125
synchronized (DSRuntime.class) {
106126
if (timerHead == null) {
107127
timerHead = f;
@@ -110,8 +130,9 @@ public static Timer run(Runnable arg, long start, long interval) {
110130
timerTail.next = f;
111131
timerTail = f;
112132
}
113-
if (start < nextCycle) {
114-
nextCycle = start;
133+
if (!hasStarted || startNanos - nextCycle < 0) {
134+
nextCycle = startNanos;
135+
hasStarted = true;
115136
DSRuntime.class.notifyAll();
116137
}
117138
}
@@ -126,7 +147,21 @@ public static Timer run(Runnable arg, long start, long interval) {
126147
* @return For inspecting and cancel execution.
127148
*/
128149
public static Timer runAt(Runnable arg, long at) {
129-
Timer f = new Timer(arg, at, -1);
150+
long delayMillis = at - System.currentTimeMillis();
151+
return runDelayed(arg, delayMillis < 0 ? 0 : delayMillis);
152+
}
153+
154+
/**
155+
* Run once after the given delay.
156+
*
157+
* @param arg What to runAt.
158+
* @param delayMillis The number of millis to wait before running.
159+
* @return For inspecting and cancel execution.
160+
*/
161+
public static Timer runDelayed(Runnable arg, long delayMillis) {
162+
long delayNanos = delayMillis * DSTime.NANOS_IN_MS;
163+
long startNanos = System.nanoTime() + delayNanos;
164+
Timer f = new Timer(arg, startNanos, -1);
130165
synchronized (DSRuntime.class) {
131166
if (timerHead == null) {
132167
timerHead = f;
@@ -135,25 +170,15 @@ public static Timer runAt(Runnable arg, long at) {
135170
timerTail.next = f;
136171
timerTail = f;
137172
}
138-
if (at < nextCycle) {
139-
nextCycle = at;
173+
if (!hasStarted || startNanos - nextCycle < 0) {
174+
nextCycle = startNanos;
175+
hasStarted = true;
140176
DSRuntime.class.notifyAll();
141177
}
142178
}
143179
return f;
144180
}
145181

146-
/**
147-
* Run once after the given delay.
148-
*
149-
* @param arg What to runAt.
150-
* @param delayMillis The number of millis to wait before running.
151-
* @return For inspecting and cancel execution.
152-
*/
153-
public static Timer runDelayed(Runnable arg, long delayMillis) {
154-
return runAt(arg, System.currentTimeMillis() + delayMillis);
155-
}
156-
157182
private static void shutdown() {
158183
synchronized (DSRuntime.class) {
159184
alive = false;
@@ -173,18 +198,21 @@ public static class Timer implements Runnable {
173198

174199
private long count = 0;
175200
private long interval = 0;
176-
private long lastRun = -1;
201+
private long lastRun = 0;
202+
boolean hasRun = false;
177203
private Timer next; //linked list
178-
private long nextRun = 1; //0 == canceled, <0 == done
204+
private long nextRun = 0;
205+
boolean cancelled = false;
206+
boolean done = false;
179207
private Runnable runnable;
180208
private boolean running = false;
181209
private boolean skipMissed = true;
182210

183-
Timer(Runnable runnable, long start, long interval) {
211+
private Timer(Runnable runnable, long start, long interval) {
184212
this.interval = interval;
185-
if (start <= 0) {
186-
start = System.currentTimeMillis();
187-
}
213+
// if (start <= 0) {
214+
// start = System.currentTimeMillis();
215+
// }
188216
this.nextRun = start;
189217
this.runnable = runnable;
190218
}
@@ -194,30 +222,32 @@ public static class Timer implements Runnable {
194222
* already cancelled.
195223
*/
196224
public void cancel() {
197-
if (nextRun > 0) {
198-
nextRun = 0;
225+
if (!done) {
226+
done = true;
227+
cancelled = true;
199228
}
200229
}
201230

202-
private long computeNextRun(long now) {
231+
private boolean computeNextRun(long now) {
203232
if (interval <= 0) {
204-
return nextRun = -1;
233+
done = true;
234+
return false;
205235
}
206236
if (skipMissed) {
207-
while (nextRun <= now) {
237+
while (nextRun - now <= 0) {
208238
nextRun += interval;
209239
}
210240
} else {
211241
nextRun += interval;
212242
}
213-
return nextRun;
243+
return true;
214244
}
215245

216246
/**
217247
* The interval between runs, zero or less for no interval.
218248
*/
219249
public long getInterval() {
220-
return interval;
250+
return interval / DSTime.NANOS_IN_MS;
221251
}
222252

223253
/**
@@ -228,14 +258,14 @@ public Runnable getRunnable() {
228258
}
229259

230260
public boolean isCancelled() {
231-
return nextRun == 0;
261+
return cancelled;
232262
}
233263

234264
/**
235265
* True if cancelled or was a one time execution and that has finished.
236266
*/
237267
public boolean isFinished() {
238-
return nextRun <= 0;
268+
return done;
239269
}
240270

241271
/**
@@ -249,7 +279,7 @@ public boolean isRunning() {
249279
* The lastRun run or -1 if it hasn't run yet.
250280
*/
251281
public long lastRun() {
252-
return lastRun;
282+
return hasRun ? DSTime.nanoTimeToSystemTimeMillis(lastRun) : -1;
253283
}
254284

255285
/**
@@ -258,7 +288,7 @@ public long lastRun() {
258288
* @return 0 or less when finished.
259289
*/
260290
public long nextRun() {
261-
return nextRun;
291+
return done ? cancelled ? 0 : -1 : DSTime.nanoTimeToSystemTimeMillis(nextRun);
262292
}
263293

264294
/**
@@ -276,27 +306,32 @@ public void run() {
276306
* Executes the task if it is time.
277307
*
278308
* @param now The current time, just an efficiency.
279-
* @return The next update time, or 0 or less if done.
309+
* @return Whether the task should be run at some point in the future
280310
*/
281-
long run(long now) {
282-
if (nextRun <= 0) {
283-
return nextRun;
311+
boolean run(long now) {
312+
if (done) {
313+
return false;
284314
}
285-
if (now < nextRun) {
286-
return nextRun;
315+
if (now - nextRun < 0) {
316+
return true;
287317
}
288318
if (running) {
289319
if (skipMissed) {
290320
return computeNextRun(now);
291321
}
292-
return nextRun;
322+
return true;
293323
}
294324
running = true;
295325
DSRuntime.run(this);
296326
count++;
297327
lastRun = nextRun;
328+
hasRun = true;
298329
return computeNextRun(now);
299330
}
331+
332+
long nextRunNanos() {
333+
return nextRun;
334+
}
300335

301336
/**
302337
* The number of completed runs.
@@ -319,7 +354,7 @@ public Timer setSkipMissedIntervals(boolean skipMissed) {
319354

320355
public String toString() {
321356
StringBuilder buf = new StringBuilder();
322-
DSTime.encode(nextRun, false, buf);
357+
DSTime.encode(nextRun(), false, buf);
323358
buf.append(" - ").append(runnable.toString());
324359
return buf.toString();
325360
}
@@ -341,7 +376,7 @@ public void run() {
341376
while (alive) {
342377
executeTimers();
343378
synchronized (DSRuntime.class) {
344-
delta = nextCycle - System.currentTimeMillis();
379+
delta = (nextCycle - System.nanoTime()) / DSTime.NANOS_IN_MS;
345380
if (delta > 0) {
346381
try {
347382
DSRuntime.class.wait(delta);
@@ -381,5 +416,4 @@ public void run() {
381416
runtimeThread = new RuntimeThread();
382417
runtimeThread.start();
383418
}
384-
385419
}

dslink-v2/src/main/java/org/iot/dsa/time/DSTime.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,14 @@ private static void validateChar(char c1, char c2) {
795795
throw new IllegalStateException();
796796
}
797797
}
798+
799+
public static long nanoTimeToSystemTimeMillis(long nanoTime) {
800+
long nowNanos = System.nanoTime();
801+
long nowMillis = System.currentTimeMillis();
802+
long nanosTillTime = nanoTime - nowNanos;
803+
long millisTillTime = nanosTillTime / NANOS_IN_MS;
804+
return nowMillis + millisTillTime;
805+
}
798806

799807

800808
}

0 commit comments

Comments
 (0)