aboutsummaryrefslogtreecommitdiff
path: root/mysql/mysys/posix_timers.c
diff options
context:
space:
mode:
Diffstat (limited to 'mysql/mysys/posix_timers.c')
-rw-r--r--mysql/mysys/posix_timers.c395
1 files changed, 395 insertions, 0 deletions
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 <string.h> /* memset */
+#include <signal.h>
+
+#if defined(HAVE_SIGEV_THREAD_ID)
+#include <sys/syscall.h> /* 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 <port.h>
+
+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);
+}
+