1 /* Copyright (c) 2014, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23
24 #if defined (_WIN32)
25 #include "my_global.h"
26 #include "my_thread.h" /* my_thread_init, my_thread_end */
27 #include "my_sys.h" /* my_message_local */
28 #include "my_timer.h" /* my_timer_t */
29 #include <windows.h> /* Timer Queue and IO completion port functions */
30
31
32 enum enum_timer_state { TIMER_SET=FALSE, TIMER_EXPIRED=TRUE };
33
34 // Timer notifier thread id.
35 static my_thread_handle timer_notify_thread;
36
37 // IO completion port handle
38 HANDLE io_compl_port= 0;
39
40 // Timer queue handle
41 HANDLE timer_queue= 0;
42
43 /**
44 Callback function registered to execute on timer expiration.
45
46 @param timer_data timer data passed to function.
47 @param timer_or_wait_fired flag to represent timer fired or signalled.
48
49 @remark this function is executed in timer owner thread when timer
50 expires.
51 */
52 static void CALLBACK
timer_callback_function(PVOID timer_data,BOOLEAN timer_or_wait_fired MY_ATTRIBUTE ((unused)))53 timer_callback_function(PVOID timer_data, BOOLEAN timer_or_wait_fired MY_ATTRIBUTE((unused)))
54 {
55 my_timer_t *timer= (my_timer_t *)timer_data;
56 assert(timer != NULL);
57 timer->id.timer_state= TIMER_EXPIRED;
58 PostQueuedCompletionStatus(io_compl_port, 0, (ULONG_PTR)timer, 0);
59 }
60
61
62 /**
63 Timer expiration notification thread.
64
65 @param arg Unused.
66 */
67 static void*
timer_notify_thread_func(void * arg MY_ATTRIBUTE ((unused)))68 timer_notify_thread_func(void *arg MY_ATTRIBUTE((unused)))
69 {
70 DWORD bytes_transferred;
71 ULONG_PTR compl_key;
72 LPOVERLAPPED overlapped;
73 my_timer_t *timer;
74
75 my_thread_init();
76
77 while(1)
78 {
79 // Get IO Completion status.
80 if (GetQueuedCompletionStatus(io_compl_port, &bytes_transferred, &compl_key,
81 &overlapped, INFINITE) == 0)
82 break;
83
84 timer= (my_timer_t*)compl_key;
85 timer->notify_function(timer);
86 }
87
88 my_thread_end();
89
90 return NULL;
91 }
92
93
94 /**
95 Delete a timer.
96
97 @param timer Timer Object.
98 @param state The state of the timer at the time of deletion, either
99 signaled (0) or nonsignaled (1).
100
101 @return 0 On Success
102 -1 On error.
103 */
104 static int
delete_timer(my_timer_t * timer,int * state)105 delete_timer(my_timer_t *timer, int *state)
106 {
107 int ret_val;
108 int retry_count= 3;
109
110 assert(timer != 0);
111 assert(timer_queue != 0);
112
113 if (state != NULL)
114 *state= 0;
115
116 if (timer->id.timer_handle)
117 {
118 do {
119 ret_val= DeleteTimerQueueTimer(timer_queue, timer->id.timer_handle, NULL);
120
121 if (ret_val != 0)
122 {
123 /**
124 From MSDN documentation of DeleteTimerQueueTimer:
125
126 ------------------------------------------------------------------
127
128 BOOL WINAPI DeleteTimerQueueTimer(
129 _In_opt_ HANDLE TimerQueue,
130 _In_ HANDLE Timer,
131 _In_opt_ HANDLE CompletionEvent
132 );
133
134 ...
135 If there are outstanding callback functions and CompletionEvent is
136 NULL, the function will fail and set the error code to
137 ERROR_IO_PENDING. This indicates that there are outstanding callback
138 functions. Those callbacks either will execute or are in the middle
139 of executing.
140 ...
141
142 ------------------------------------------------------------------
143
144 So we are here only in 2 cases,
145 1 When timer is *not* expired yet.
146 2 When timer is expired and callback function execution is
147 completed.
148
149 So here in case 1 timer.id->timer_state is TIMER_SET and
150 in case 2 timer.id->timer_state is TIMER_EXPIRED
151 (From MSDN documentation(pasted above), if timer callback function is
152 not yet executed or it is in the middle of execution then
153 DeleteTimerQueueTimer() fails. Hence when we are here, we are sure
154 that state is either TIMER_SET(case 1) or TIMER_EXPIRED(case 2))
155
156 Note:
157 timer.id->timer_state is set to TIMER_EXPIRED in
158 timer_callback_function(). This function is executed by the OS
159 thread on timer expiration.
160
161 On timer expiration, when callback function is in the middle of
162 execution or it is yet to be executed by OS thread the call to
163 DeleteTimerQueueTimer() fails with an error "ERROR_IO_PENDING".
164 In this case, timer.id->timer_state is not accessed in the current
165 code (please check else if block below).
166
167 Since timer.id->timer_state is not accessed in the current code
168 while it is getting modified in timer_callback_function,
169 no synchronization mechanism used.
170
171 Setting state to 1(non-signaled) if timer_state is not set to
172 "TIMER_EXPIRED"
173 */
174 if (timer->id.timer_state != TIMER_EXPIRED && state != NULL)
175 *state= 1;
176
177 timer->id.timer_handle= 0;
178 }
179 else if (GetLastError() == ERROR_IO_PENDING)
180 {
181 /**
182 Timer is expired and timer callback function execution is not
183 yet completed.
184
185 Note: timer->id.timer_state is modified in callback function.
186 Accessing timer->id.timer_state here might result in
187 race conditions.
188 Currently we are not accessing timer->id.timer_state
189 here so not using any synchronization mechanism.
190 */
191 timer->id.timer_handle= 0;
192 ret_val= 1;
193 }
194 else
195 {
196 /**
197 Timer deletion from queue failed and there are no outstanding
198 callback functions for this timer.
199 */
200 if (--retry_count == 0)
201 {
202 my_message_local(ERROR_LEVEL, "Failed to delete timer (errno= %d).",
203 errno);
204 return -1;
205 }
206 }
207 } while (ret_val == 0);
208 }
209
210 return 0;
211 }
212
213
214 /**
215 Initialize internal components.
216
217 @return 0 On success
218 -1 On error.
219 */
220 int
my_timer_initialize(void)221 my_timer_initialize(void)
222 {
223 // Create timer queue.
224 timer_queue= CreateTimerQueue();
225 if (!timer_queue)
226 {
227 my_message_local(ERROR_LEVEL, "Failed to create Timer Queue (errno= %d)",
228 errno);
229 goto err;
230 }
231
232 // Create IO completion port.
233 io_compl_port= CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
234 if (!io_compl_port)
235 {
236 my_message_local(ERROR_LEVEL,
237 "Failed to create IO completion port (errno= %d)",
238 errno);
239 goto err;
240 }
241
242 if (mysql_thread_create(key_thread_timer_notifier, &timer_notify_thread, 0,
243 timer_notify_thread_func, 0))
244 {
245 my_message_local(ERROR_LEVEL,
246 "Failed to create timer notify thread (errno= %d).",
247 errno);
248 goto err;
249 }
250
251 return 0;
252
253 err:
254 if (timer_queue)
255 {
256 DeleteTimerQueueEx(timer_queue, NULL);
257 timer_queue= 0;
258 }
259
260 if (io_compl_port)
261 {
262 CloseHandle(io_compl_port);
263 io_compl_port= 0;
264 }
265
266 return -1;
267 }
268
269
270 /**
271 Release any resources that were allocated as part of initialization.
272 */
273 void
my_timer_deinitialize()274 my_timer_deinitialize()
275 {
276 if (timer_queue)
277 {
278 DeleteTimerQueueEx(timer_queue, NULL);
279 timer_queue= 0;
280 }
281
282 if (io_compl_port)
283 {
284 CloseHandle(io_compl_port);
285 io_compl_port= 0;
286 }
287
288 my_thread_join(&timer_notify_thread, NULL);
289 }
290
291
292 /**
293 Create a timer object.
294
295 @param timer Timer object.
296
297 @return On success, 0.
298 */
299 int
my_timer_create(my_timer_t * timer)300 my_timer_create(my_timer_t *timer)
301 {
302 assert(timer_queue != 0);
303 timer->id.timer_handle= 0;
304 return 0;
305 }
306
307
308 /**
309 Set the time until the next expiration of the timer.
310
311 @param timer Timer object.
312 @param time Amount of time (in milliseconds) before the timer expires.
313
314 @return On success, 0.
315 On error, -1.
316 */
317 int
my_timer_set(my_timer_t * timer,unsigned long time)318 my_timer_set(my_timer_t *timer, unsigned long time)
319 {
320 assert(timer != NULL);
321 assert(timer_queue != 0);
322
323 /**
324 If timer set previously is expired then it will not be
325 removed from the timer queue. Removing it before creating
326 a new timer queue timer.
327 */
328 if (timer->id.timer_handle != 0)
329 my_timer_delete(timer);
330
331 timer->id.timer_state= TIMER_SET;
332
333 if (CreateTimerQueueTimer(&timer->id.timer_handle, timer_queue,
334 timer_callback_function, timer, time, 0,
335 WT_EXECUTEONLYONCE) == 0)
336 return -1;
337
338 return 0;
339 }
340
341
342 /**
343 Cancel the timer.
344
345 @param timer Timer object.
346 @param state The state of the timer at the time of cancellation, either
347 signaled (0) or nonsignaled (1).
348
349 @return On success, 0.
350 On error, -1.
351 */
352 int
my_timer_cancel(my_timer_t * timer,int * state)353 my_timer_cancel(my_timer_t *timer, int *state)
354 {
355 assert(state != NULL);
356
357 return delete_timer(timer, state);
358 }
359
360
361 /**
362 Delete a timer object.
363
364 @param timer Timer Object.
365 */
366 void
my_timer_delete(my_timer_t * timer)367 my_timer_delete(my_timer_t *timer)
368 {
369 delete_timer(timer, NULL);
370 }
371 #endif
372