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 }