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