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