1 /*
2  * Geario - A cross-platform abstraction library with asynchronous I/O.
3  *
4  * Copyright (C) 2021-2022 Kerisy.com
5  *
6  * Website: https://www.kerisy.com
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module geario.util.Timer;
13 
14 import geario.event;
15 import geario.event.timer;
16 import geario.logging;
17 import geario.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                 log.error("the Timer Read is Error: ", this.ErrorMessage);
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 (GEAR_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 (GEAR_DEBUG)
206             Trace("handler thread id: ", thisThreadID());
207         AbstractNativeTimer timer = cast(AbstractNativeTimer)(param);
208         assert(timer !is null);
209         timer.OnTick();
210     }
211 }