forked from grijjy/GrijjyFoundation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGrijjy.TimerQueue.Win.pas
207 lines (178 loc) · 4.17 KB
/
Grijjy.TimerQueue.Win.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
unit Grijjy.TimerQueue.Win;
{ Windows based timer queue }
{$I Grijjy.inc}
interface
uses
System.Classes,
System.SysUtils,
System.SyncObjs,
System.DateUtils,
System.Generics.Collections,
Winapi.Windows;
type
TgoTimer = class;
TOnTimer = procedure(const ASender: TObject) of object;
{ Timer object }
TgoTimer = class(TObject)
private
FHandle: THandle;
FInterval: Cardinal;
FOnTimer: TOnTimer;
public
constructor Create;
destructor Destroy; override;
public
{ Handle of the timer object }
property Handle: THandle read FHandle;
{ Timer interval in milliseconds }
property Interval: Cardinal read FInterval;
{ Timer callback event }
property OnTimer: TOnTimer read FOnTimer write FOnTimer;
end;
{ Timer queue instance }
TgoTimerQueue = class(TObject)
private
FHandle: THandle;
private
procedure _Release(const ATimer: TgoTimer);
procedure ReleaseAll;
public
constructor Create;
destructor Destroy; override;
public
{ Adds a new timer to the queue}
function Add(const AInterval: Cardinal; const AOnTimer: TOnTimer): THandle;
{ Release an existing timer }
procedure Release(const AHandle: THandle);
{ Change the internal rate of a timer }
function SetInterval(const AHandle: THandle; const AInterval: Cardinal): Boolean;
end;
implementation
var
_Timers: TDictionary<THandle, TgoTimer>;
_TimersLock: TCriticalSection;
{ TgoTimer }
constructor TgoTimer.Create;
begin
inherited;
FHandle := INVALID_HANDLE_VALUE;
FInterval := 0;
FOnTimer := nil;
end;
destructor TgoTimer.Destroy;
begin
inherited;
end;
{ TgoTimerQueue }
constructor TgoTimerQueue.Create;
begin
FHandle := CreateTimerQueue;
end;
destructor TgoTimerQueue.Destroy;
begin
ReleaseAll;
DeleteTimerQueueEx(FHandle, INVALID_HANDLE_VALUE);
FHandle := INVALID_HANDLE_VALUE;
end;
procedure WaitOrTimerCallback(Timer: TgoTimer; TimerOrWaitFired: ByteBool); stdcall;
begin
if Timer <> nil then
begin
_TimersLock.Enter;
try
if not _Timers.ContainsKey(Timer.Handle) then
Exit;
finally
_TimersLock.Leave;
end;
if TimerOrWaitFired then
if Assigned(Timer.OnTimer) then
Timer.OnTimer(Timer);
end;
end;
function TgoTimerQueue.Add(const AInterval: Cardinal; const AOnTimer: TOnTimer): THandle;
var
Timer: TgoTimer;
begin
Result := 0;
{ create a timer object }
Timer := TgoTimer.Create;
Timer.FInterval := AInterval;
Timer.FOnTimer := AOnTimer;
if CreateTimerQueueTimer(Timer.FHandle, FHandle, @WaitOrTimerCallback, Timer, 0, AInterval, 0) then
begin
_TimersLock.Enter;
try
_Timers.Add(Timer.Handle, Timer);
Result := Timer.Handle;
finally
_TimersLock.Leave;
end;
end
else
FreeAndNil(Timer);
end;
procedure TgoTimerQueue._Release(const ATimer: TgoTimer);
begin
ATimer.OnTimer := nil;
{ the DeleteTimerQueueTimer API will block until all the callbacks are completed }
if DeleteTimerQueueTimer(FHandle, ATimer.Handle, INVALID_HANDLE_VALUE) then
ATimer.Free;
end;
procedure TgoTimerQueue.Release(const AHandle: THandle);
var
Timer: TgoTimer;
begin
Timer := nil;
_TimersLock.Enter;
try
if _Timers.TryGetValue(AHandle, Timer) then
_Timers.Remove(AHandle);
finally
_TimersLock.Leave;
end;
if Timer <> nil then
_Release(Timer);
end;
procedure TgoTimerQueue.ReleaseAll;
var
Timer: TgoTimer;
begin
_TimersLock.Enter;
try
for Timer in _Timers.Values do
_Release(Timer);
_Timers.Clear;
finally
_TimersLock.Leave;
end;
end;
function TgoTimerQueue.SetInterval(const AHandle: THandle; const AInterval: Cardinal): Boolean;
var
Timer: TgoTimer;
begin
Result := False;
_TimersLock.Enter;
try
if _Timers.TryGetValue(AHandle, Timer) then
if ChangeTimerQueueTimer(FHandle, Timer.Handle, 0, AInterval) then
begin
Timer.FInterval := AInterval;
Result := True;
end;
finally
_TimersLock.Leave;
end;
end;
initialization
_Timers := TDictionary<THandle, TgoTimer>.Create;
_TimersLock := TCriticalSection.Create;
finalization
_TimersLock.Enter;
try
_Timers.Free;
finally
_TimersLock.Leave;
end;
_TimersLock.Free;
end.