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