1 module yu.timer.timingwheeltimer; 2 3 import yu.memory.allocator.smartgcalloctor; 4 import std.experimental.allocator; 5 6 alias TimingWheel = ITimingWheel!SmartGCAllocator; 7 alias WheelTimer = TimingWheel.WheelTimer; 8 alias NullWheelTimer = TimingWheel.NullWheelTimer; 9 /** 10 Timing Wheel manger Class 11 */ 12 @trusted final class ITimingWheel(Allocator) { 13 alias WheelTimer = IWheelTimer!Allocator; 14 alias NullWheelTimer = INullWheelTimer!Allocator; 15 /** 16 constructor 17 Params: 18 wheelSize = the Wheel's element router. 19 */ 20 static if (stateSize!Allocator == 0) { 21 this(uint wheelSize) { 22 _init(wheelSize); 23 } 24 } else { 25 this(uint wheelSize, Allocator alloc) { 26 _alloc = alloc; 27 _init(wheelSize); 28 } 29 } 30 31 ~this() { 32 rmGcScan(); 33 foreach (NullWheelTimer tm; _list) { 34 _alloc.dispose(tm); 35 tm = null; 36 } 37 _alloc.dispose(_list); 38 } 39 40 /** 41 add a Timer into the Wheel 42 Params: 43 tm = the timer. 44 Notes: 如果对象是来自GC,而yuAlloctor 非GC,则注意需要把添加内存到GC扫描区 45 */ 46 pragma(inline) void addNewTimer(WheelTimer tm, size_t wheel = 0) nothrow { 47 size_t index; 48 if (wheel > 0) 49 index = nextWheel(wheel); 50 else 51 index = getPrev(); 52 NullWheelTimer timer = _list[index]; 53 tm._next = timer._next; 54 tm._prev = timer; 55 if (timer._next) 56 timer._next._prev = tm; 57 timer._next = tm; 58 tm._manger = this; 59 } 60 61 /** 62 The Wheel go forward 63 Params: 64 size = forward's element size; 65 Notes: 66 all forward's element will timeout. 67 */ 68 void prevWheel(uint size = 1) nothrow { 69 if (size == 0) 70 return; 71 foreach (i; 0 .. size) { 72 NullWheelTimer timer = doNext(); 73 timer.onTimeOut(); 74 } 75 } 76 77 void addGcScan() { 78 if (_gcScan) 79 return; 80 import core.memory; 81 82 foreach (NullWheelTimer tm; _list) { 83 GC.addRange(cast(void*) tm, NullWheelTimer.sizeof); 84 } 85 86 } 87 88 void rmGcScan() { 89 if (!_gcScan) 90 return; 91 import core.memory; 92 93 foreach (NullWheelTimer tm; _list) { 94 GC.removeRange(cast(void*) tm); 95 } 96 } 97 98 @property isGcScan() { 99 return _gcScan; 100 } 101 102 protected: 103 /// get next wheel times 's Wheel 104 pragma(inline) size_t nextWheel(size_t wheel) nothrow { 105 auto next = wheel % _list.length; 106 return (_now + next) % _list.length; 107 } 108 109 /// get the index whitch is farthest with current index. 110 size_t getPrev() const nothrow { 111 if (_now == 0) 112 return (_list.length - 1); 113 else 114 return (_now - 1); 115 } 116 /// go forward a element,and return the element. 117 pragma(inline) NullWheelTimer doNext() nothrow { 118 ++_now; 119 if (_now == _list.length) 120 _now = 0; 121 return _list[_now]; 122 } 123 /// rest a timer. 124 pragma(inline) void rest(WheelTimer tm, size_t next) nothrow { 125 remove(tm); 126 tm._manger = null; 127 tm._next = null; 128 tm._prev = null; 129 addNewTimer(tm, next); 130 } 131 /// remove the timer. 132 pragma(inline) void remove(WheelTimer tm) nothrow { 133 tm._prev._next = tm._next; 134 if (tm._next) 135 tm._next._prev = tm._prev; 136 } 137 138 private: 139 void _init(uint wheelSize) { 140 if (wheelSize == 0) 141 wheelSize = 2; 142 _list = () @trusted{ return makeArray!NullWheelTimer(_alloc, wheelSize); }(); 143 for (int i = 0; i < wheelSize; ++i) 144 _list[i] = _alloc.make!NullWheelTimer(); 145 } 146 147 NullWheelTimer[] _list; 148 size_t _now; 149 static if (stateSize!Allocator == 0) 150 alias _alloc = Allocator.instance; 151 else 152 Allocator _alloc; 153 154 bool _gcScan = false; 155 } 156 157 /** 158 The timer parent's class. 159 */ 160 @trusted abstract class IWheelTimer(Allocator) { 161 alias TimerWheel = ITimingWheel!Allocator; 162 alias WheelTimer = IWheelTimer!Allocator; 163 164 ~this() { 165 stop(); 166 } 167 /** 168 the function will be called when the timer timeout. 169 */ 170 void onTimeOut() nothrow; 171 172 /// rest the timer. 173 pragma(inline) final void rest(size_t next = 0) nothrow { 174 if (_manger) { 175 _manger.rest(this, next); 176 } 177 } 178 179 /// stop the time, it will remove from Wheel. 180 pragma(inline) final void stop() nothrow { 181 if (_manger) { 182 _manger.remove(this); 183 } 184 _manger = null; 185 _next = null; 186 _prev = null; 187 } 188 189 /// the time is active. 190 pragma(inline, true) final bool isActive() const nothrow { 191 return _manger !is null; 192 } 193 194 /// get the timer only run once. 195 pragma(inline, true) final @property oneShop() { 196 return _oneShop; 197 } 198 /// set the timer only run once. 199 pragma(inline) final @property oneShop(bool one) { 200 _oneShop = one; 201 } 202 203 private: 204 WheelTimer _next = null; 205 WheelTimer _prev = null; 206 TimerWheel _manger = null; 207 bool _oneShop = false; 208 } 209 210 private: 211 212 /// the Header Timer in the wheel. 213 @safe class INullWheelTimer(Allocator) : IWheelTimer!Allocator { 214 override void onTimeOut() nothrow { 215 WheelTimer tm = _next; 216 while (tm) { 217 auto timer = tm._next; 218 if (tm.oneShop()) { 219 tm.stop(); 220 } 221 tm.onTimeOut(); 222 tm = timer; 223 } 224 } 225 226 ~this() { 227 this._manger = null; 228 WheelTimer tm = _next; 229 if (tm is null) 230 return; 231 tm._prev = null; 232 while (tm) { 233 tm._manger = null; 234 tm = tm._next; 235 } 236 } 237 238 } 239 240 unittest { 241 import std.datetime; 242 import std.stdio; 243 import std.conv; 244 import core.thread; 245 246 import yu.exception : yuCathException; 247 import yu.memory.gc; 248 249 @trusted class TestWheelTimer : WheelTimer { 250 this() { 251 time = Clock.currTime(); 252 } 253 254 override void onTimeOut() nothrow { 255 yuCathException(writeln("\nname is ", name, " \tcutterTime is : ", 256 Clock.currTime().toSimpleString(), "\t new time is : ", time.toSimpleString())); 257 } 258 259 string name; 260 private: 261 SysTime time; 262 } 263 264 writeln("start"); 265 TimingWheel wheel = new TimingWheel(5); 266 TestWheelTimer[] timers = new TestWheelTimer[5]; 267 foreach (tm; 0 .. 5) { 268 timers[tm] = new TestWheelTimer(); 269 } 270 271 int i = 0; 272 foreach (timer; timers) { 273 timer.name = to!string(i); 274 wheel.addNewTimer(timer); 275 writeln("i = ", i); 276 ++i; 277 278 } 279 writeln("prevWheel(5) the _now = ", wheel._now); 280 wheel.prevWheel(5); 281 Thread.sleep(2.seconds); 282 timers[4].stop(); 283 writeln("prevWheel(5) the _now = ", wheel._now); 284 wheel.prevWheel(5); 285 Thread.sleep(2.seconds); 286 writeln("prevWheel(3) the _now = ", wheel._now); 287 wheel.prevWheel(3); 288 assert(wheel._now == 3); 289 timers[2].rest(); 290 timers[4].rest(); 291 writeln("rest prevWheel(2) the _now = ", wheel._now); 292 wheel.prevWheel(2); 293 assert(wheel._now == 0); 294 295 foreach (u; 0 .. 20) { 296 Thread.sleep(1.seconds); 297 writeln("prevWheel() the _now = ", wheel._now); 298 wheel.prevWheel(); 299 } 300 writeln("free timer..."); 301 gcFree(wheel); 302 foreach (timer; timers) { 303 gcFree(timer); 304 } 305 }