1 /**
2   Utility for medium-precision interval timing.
3 */
4 
5 module dcord.util.ticker;
6 
7 import core.time,
8        std.algorithm.comparison;
9 
10 import vibe.core.core : vibeSleep = sleep;
11 
12 import dcord.util.time;
13 
14 /**
15   Ticker which can be used for interval-based timing. Operates at millisecond
16   precision.
17 */
18 class Ticker {
19   private {
20     long interval;
21     long next = 0;
22   }
23 
24   /**
25     Create a new Ticker
26 
27     Params:
28       interval = interval to tick at (any unit up to millisecond precision)
29       autoStart = whether the instantiation of the object marks the first tick
30   */
31   this(Duration interval, bool autoStart=false) {
32     this.interval = interval.total!"msecs";
33 
34     if (autoStart) this.start();
35   }
36 
37   /// Sets when the next tick occurs
38   void setNext() {
39     this.next = getEpochTimeMilli() + this.interval;
40   }
41 
42   /// Starts the ticker
43   void start() {
44     assert(this.next == 0, "Cannot start already running ticker");
45     this.setNext();
46   }
47 
48   /// Sleeps until the next tick
49   void sleep() {
50     long now = getEpochTimeMilli();
51 
52     if (this.next < now) {
53       this.setNext();
54       return;
55     }
56 
57     long delay = min(this.interval, this.next - now);
58     vibeSleep(delay.msecs);
59     this.setNext();
60   }
61 }
62 
63 /**
64   Ticker which ticks at accurate offsets from a starting point.
65 */
66 class StaticTicker: Ticker {
67   private {
68     long iter;
69     long startTime;
70   }
71 
72   this(Duration interval, bool autoStart=false) {
73     super(interval, autoStart);
74   }
75 
76   void reset() {
77     this.iter = 0;
78     this.next = 0;
79     this.start();
80   }
81 
82   override void start() {
83     this.startTime = getEpochTimeMilli();
84     super.start();
85   }
86 
87   override void setNext() {
88     this.iter += 1;
89     this.next = this.startTime + (this.interval * this.iter);
90   }
91 }