1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.event.timer.Common; 13 14 import hunt.event.selector.Selector; 15 import hunt.io.channel.AbstractChannel; 16 import hunt.io.channel.Common; 17 import hunt.logging.ConsoleLogger; 18 import hunt.Exceptions; 19 20 import std.datetime; 21 import std.exception; 22 23 enum CustomTimerMinTimeOut = 50; // in ms 24 enum CustomTimerWheelSize = 500; 25 enum CustomTimer_Next_TimeOut = cast(long)(CustomTimerMinTimeOut * (2.0 / 3.0)); 26 27 alias TickedEventHandler = void delegate(Object sender); 28 29 alias UintObject = BaseTypeObject!uint; 30 31 /** 32 */ 33 interface ITimer { 34 35 /// 36 bool isActive(); 37 38 /// in ms 39 size_t interval(); 40 41 /// ditto 42 ITimer interval(size_t v); 43 44 /// ditto 45 ITimer interval(Duration duration); 46 47 /// 48 ITimer onTick(TickedEventHandler handler); 49 50 /// immediately: true to call first event immediately 51 /// once: true to call timed event only once 52 void start(bool immediately = false, bool once = false); 53 54 void stop(); 55 56 void reset(bool immediately = false, bool once = false); 57 58 void reset(size_t interval); 59 60 void reset(Duration duration); 61 } 62 63 /** 64 Timing Wheel manger Class 65 */ 66 final class TimingWheel { 67 /** 68 constructor 69 Params: 70 wheelSize = the Wheel's element router. 71 */ 72 this(uint wheelSize) { 73 if (wheelSize == 0) 74 wheelSize = 2; 75 _list = new NullWheelTimer[wheelSize]; 76 for (int i = 0; i < wheelSize; ++i) { 77 _list[i] = new NullWheelTimer(); 78 } 79 } 80 81 /** 82 add a Timer into the Wheel 83 Params: 84 tm = the timer. 85 */ 86 pragma(inline) void addNewTimer(WheelTimer tm, size_t wheel = 0) { 87 size_t index; 88 if (wheel > 0) 89 index = nextWheel(wheel); 90 else 91 index = getPrev(); 92 93 NullWheelTimer timer = _list[index]; 94 tm._next = timer._next; 95 tm._prev = timer; 96 if (timer._next) 97 timer._next._prev = tm; 98 timer._next = tm; 99 tm._manger = this; 100 } 101 102 /** 103 The Wheel go forward 104 Params: 105 size = forward's element size; 106 Notes: 107 all forward's element will timeout. 108 */ 109 void prevWheel(uint size = 1) { 110 if (size == 0) 111 return; 112 foreach (i; 0 .. size) { 113 NullWheelTimer timer = doNext(); 114 timer.onTimeOut(); 115 } 116 } 117 118 protected: 119 /// get next wheel times 's Wheel 120 pragma(inline) size_t nextWheel(size_t wheel) { 121 auto next = wheel % _list.length; 122 return (_now + next) % _list.length; 123 } 124 125 /// get the index whitch is farthest with current index. 126 size_t getPrev() { 127 if (_now == 0) 128 return (_list.length - 1); 129 else 130 return (_now - 1); 131 } 132 /// go forward a element,and return the element. 133 pragma(inline) NullWheelTimer doNext() { 134 ++_now; 135 if (_now == _list.length) 136 _now = 0; 137 return _list[_now]; 138 } 139 /// rest a timer. 140 pragma(inline) void rest(WheelTimer tm, size_t next) { 141 remove(tm); 142 addNewTimer(tm, next); 143 } 144 /// remove the timer. 145 pragma(inline) void remove(WheelTimer tm) { 146 tm._prev._next = tm._next; 147 if (tm._next) 148 tm._next._prev = tm._prev; 149 tm._manger = null; 150 tm._next = null; 151 tm._prev = null; 152 } 153 154 private: 155 NullWheelTimer[] _list; 156 size_t _now; 157 } 158 159 /** 160 The timer parent's class. 161 */ 162 abstract class WheelTimer { 163 ~this() @nogc { 164 // stop(); 165 } 166 /** 167 the function will be called when the timer timeout. 168 */ 169 void onTimeOut(); 170 171 /// rest the timer. 172 pragma(inline) final void rest(size_t next = 0) { 173 if (_manger) { 174 _manger.rest(this, next); 175 } 176 } 177 178 /// stop the time, it will remove from Wheel. 179 pragma(inline) final void stop() { 180 if (_manger) { 181 _manger.remove(this); 182 } 183 } 184 185 /// the time is active. 186 pragma(inline, true) final bool isActive() { 187 return _manger !is null; 188 } 189 190 /// get the timer only run once. 191 pragma(inline, true) final @property oneShop() { 192 return _oneShop; 193 } 194 /// set the timer only run once. 195 pragma(inline) final @property oneShop(bool one) { 196 _oneShop = one; 197 } 198 199 private: 200 WheelTimer _next = null; 201 WheelTimer _prev = null; 202 TimingWheel _manger = null; 203 bool _oneShop = false; 204 } 205 206 /// the Header Timer in the wheel. 207 class NullWheelTimer : WheelTimer { 208 override void onTimeOut() { 209 WheelTimer tm = _next; 210 211 while (tm) { 212 // WheelTimer timer = tm._next; 213 if (tm.oneShop()) { 214 tm.stop(); 215 } 216 tm.onTimeOut(); 217 tm = tm._next; 218 } 219 } 220 } 221 222 /** 223 */ 224 struct CustomTimer { 225 void init() { 226 if (_timeWheel is null) 227 _timeWheel = new TimingWheel(CustomTimerWheelSize); 228 _nextTime = (Clock.currStdTime() / 10000) + CustomTimerMinTimeOut; 229 } 230 231 int doWheel() { 232 auto nowTime = (Clock.currStdTime() / 10000); 233 // tracef("nowTime - _nextTime = %d", nowTime - _nextTime); 234 while (nowTime >= _nextTime) { 235 _timeWheel.prevWheel(); 236 _nextTime += CustomTimerMinTimeOut; 237 nowTime = (Clock.currStdTime() / 10000); 238 } 239 nowTime = _nextTime - nowTime; 240 return cast(int) nowTime; 241 } 242 243 TimingWheel timeWheel() { 244 return _timeWheel; 245 } 246 247 private: 248 TimingWheel _timeWheel; 249 long _nextTime; 250 } 251 252 /** 253 */ 254 abstract class TimerChannelBase : AbstractChannel, ITimer { 255 256 protected bool _isActive = false; 257 protected size_t _interval = 1000; 258 259 /// Timer tick handler 260 TickedEventHandler ticked; 261 262 this(Selector loop) { 263 super(loop, ChannelType.Timer); 264 _timeOut = 50; 265 } 266 267 /// 268 @property bool isActive() { 269 return _isActive; 270 } 271 272 /// in ms 273 @property size_t interval() { 274 return _interval; 275 } 276 277 /// ditto 278 @property ITimer interval(size_t v) { 279 _interval = v; 280 return this; 281 } 282 283 /// ditto 284 @property ITimer interval(Duration duration) { 285 _interval = cast(size_t) duration.total!("msecs"); 286 return this; 287 } 288 289 /// The handler will be handled in another thread. 290 ITimer onTick(TickedEventHandler handler) { 291 this.ticked = handler; 292 return this; 293 } 294 295 @property size_t wheelSize() { 296 return _wheelSize; 297 } 298 299 @property size_t time() { 300 return _interval; 301 } 302 303 void start(bool immediately = false, bool once = false) { 304 _inLoop.register(this); 305 _isRegistered = true; 306 _isActive = true; 307 } 308 309 void stop() { 310 if (_isActive) { 311 _isActive = false; 312 close(); 313 } 314 } 315 316 void reset(size_t interval) { 317 this.interval = interval; 318 reset(); 319 } 320 321 void reset(Duration duration) { 322 this.interval = duration; 323 reset(); 324 } 325 326 void reset(bool immediately = false, bool once = false) { 327 if (_isActive) { 328 stop(); 329 start(); 330 } 331 } 332 333 override void close() { 334 onClose(); 335 } 336 337 protected void onTick() { 338 // trace("tick thread id: ", getTid()); 339 if (ticked !is null) 340 ticked(this); 341 } 342 343 protected: 344 uint _wheelSize; 345 uint _circle; 346 size_t _timeOut; 347 } 348 349 alias TimeoutHandler = void delegate(Object sender); 350 351 /** 352 */ 353 class HuntWheelTimer : WheelTimer { 354 this() { 355 // time = Clock.currTime(); 356 } 357 358 // override void onTimeOut() nothrow 359 // { 360 // collectException(trace("\nname is ", name, " \tcutterTime is : ", 361 // Clock.currTime().toSimpleString(), "\t new time is : ", time.toSimpleString())); 362 // } 363 364 override void onTimeOut() { 365 _now++; 366 if (_now >= _circle) { 367 _now = 0; 368 if (timeout !is null) { 369 timeout(this); 370 } 371 } 372 } 373 374 TimeoutHandler timeout; 375 376 private: 377 // SysTime time; 378 // uint _wheelSize; 379 uint _circle; 380 uint _now = 0; 381 }