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.util.timer;
13 
14 import kiss.core;
15 import kiss.net.core;
16 import kiss.event;
17 import kiss.event.timer;
18 
19 import core.time;
20 import std.datetime;
21 import kiss.logger;
22 
23 alias  KissTimer = Timer; 
24 
25 /**
26 */
27 class Timer : AbstractTimer
28 {
29 
30     this(Selector loop)
31     {
32         super(loop);
33         this.interval = 1000;
34     }
35 
36     this(Selector loop, size_t interval)
37     {
38         super(loop);
39         this.interval = interval;
40     }
41 
42     this(Selector loop, Duration duration)
43     {
44         super(loop);
45         this.interval = duration;
46     }
47 
48 protected:
49 
50     override void onRead()
51     {
52         bool canRead = true;
53         while (canRead && _isRegistered)
54         {
55             canRead = readTimer((Object obj) {
56                 BaseTypeObject!uint tm = cast(BaseTypeObject!uint) obj;
57                 if (tm is null)
58                     return;
59                 while (tm.data > 0)
60                 {
61                     if (ticked !is null)
62                         ticked(this);
63                     tm.data--;
64                 }
65             });
66             if (this.isError)
67             {
68                 canRead = false;
69                 this.close();
70                 error("the Timer Read is error: ", this.erroString);
71             }
72         }
73     }
74 
75 }
76 
77 // dfmt off
78 version (Windows) : 
79 
80 // dfmt on
81 
82 import std.datetime;
83 import std.exception;
84 import std.process;
85 
86 import kiss.logger;
87 import core.sys.windows.windows;
88 import core.thread;
89 import core.time;
90 
91 /**
92 */
93 abstract class AbstractNativeTimer : ITimer
94 {
95     protected bool _isActive = false;
96     protected size_t _interval = 1000;
97 
98     /// Timer tick handler
99     TickedEventHandler ticked;
100 
101     this()
102     {
103         this(1000);
104     }
105 
106     this(size_t interval)
107     {
108         this.interval = interval;
109     }
110 
111     this(Duration duration)
112     {
113         this.interval = duration;
114     }
115 
116     /// 
117     @property bool isActive()
118     {
119         return _isActive;
120     }
121 
122     /// in ms
123     @property size_t interval()
124     {
125         return _interval;
126     }
127 
128     /// ditto
129     @property ITimer interval(size_t v)
130     {
131         _interval = v;
132         return this;
133     }
134 
135     /// ditto
136     @property ITimer interval(Duration duration)
137     {
138         _interval = cast(size_t) duration.total!("msecs");
139         return this;
140     }
141 
142     /// The handler will be handled in another thread.
143     ITimer onTick(TickedEventHandler handler)
144     {
145         this.ticked = handler;
146         return this;
147     }
148 
149     /// immediately: true to call first event immediately
150     /// once: true to call timed event only once
151     abstract void start(bool immediately = false, bool once = false);
152     void start(uint interval)
153     {
154         this.interval = interval;
155         start();
156     }
157 
158     abstract void stop();
159 
160     abstract void reset(bool immediately = false, bool once = false);
161 
162     void reset(size_t interval)
163     {
164         this.interval = interval;
165         reset();
166     }
167 
168     void reset(Duration duration)
169     {
170         this.interval = duration;
171         reset();
172     }
173 
174     protected void onTick()
175     {
176         // trace("tick thread id: ", getTid());
177         if (ticked !is null)
178             ticked(this);
179     }
180 }
181 
182 alias NativeTimerBase = AbstractNativeTimer;
183 
184 /**
185 * See_also:
186 *	https://www.codeproject.com/articles/146617/simple-c-timer-wrapper
187 *	https://msdn.microsoft.com/en-us/library/ms687003(v=vs.85)
188 */
189 class KissNativeTimer : AbstractNativeTimer
190 {
191     protected HANDLE _handle = null;
192 
193     this()
194     {
195         super(1000);
196     }
197 
198     this(size_t interval)
199     {
200         super(interval);
201     }
202 
203     this(Duration duration)
204     {
205         super(duration);
206     }
207 
208     /// immediately: true to call first event immediately
209     /// once: true to call timed event only once
210     override void start(bool immediately = false, bool once = false)
211     {
212         version(KissDebugMode) trace("main thread id: ", thisThreadID());
213         if (_isActive)
214             return;
215         BOOL r = CreateTimerQueueTimer(&_handle, null, &timerProc,
216                 cast(PVOID) this, immediately ? 0 : cast(int) interval, once ? 0
217                 : cast(int) interval, WT_EXECUTEINTIMERTHREAD);
218         assert(r != 0);
219         _isActive = true;
220     }
221 
222     override void stop()
223     {
224         if (_isActive)
225         {
226             DeleteTimerQueueTimer(null, _handle, null);
227             _isActive = false;
228         }
229     }
230 
231     override void reset(bool immediately = false, bool once = false)
232     {
233         if (_isActive)
234         {
235             assert(ChangeTimerQueueTimer(null, _handle, immediately ? 0
236                     : cast(int) interval, once ? 0 : cast(int) interval) != 0);
237         }
238     }
239 
240     /// https://msdn.microsoft.com/en-us/library/ms687066(v=vs.85)
241     extern (Windows) static private void timerProc(PVOID param, bool timerCalled)
242     {
243         version(KissDebugMode) trace("handler thread id: ", thisThreadID());
244         AbstractNativeTimer timer = cast(AbstractNativeTimer)(param);
245         assert(timer !is null);
246         timer.onTick();
247     }
248 }