From 354bb40e75d94466e91fe6960523612c9d17ccfb Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 2 Nov 2017 23:11:29 +0300 Subject: Add implementation --- mysql/mysys/posix_timers.c | 395 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 mysql/mysys/posix_timers.c (limited to 'mysql/mysys/posix_timers.c') diff --git a/mysql/mysys/posix_timers.c b/mysql/mysys/posix_timers.c new file mode 100644 index 0000000..2215a66 --- /dev/null +++ b/mysql/mysys/posix_timers.c @@ -0,0 +1,395 @@ +/* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include "my_global.h" +#include "my_thread.h" /* my_thread_init, my_thread_end */ +#include "my_sys.h" /* my_message_local */ +#include "my_timer.h" /* my_timer_t */ + +#include /* memset */ +#include + +#if defined(HAVE_SIGEV_THREAD_ID) +#include /* SYS_gettid */ + +#ifndef sigev_notify_thread_id +#define sigev_notify_thread_id _sigev_un._tid +#endif + +#define MY_TIMER_EVENT_SIGNO (SIGRTMIN) +#define MY_TIMER_KILL_SIGNO (SIGRTMIN+1) + +/* Timer thread ID (TID). */ +static pid_t timer_notify_thread_id; + +#elif defined(HAVE_SIGEV_PORT) +#include + +int port_id= -1; + +#endif + +/* Timer thread object. */ +static my_thread_handle timer_notify_thread; + +#if defined(HAVE_SIGEV_THREAD_ID) +/** + Timer expiration notification thread. + + @param arg Barrier object. +*/ + +static void * +timer_notify_thread_func(void *arg) +{ + sigset_t set; + siginfo_t info; + my_timer_t *timer; + pthread_barrier_t *barrier= arg; + + my_thread_init(); + + sigemptyset(&set); + sigaddset(&set, MY_TIMER_EVENT_SIGNO); + sigaddset(&set, MY_TIMER_KILL_SIGNO); + + /* Get the thread ID of the current thread. */ + timer_notify_thread_id= (pid_t) syscall(SYS_gettid); + + /* Wake up parent thread, timer_notify_thread_id is available. */ + pthread_barrier_wait(barrier); + + while (1) + { + if (sigwaitinfo(&set, &info) < 0) + continue; + + if (info.si_signo == MY_TIMER_EVENT_SIGNO) + { + timer= (my_timer_t*)info.si_value.sival_ptr; + timer->notify_function(timer); + } + else if (info.si_signo == MY_TIMER_KILL_SIGNO) + break; + } + + my_thread_end(); + + return NULL; +} + + +/** + Create a helper thread to dispatch timer expiration notifications. + + @return On success, 0. On error, -1 is returned. +*/ + +static int +start_helper_thread(void) +{ + pthread_barrier_t barrier; + + if (pthread_barrier_init(&barrier, NULL, 2)) + { + my_message_local(ERROR_LEVEL, + "Failed to initialize pthread barrier. errno=%d", errno); + return -1; + } + + if (mysql_thread_create(key_thread_timer_notifier, &timer_notify_thread, + NULL, timer_notify_thread_func, &barrier)) + { + my_message_local(ERROR_LEVEL, + "Failed to create timer notify thread (errno= %d).", + errno); + pthread_barrier_destroy(&barrier); + return -1; + } + + pthread_barrier_wait(&barrier); + pthread_barrier_destroy(&barrier); + + return 0; +} + + +/** + Initialize internal components. + + @return On success, 0. + On error, -1 is returned, and errno is set to indicate the error. +*/ + +int +my_timer_initialize(void) +{ + int rc; + sigset_t set, old_set; + + if (sigfillset(&set)) + { + my_message_local(ERROR_LEVEL, + "Failed to intialize signal set (errno=%d).", errno); + return -1; + } + + /* + Temporarily block all signals. New thread will inherit signal + mask of the current thread. + */ + if (pthread_sigmask(SIG_BLOCK, &set, &old_set)) + return -1; + + /* Create a helper thread. */ + rc= start_helper_thread(); + + /* Restore the signal mask. */ + pthread_sigmask(SIG_SETMASK, &old_set, NULL); + + return rc; +} + + +/** + Release any resources that were allocated as part of initialization. +*/ + +void +my_timer_deinitialize(void) +{ + /* Kill helper thread. */ + pthread_kill(timer_notify_thread.thread, MY_TIMER_KILL_SIGNO); + + /* Wait for helper thread termination. */ + my_thread_join(&timer_notify_thread, NULL); +} + + +/** + Create a timer object. + + @param timer Location where the timer ID is returned. + + @return On success, 0. + On error, -1 is returned, and errno is set to indicate the error. +*/ + +int +my_timer_create(my_timer_t *timer) +{ + struct sigevent sigev; + + memset(&sigev, 0, sizeof(sigev)); + + sigev.sigev_value.sival_ptr= timer; + sigev.sigev_signo= MY_TIMER_EVENT_SIGNO; + sigev.sigev_notify= SIGEV_SIGNAL | SIGEV_THREAD_ID; + sigev.sigev_notify_thread_id= timer_notify_thread_id; + + return timer_create(CLOCK_MONOTONIC, &sigev, &timer->id); +} +#elif defined(HAVE_SIGEV_PORT) +/** + Timer expiration notification thread. + + @param arg Barrier object. +*/ + +static void * +timer_notify_thread_func(void *arg MY_ATTRIBUTE((unused))) +{ + port_event_t port_event; + my_timer_t *timer; + + my_thread_init(); + + while (1) + { + if (port_get(port_id, &port_event, NULL)) + break; + + if (port_event.portev_source != PORT_SOURCE_TIMER) + continue; + + timer= (my_timer_t*)port_event.portev_user; + timer->notify_function(timer); + } + + my_thread_end(); + + return NULL; +} + + +/** + Create a helper thread to dispatch timer expiration notifications. + + @return On success, 0. On error, -1 is returned. +*/ + +static int +start_helper_thread(void) +{ + if (mysql_thread_create(key_thread_timer_notifier, &timer_notify_thread, + NULL, timer_notify_thread_func, NULL)) + { + my_message_local(ERROR_LEVEL, + "Failed to create timer notify thread (errno= %d).", + errno); + return -1; + } + + return 0; +} + + +/** + Initialize internal components. + + @return On success, 0. + On error, -1 is returned, and errno is set to indicate the error. +*/ + +int +my_timer_initialize(void) +{ + int rc; + + if ((port_id= port_create()) < 0) + { + my_message_local(ERROR_LEVEL, "Failed to create port (errno= %d).", errno); + return -1; + } + + /* Create a helper thread. */ + rc= start_helper_thread(); + + return rc; +} + + +/** + Release any resources that were allocated as part of initialization. +*/ + +void +my_timer_deinitialize(void) +{ + DBUG_ASSERT(port_id >= 0); + + // close port + close(port_id); + + /* Wait for helper thread termination. */ + my_thread_join(&timer_notify_thread, NULL); +} + + +/** + Create a timer object. + + @param timer Location where the timer ID is returned. + + @return On success, 0. + On error, -1 is returned, and errno is set to indicate the error. +*/ + +int +my_timer_create(my_timer_t *timer) +{ + struct sigevent sigev; + port_notify_t port_notify; + + port_notify.portnfy_port= port_id; + port_notify.portnfy_user= timer; + + memset(&sigev, 0, sizeof(sigev)); + sigev.sigev_value.sival_ptr= &port_notify; + sigev.sigev_notify= SIGEV_PORT; + + return timer_create(CLOCK_REALTIME, &sigev, &timer->id); +} +#endif + + +/** + Set the time until the next expiration of the timer. + + @param timer Timer object. + @param time Amount of time (in milliseconds) before the timer expires. + + @return On success, 0. + On error, -1 is returned, and errno is set to indicate the error. +*/ + +int +my_timer_set(my_timer_t *timer, unsigned long time) +{ + const struct itimerspec spec= { + .it_interval= {.tv_sec= 0, .tv_nsec= 0}, + .it_value= {.tv_sec= time / 1000, + .tv_nsec= (time % 1000) * 1000000} + }; + + return timer_settime(timer->id, 0, &spec, NULL); +} + + +/** + Cancel the timer. + + @param timer Timer object. + @param state The state of the timer at the time of cancellation, either + signaled (false) or nonsignaled (true). + + @return On success, 0. + On error, -1 is returned, and errno is set to indicate the error. +*/ + +int +my_timer_cancel(my_timer_t *timer, int *state) +{ + int status; + struct itimerspec old_spec; + + /* A zeroed initial expiration value disarms the timer. */ + const struct timespec zero_time= { .tv_sec= 0, .tv_nsec= 0 }; + const struct itimerspec zero_spec= { .it_value= zero_time }; + + /* + timer_settime returns the amount of time before the timer + would have expired or zero if the timer was disarmed. + */ + if (! (status= timer_settime(timer->id, 0, &zero_spec, &old_spec))) + *state= (old_spec.it_value.tv_sec || old_spec.it_value.tv_nsec); + + return status; +} + + +/** + Delete a timer object. + + @param timer Timer object. +*/ + +void +my_timer_delete(my_timer_t *timer) +{ + timer_delete(timer->id); +} + -- cgit v1.1