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 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28
29 #include "my_global.h"
30 #include "my_thread.h" /* my_thread_init, my_thread_end */
31 #include "my_sys.h" /* my_message_local */
32 #include "my_timer.h" /* my_timer_t */
33
34 #include <string.h> /* memset */
35 #include <signal.h>
36
37 #if defined(HAVE_SIGEV_THREAD_ID)
38 #include <sys/syscall.h> /* SYS_gettid */
39
40 #ifndef sigev_notify_thread_id
41 #define sigev_notify_thread_id _sigev_un._tid
42 #endif
43
44 #define MY_TIMER_EVENT_SIGNO (SIGRTMIN)
45 #define MY_TIMER_KILL_SIGNO (SIGRTMIN+1)
46
47 /* Timer thread ID (TID). */
48 static pid_t timer_notify_thread_id;
49
50 #elif defined(HAVE_SIGEV_PORT)
51 #include <port.h>
52
53 int port_id= -1;
54
55 #endif
56
57 /* Timer thread object. */
58 static my_thread_handle timer_notify_thread;
59
60 #if defined(HAVE_SIGEV_THREAD_ID)
61 /**
62 Timer expiration notification thread.
63
64 @param arg Barrier object.
65 */
66
67 static void *
timer_notify_thread_func(void * arg)68 timer_notify_thread_func(void *arg)
69 {
70 sigset_t set;
71 siginfo_t info;
72 my_timer_t *timer;
73 pthread_barrier_t *barrier= arg;
74
75 my_thread_init();
76
77 sigemptyset(&set);
78 sigaddset(&set, MY_TIMER_EVENT_SIGNO);
79 sigaddset(&set, MY_TIMER_KILL_SIGNO);
80
81 /* Get the thread ID of the current thread. */
82 timer_notify_thread_id= (pid_t) syscall(SYS_gettid);
83
84 /* Wake up parent thread, timer_notify_thread_id is available. */
85 pthread_barrier_wait(barrier);
86
87 while (1)
88 {
89 if (sigwaitinfo(&set, &info) < 0)
90 continue;
91
92 if (info.si_signo == MY_TIMER_EVENT_SIGNO)
93 {
94 timer= (my_timer_t*)info.si_value.sival_ptr;
95 timer->notify_function(timer);
96 }
97 else if (info.si_signo == MY_TIMER_KILL_SIGNO)
98 break;
99 }
100
101 my_thread_end();
102
103 return NULL;
104 }
105
106
107 /**
108 Create a helper thread to dispatch timer expiration notifications.
109
110 @return On success, 0. On error, -1 is returned.
111 */
112
113 static int
start_helper_thread(void)114 start_helper_thread(void)
115 {
116 pthread_barrier_t barrier;
117
118 if (pthread_barrier_init(&barrier, NULL, 2))
119 {
120 my_message_local(ERROR_LEVEL,
121 "Failed to initialize pthread barrier. errno=%d", errno);
122 return -1;
123 }
124
125 if (mysql_thread_create(key_thread_timer_notifier, &timer_notify_thread,
126 NULL, timer_notify_thread_func, &barrier))
127 {
128 my_message_local(ERROR_LEVEL,
129 "Failed to create timer notify thread (errno= %d).",
130 errno);
131 pthread_barrier_destroy(&barrier);
132 return -1;
133 }
134
135 pthread_barrier_wait(&barrier);
136 pthread_barrier_destroy(&barrier);
137
138 return 0;
139 }
140
141
142 /**
143 Initialize internal components.
144
145 @return On success, 0.
146 On error, -1 is returned, and errno is set to indicate the error.
147 */
148
149 int
my_timer_initialize(void)150 my_timer_initialize(void)
151 {
152 int rc;
153 sigset_t set, old_set;
154
155 if (sigfillset(&set))
156 {
157 my_message_local(ERROR_LEVEL,
158 "Failed to intialize signal set (errno=%d).", errno);
159 return -1;
160 }
161
162 /*
163 Temporarily block all signals. New thread will inherit signal
164 mask of the current thread.
165 */
166 if (pthread_sigmask(SIG_BLOCK, &set, &old_set))
167 return -1;
168
169 /* Create a helper thread. */
170 rc= start_helper_thread();
171
172 /* Restore the signal mask. */
173 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
174
175 return rc;
176 }
177
178
179 /**
180 Release any resources that were allocated as part of initialization.
181 */
182
183 void
my_timer_deinitialize(void)184 my_timer_deinitialize(void)
185 {
186 /* Kill helper thread. */
187 pthread_kill(timer_notify_thread.thread, MY_TIMER_KILL_SIGNO);
188
189 /* Wait for helper thread termination. */
190 my_thread_join(&timer_notify_thread, NULL);
191 }
192
193
194 /**
195 Create a timer object.
196
197 @param timer Location where the timer ID is returned.
198
199 @return On success, 0.
200 On error, -1 is returned, and errno is set to indicate the error.
201 */
202
203 int
my_timer_create(my_timer_t * timer)204 my_timer_create(my_timer_t *timer)
205 {
206 struct sigevent sigev;
207
208 memset(&sigev, 0, sizeof(sigev));
209
210 sigev.sigev_value.sival_ptr= timer;
211 sigev.sigev_signo= MY_TIMER_EVENT_SIGNO;
212 sigev.sigev_notify= SIGEV_SIGNAL | SIGEV_THREAD_ID;
213 sigev.sigev_notify_thread_id= timer_notify_thread_id;
214
215 return timer_create(CLOCK_MONOTONIC, &sigev, &timer->id);
216 }
217 #elif defined(HAVE_SIGEV_PORT)
218 /**
219 Timer expiration notification thread.
220
221 @param arg Barrier object.
222 */
223
224 static void *
timer_notify_thread_func(void * arg MY_ATTRIBUTE ((unused)))225 timer_notify_thread_func(void *arg MY_ATTRIBUTE((unused)))
226 {
227 port_event_t port_event;
228 my_timer_t *timer;
229
230 my_thread_init();
231
232 while (1)
233 {
234 if (port_get(port_id, &port_event, NULL))
235 break;
236
237 if (port_event.portev_source != PORT_SOURCE_TIMER)
238 continue;
239
240 timer= (my_timer_t*)port_event.portev_user;
241 timer->notify_function(timer);
242 }
243
244 my_thread_end();
245
246 return NULL;
247 }
248
249
250 /**
251 Create a helper thread to dispatch timer expiration notifications.
252
253 @return On success, 0. On error, -1 is returned.
254 */
255
256 static int
start_helper_thread(void)257 start_helper_thread(void)
258 {
259 if (mysql_thread_create(key_thread_timer_notifier, &timer_notify_thread,
260 NULL, timer_notify_thread_func, NULL))
261 {
262 my_message_local(ERROR_LEVEL,
263 "Failed to create timer notify thread (errno= %d).",
264 errno);
265 return -1;
266 }
267
268 return 0;
269 }
270
271
272 /**
273 Initialize internal components.
274
275 @return On success, 0.
276 On error, -1 is returned, and errno is set to indicate the error.
277 */
278
279 int
my_timer_initialize(void)280 my_timer_initialize(void)
281 {
282 int rc;
283
284 if ((port_id= port_create()) < 0)
285 {
286 my_message_local(ERROR_LEVEL, "Failed to create port (errno= %d).", errno);
287 return -1;
288 }
289
290 /* Create a helper thread. */
291 rc= start_helper_thread();
292
293 return rc;
294 }
295
296
297 /**
298 Release any resources that were allocated as part of initialization.
299 */
300
301 void
my_timer_deinitialize(void)302 my_timer_deinitialize(void)
303 {
304 assert(port_id >= 0);
305
306 // close port
307 close(port_id);
308
309 /* Wait for helper thread termination. */
310 my_thread_join(&timer_notify_thread, NULL);
311 }
312
313
314 /**
315 Create a timer object.
316
317 @param timer Location where the timer ID is returned.
318
319 @return On success, 0.
320 On error, -1 is returned, and errno is set to indicate the error.
321 */
322
323 int
my_timer_create(my_timer_t * timer)324 my_timer_create(my_timer_t *timer)
325 {
326 struct sigevent sigev;
327 port_notify_t port_notify;
328
329 port_notify.portnfy_port= port_id;
330 port_notify.portnfy_user= timer;
331
332 memset(&sigev, 0, sizeof(sigev));
333 sigev.sigev_value.sival_ptr= &port_notify;
334 sigev.sigev_notify= SIGEV_PORT;
335
336 return timer_create(CLOCK_REALTIME, &sigev, &timer->id);
337 }
338 #endif
339
340
341 /**
342 Set the time until the next expiration of the timer.
343
344 @param timer Timer object.
345 @param time Amount of time (in milliseconds) before the timer expires.
346
347 @return On success, 0.
348 On error, -1 is returned, and errno is set to indicate the error.
349 */
350
351 int
my_timer_set(my_timer_t * timer,unsigned long time)352 my_timer_set(my_timer_t *timer, unsigned long time)
353 {
354 const struct itimerspec spec= {
355 .it_interval= {.tv_sec= 0, .tv_nsec= 0},
356 .it_value= {.tv_sec= time / 1000,
357 .tv_nsec= (time % 1000) * 1000000}
358 };
359
360 return timer_settime(timer->id, 0, &spec, NULL);
361 }
362
363
364 /**
365 Cancel the timer.
366
367 @param timer Timer object.
368 @param state The state of the timer at the time of cancellation, either
369 signaled (false) or nonsignaled (true).
370
371 @return On success, 0.
372 On error, -1 is returned, and errno is set to indicate the error.
373 */
374
375 int
my_timer_cancel(my_timer_t * timer,int * state)376 my_timer_cancel(my_timer_t *timer, int *state)
377 {
378 int status;
379 struct itimerspec old_spec;
380
381 /* A zeroed initial expiration value disarms the timer. */
382 const struct timespec zero_time= { .tv_sec= 0, .tv_nsec= 0 };
383 const struct itimerspec zero_spec= { .it_value= zero_time };
384
385 /*
386 timer_settime returns the amount of time before the timer
387 would have expired or zero if the timer was disarmed.
388 */
389 if (! (status= timer_settime(timer->id, 0, &zero_spec, &old_spec)))
390 *state= (old_spec.it_value.tv_sec || old_spec.it_value.tv_nsec);
391
392 return status;
393 }
394
395
396 /**
397 Delete a timer object.
398
399 @param timer Timer object.
400 */
401
402 void
my_timer_delete(my_timer_t * timer)403 my_timer_delete(my_timer_t *timer)
404 {
405 timer_delete(timer->id);
406 }
407
408