Skip to content

Commit

Permalink
MYSQL-15: Implement server-side statement timeout support
Browse files Browse the repository at this point in the history
Introduce an implementation of per-thread timers. The API can be used
to create one-shot timers that use a monotonically increasing clock
as the timing base. A timer expires in a given amount of time (in
milliseconds) from when the timer is armed. Upon timer expiration, a
given callback function is invoked in a separate thread. Timers can
also be cancelled (disarmed) and provide an indication of the state
(signaled or non-signaled) of the timer at the time of cancellation.

The timer API is currently only implemented on Linux and Mac OS X.
  • Loading branch information
Davi Arnaut committed Feb 29, 2012
1 parent 7a39b2b commit 7d0453f
Show file tree
Hide file tree
Showing 9 changed files with 860 additions and 3 deletions.
4 changes: 4 additions & 0 deletions config.h.cmake
Expand Up @@ -308,6 +308,10 @@
#cmakedefine STRUCT_DIRENT_HAS_D_NAMLEN 1
#cmakedefine SPRINTF_RETURNS_INT 1

#cmakedefine HAVE_POSIX_TIMERS 1
#cmakedefine HAVE_KQUEUE_TIMERS 1
#cmakedefine HAVE_MY_TIMER 1

#define USE_MB 1
#define USE_MB_IDENT 1

Expand Down
17 changes: 17 additions & 0 deletions configure.cmake
Expand Up @@ -147,6 +147,7 @@ IF(UNIX)
IF(NOT LIBRT)
MY_SEARCH_LIBS(clock_gettime rt LIBRT)
ENDIF()
MY_SEARCH_LIBS(timer_create rt LIBRT)
FIND_PACKAGE(Threads)

SET(CMAKE_REQUIRED_LIBRARIES
Expand Down Expand Up @@ -454,6 +455,9 @@ CHECK_FUNCTION_EXISTS (valloc HAVE_VALLOC)
CHECK_FUNCTION_EXISTS (memalign HAVE_MEMALIGN)
CHECK_FUNCTION_EXISTS (chown HAVE_CHOWN)
CHECK_FUNCTION_EXISTS (nl_langinfo HAVE_NL_LANGINFO)
CHECK_FUNCTION_EXISTS (timer_create HAVE_TIMER_CREATE)
CHECK_FUNCTION_EXISTS (timer_settime HAVE_TIMER_SETTIME)
CHECK_FUNCTION_EXISTS (kqueue HAVE_KQUEUE)

#--------------------------------------------------------------------
# Support for WL#2373 (Use cycle counter for timing)
Expand Down Expand Up @@ -495,6 +499,8 @@ CHECK_SYMBOL_EXISTS(FIONREAD "sys/ioctl.h" FIONREAD_IN_SYS_IOCTL)
CHECK_SYMBOL_EXISTS(TIOCSTAT "sys/ioctl.h" TIOCSTAT_IN_SYS_IOCTL)
CHECK_SYMBOL_EXISTS(FIONREAD "sys/filio.h" FIONREAD_IN_SYS_FILIO)
CHECK_SYMBOL_EXISTS(gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY)
CHECK_SYMBOL_EXISTS(SIGEV_THREAD_ID "signal.h;time.h" HAVE_SIGEV_THREAD_ID)
CHECK_SYMBOL_EXISTS(EVFILT_TIMER "sys/types.h;sys/event.h;sys/time.h" HAVE_EVFILT_TIMER)

CHECK_SYMBOL_EXISTS(finite "math.h" HAVE_FINITE_IN_MATH_H)
IF(HAVE_FINITE_IN_MATH_H)
Expand All @@ -514,7 +520,18 @@ int main() {
return 0;
}" HAVE_ISINF)

# Check for the Linux-specific POSIX timers API.
IF(HAVE_TIMER_CREATE AND HAVE_TIMER_SETTIME AND HAVE_SIGEV_THREAD_ID)
SET(HAVE_POSIX_TIMERS 1 CACHE INTERNAL "Have POSIX timer-related functions")
ENDIF()

IF(HAVE_KQUEUE AND HAVE_EVFILT_TIMER)
SET(HAVE_KQUEUE_TIMERS 1 CACHE INTERNAL "Have kqueue timer-related filter")
ENDIF()

IF(HAVE_POSIX_TIMERS OR HAVE_KQUEUE_TIMERS)
SET(HAVE_MY_TIMER 1 CACHE INTERNAL "Have mysys timer-related functions")
ENDIF()

#
# Test for endianess
Expand Down
17 changes: 14 additions & 3 deletions include/my_global.h
Expand Up @@ -377,6 +377,19 @@ C_MODE_END
#include <crypt.h>
#endif

/**
Cast a member of a structure to the structure that contains it.
@param ptr Pointer to the member.
@param type Type of the structure that contains the member.
@param member Name of the member within the structure.
*/
#define my_container_of(ptr, type, member) \
({ \
const typeof(((type *)0)->member) *__mptr= (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); \
})

/*
A lot of our programs uses asserts, so better to always include it
This also fixes a problem when people uses DBUG_ASSERT without including
Expand Down Expand Up @@ -1418,9 +1431,7 @@ do { doubleget_union _tmp; \
#define NEED_EXPLICIT_SYNC_DIR 1
#endif

#if !defined(__cplusplus) && !defined(bool)
#define bool In_C_you_should_use_my_bool_instead()
#endif
#include <stdbool.h>

/* Provide __func__ macro definition for platforms that miss it. */
#if __STDC_VERSION__ < 199901L
Expand Down
69 changes: 69 additions & 0 deletions include/my_timer.h
@@ -0,0 +1,69 @@
/* Copyright (c) 2012, Twitter, Inc. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. */

#ifndef MY_TIMER_H
#define MY_TIMER_H

#include "my_global.h" /* C_MODE_START, C_MODE_END */
#include "my_config.h" /* HAVE_*_TIMERS */

/* POSIX timers API. */
#ifdef HAVE_POSIX_TIMERS
# include <time.h> /* timer_t */
typedef timer_t os_timer_t;
#elif HAVE_KQUEUE_TIMERS
# include <sys/types.h> /* uintptr_t */
typedef uintptr_t os_timer_t;
#endif

/* Whether timer API is implemented. */
#ifdef HAVE_MY_TIMER

C_MODE_START

typedef struct st_my_timer my_timer_t;

/** Non-copyable timer object. */
struct st_my_timer
{
/** Timer ID used to identify the timer in timer requests. */
os_timer_t id;

/** Timer expiration notification function. */
void (*notify_function)(my_timer_t *);
};

/* Initialize internal components. */
int my_timer_init_ext(void);

/* Release any resources acquired. */
void my_timer_deinit(void);

/* Create a timer object. */
int my_timer_create(my_timer_t *timer);

/* Set the time (in milliseconds) until the next expiration of the timer. */
int my_timer_set(my_timer_t *timer, unsigned long time);

/* Reset the time until the next expiration of the timer. */
int my_timer_reset(my_timer_t *timer, bool *state);

/* Delete a timer object. */
void my_timer_delete(my_timer_t *timer);

C_MODE_END

#endif /* HAVE_MY_TIMER */
#endif /* MY_TIMER_H */
8 changes: 8 additions & 0 deletions mysys/CMakeLists.txt
Expand Up @@ -44,6 +44,14 @@ IF(HAVE_ALARM)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_alarm.c)
ENDIF()

