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