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