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