IF(HAVE_POSIX_TIMERS)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} posix_timers.c)
ENDIF()

IF(HAVE_KQUEUE_TIMERS)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} kqueue_timers.c)
ENDIF()

IF(NOT HAVE_CXX_NEW)
# gcc as C++ compiler does not have new/delete
SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_new.cc)
Expand Down
219 changes: 219 additions & 0 deletions mysys/kqueue_timers.c
@@ -0,0 +1,219 @@
/* Copyright (c) 2012, Twitter, Inc. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. */

#include "my_timer.h" /* my_timer_t */
#include "my_pthread.h" /* my_thread_init, my_thread_end */

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <assert.h>
#include <errno.h>

static int kq_fd= -1;
static pthread_t thread;

/**
Timer expiration notification thread.
@param arg Unused.
*/

static void *
timer_notify_thread(void *arg __attribute__((unused)))
{
my_timer_t *timer;
struct kevent kev;

my_thread_init();

while (1)
{
if (kevent(kq_fd, NULL, 0, &kev, 1, NULL) < 0)
continue;

if (kev.filter == EVFILT_TIMER)
{
timer= kev.udata;
assert(timer->id == kev.ident);
timer->notify_function(timer);
}
else if (kev.filter == EVFILT_USER)
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)
{
struct kevent kev;

EV_SET(&kev, 0, EVFILT_USER, EV_ADD, 0, 0, 0);

if (kevent(kq_fd, &kev, 1, NULL, 0, NULL) < 0)
return -1;

return pthread_create(&thread, NULL, timer_notify_thread, NULL);
}


/**
Initialize internal components.
@return On success, 0.
On error, -1 is returned, and errno is set to indicate the error.
*/

int
my_timer_init_ext(void)
{
int rc;

/* Create a file descriptor for event notification. */
if ((kq_fd= kqueue()) < 0)
return -1;

/* Create a helper thread. */
if ((rc= start_helper_thread()))
close(kq_fd);

return rc;
}


/**
Release any resources that were allocated as part of initialization.
*/

void
my_timer_deinit(void)
{
struct kevent kev;

EV_SET(&kev, 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);

/* There's not much to do if triggering the event fails. */
if (kevent(kq_fd, &kev, 1, NULL, 0, NULL) > -1)
pthread_join(thread, NULL);

close(kq_fd);
}


/**
Create a timer object.
@param timer Timer object.
@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)
{
assert(kq_fd >= 0);

timer->id= (uintptr_t) timer;

return 0;
}


/**
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)
{
struct kevent kev;

EV_SET(&kev, timer->id, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, time, timer);

return kevent(kq_fd, &kev, 1, NULL, 0, NULL);
}


/**
Reset the time until the next expiration of 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_reset(my_timer_t *timer, bool *state)
{
int status;
struct kevent kev;

EV_SET(&kev, timer->id, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);

status= kevent(kq_fd, &kev, 1, NULL, 0, NULL);

/*
If the event was retrieved from the kqueue (at which point we
consider it to be signaled), the timer was automatically deleted.
*/
if (!status)
*state= 1;
else if (errno == ENOENT)
{
*state= 0;
status= 0;
}

return status;
}


/**
Delete a timer object.
@param timer Timer object.
*/

void
my_timer_delete(my_timer_t *timer)
{
struct kevent kev;

EV_SET(&kev, timer->id, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);

kevent(kq_fd, &kev, 1, NULL, 0, NULL);
}

0 comments on commit 7d0453f

Please sign in to comment.