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