1 module libasync.timer; 2 3 import libasync.types; 4 import libasync.events; 5 import std.datetime; 6 7 final class AsyncTimer 8 { 9 10 nothrow: 11 private: 12 bool m_oneshot = true; 13 fd_t m_timerId; 14 EventLoop m_evLoop; 15 TimerHandler m_evh; 16 Duration m_timeout; 17 bool m_shooting = false; 18 bool m_rearmed = false; 19 20 public: 21 this(EventLoop evl) 22 in { assert(evl !is null); } 23 body { m_evLoop = evl; } 24 25 mixin DefStatus; 26 27 /// Returns the Duration that the timer will wait before calling the handler 28 /// after it is run. 29 @property Duration timeout() const { 30 return m_timeout; 31 } 32 33 /// Returns whether the timer is set to rearm itself (oneShot=false) or 34 /// if it will have to be rearmed (oneShot=true). 35 @property bool oneShot() const { 36 return m_oneshot; 37 } 38 39 /// Sets the timer to become periodic. For a running timer, 40 /// this setting will take effect after the timer is expired (oneShot) or 41 /// after it is killed (periodic). 42 typeof(this) periodic(bool b = true) 43 in { assert(m_timerId == 0 || m_oneshot); } 44 body 45 { 46 m_oneshot = !b; 47 return this; 48 } 49 50 /// Sets or changes the duration on the timer. For a running timer, 51 /// this setting will take effect after the timer is expired (oneShot) or 52 /// after it is killed (periodic). 53 typeof(this) duration(Duration dur) { 54 m_timeout = dur; 55 return this; 56 } 57 58 /// Runs a non-periodic, oneshot timer once using the specified Duration as 59 /// a timeout. The handler from the last call to run() will be reused. 60 bool rearm(Duration dur) 61 in { 62 assert(m_timeout > 0.seconds); 63 // assert(m_shooting); 64 assert(m_oneshot, "Cannot rearm a periodic timer, it must fist be killed."); 65 } 66 body { 67 m_rearmed = true; 68 69 m_timerId = m_evLoop.run(this, m_evh, dur); 70 m_timeout = dur; 71 72 if (m_timerId == 0) 73 return false; 74 else 75 return true; 76 } 77 78 /// Starts the timer using the delegate as an expiration callback. 79 bool run(void delegate() del) 80 in { 81 assert(m_timeout > 0.seconds); 82 assert(m_oneshot || !m_timerId, "Cannot rearm a periodic timer, it must fist be killed."); 83 } 84 body { 85 TimerHandler handler; 86 handler.del = del; 87 handler.ctxt = this; 88 89 return run(handler); 90 } 91 92 private bool run(TimerHandler cb) { 93 m_evh = cb; 94 95 if (m_timerId) 96 m_rearmed = true; 97 else 98 m_rearmed = false; 99 m_timerId = m_evLoop.run(this, cb, m_timeout); 100 // try writeln("Timer starting", m_timerId); catch {} 101 if (m_timerId == 0) 102 return false; 103 else 104 return true; 105 } 106 107 /// Cleans up underlying OS resources. This is required to change the 108 /// timer from periodic to oneshot, or before disposing of this object. 109 bool kill() { 110 return m_evLoop.kill(this); 111 } 112 113 package: 114 115 version(Posix) mixin EvInfoMixins; 116 117 @property fd_t id() { 118 return m_timerId; 119 } 120 121 @property void id(fd_t fd) { 122 m_timerId = fd; 123 } 124 125 @property void rearmed(bool b) { 126 m_rearmed = b; 127 } 128 129 @property bool rearmed() { 130 return m_rearmed; 131 } 132 133 /*void handler() { 134 try m_evh(); 135 catch {} 136 return; 137 }*/ 138 } 139 140 package struct TimerHandler { 141 AsyncTimer ctxt; 142 void delegate() del; 143 void opCall() { 144 assert(ctxt !is null); 145 ctxt.m_rearmed = false; 146 ctxt.m_shooting = true; 147 del(); 148 ctxt.m_shooting = false; 149 return; 150 } 151 }