[libvirt] [PATCH v2 1/2] timer impl

* src/util/timer.c src/util/timer.h src/util/timer_linux.c src/util/timer_win32.c: timer implementation * src/Makefile.am: build timer * src/libvirt_private.syms: Export public functions * src/libvirt.c: Initialize timer * configure.ac: check the functions in librt used by timer Signed-off-by: Wen Congyang <wency@cn.fujitsu.com> --- configure.ac | 4 + src/Makefile.am | 6 +- src/libvirt.c | 2 + src/libvirt_private.syms | 6 ++ src/util/timer.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/timer.h | 37 ++++++++++ src/util/timer_linux.c | 130 ++++++++++++++++++++++++++++++++++++ src/util/timer_win32.c | 128 +++++++++++++++++++++++++++++++++++ 8 files changed, 477 insertions(+), 2 deletions(-) create mode 100644 src/util/timer.c create mode 100644 src/util/timer.h create mode 100644 src/util/timer_linux.c create mode 100644 src/util/timer_win32.c diff --git a/configure.ac b/configure.ac index 64e76dc..49b77c5 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,10 @@ LIBS="$LIBS $LIB_PTHREAD" AC_CHECK_FUNCS([pthread_sigmask pthread_mutexattr_init]) LIBS=$old_libs +dnl Availability of rt functions +AC_CHECK_LIB([rt],[timer_gettime],[]) +AC_CHECK_FUNCS([clock_gettime timer_create timer_settime timer_delete]) + dnl Availability of various common headers (non-fatal if missing). AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/syslimits.h sys/un.h \ sys/poll.h syslog.h mntent.h net/ethernet.h linux/magic.h \ diff --git a/src/Makefile.am b/src/Makefile.am index 196d8af..13a66be 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,9 +79,11 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/xml.c util/xml.h \ util/virtaudit.c util/virtaudit.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.h \ + util/timer.c util/timer.h -EXTRA_DIST += util/threads-pthread.c util/threads-win32.c +EXTRA_DIST += util/threads-pthread.c util/threads-win32.c \ + util/timer_linux.c # Internal generic driver infrastructure NODE_INFO_SOURCES = nodeinfo.h nodeinfo.c diff --git a/src/libvirt.c b/src/libvirt.c index 4188b45..a5578c0 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -40,6 +40,7 @@ #include "util.h" #include "memory.h" #include "configmake.h" +#include "timer.h" #ifndef WITH_DRIVER_MODULES # ifdef WITH_TEST @@ -329,6 +330,7 @@ virInitialize(void) if (virThreadInitialize() < 0 || virErrorInitialize() < 0 || + virTimerInitialize() < 0 || virRandomInitialize(time(NULL) ^ getpid())) return -1; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0e3033d..928316a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -787,6 +787,12 @@ virThreadJoin; virThreadSelf; virThreadSelfID; +# timer.h +get_clock; +virNewTimer; +virFreeTimer; +virModTimer; +virDelTimer; # usb.h usbDeviceFileIterate; diff --git a/src/util/timer.c b/src/util/timer.c new file mode 100644 index 0000000..2a62a39 --- /dev/null +++ b/src/util/timer.c @@ -0,0 +1,166 @@ +/* + * timer.c: timer functions + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#include <config.h> + +#include <unistd.h> + +#include "timer.h" +#include "virterror_internal.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +struct virTimer { + uint64_t expire_time; + virTimerCB function; + void *data; + virTimerPtr next; +}; + +static virTimerPtr timer_list = NULL; +static void realarm_timer(void); +static void __realarm_timer(uint64_t); +static int timer_fd[2] = {-1, -1}; + +virTimerPtr virNewTimer(virTimerCB callback, void *data) +{ + virTimerPtr timer = NULL; + + if (VIR_ALLOC(timer) < 0) { + virReportOOMError(); + return NULL; + } + + timer->function = callback; + timer->data = data; + + return timer; +} + +void virFreeTimer(virTimerPtr timer) +{ + VIR_FREE(timer); +} + +void virDelTimer(virTimerPtr timer) +{ + virTimerPtr *pt, t; + + pt = &timer_list; + for (t = *pt; t; t = t->next) { + if (t == timer) { + *pt = t->next; + break; + } + pt = &t->next; + } + + realarm_timer(); +} + +void virModTimer(virTimerPtr timer, uint64_t expire_time) +{ + virTimerPtr *pt, t; + + virDelTimer(timer); + + pt = &timer_list; + for (t = *pt; t; t = t->next) { + if (t->expire_time > expire_time) + break; + pt = &t->next; + } + timer->expire_time = expire_time; + timer->next = *pt; + *pt = timer; + + realarm_timer(); +} + +static void realarm_timer() +{ + if (!timer_list) + return; + + __realarm_timer(timer_list->expire_time); +} + +static void timer_handler(void) +{ + uint64_t current_time = 0; + virTimerPtr *timer_list_head = &timer_list; + virTimerPtr timer = NULL; + + if (!timer_list) + return; + + current_time = get_clock(); + for (timer = timer_list; timer; timer = timer->next) { + if (current_time < timer->expire_time) + break; + + /* remove timer from the list before calling the callback */ + *timer_list_head = timer->next; + timer->next = NULL; + timer->function(timer->data); + + /* timer->function may take a while, so we should update cuttent_time */ + current_time = get_clock(); + } + + realarm_timer(); +} + +static void timer_loop(void *arg ATTRIBUTE_UNUSED) +{ + fd_set readfds; + int ret; + int readinfo = 0; + + while(1) { + FD_ZERO(&readfds); + FD_SET(timer_fd[0], &readfds); + + reselect: + ret = select(timer_fd[0] + 1, &readfds, NULL, NULL, NULL); + if (ret < 0) { + if (errno == EAGAIN || errno == EINTR) + goto reselect; + else + continue; + } + + ret = read(timer_fd[0], &readinfo, sizeof(int)); + timer_handler(); + } + + return; +} + +#if defined HAVE_TIMER_CREATE +#include "timer_linux.c" +#elif defined WIN32 +#include "timer_win32.c" +#else +# error "gnu timer is required" +#endif diff --git a/src/util/timer.h b/src/util/timer.h new file mode 100644 index 0000000..f521c1b --- /dev/null +++ b/src/util/timer.h @@ -0,0 +1,37 @@ +/* + * timer.h: structure and entry points for timer support + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#ifndef __VIR_TIMER_H__ +# define __VIR_TIMER_H__ + +#include <stdint.h> +typedef struct virTimer virTimer; +typedef virTimer *virTimerPtr; +typedef void (*virTimerCB)(void *); + +extern uint64_t get_clock(void); +extern virTimerPtr virNewTimer(virTimerCB, void *); +extern void virFreeTimer(virTimerPtr); +extern void virModTimer(virTimerPtr, uint64_t); +extern void virDelTimer(virTimerPtr); +extern int virTimerInitialize(void); +#endif /* __VIR_TIMER_H__ */ diff --git a/src/util/timer_linux.c b/src/util/timer_linux.c new file mode 100644 index 0000000..be483cf --- /dev/null +++ b/src/util/timer_linux.c @@ -0,0 +1,130 @@ +/* + * timer.c: timer functions + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <stddef.h> +#include <time.h> +#include <sys/time.h> +#include <signal.h> +#include <sys/select.h> +#include <pthread.h> + +#include "timer.h" +#include "threads.h" + +static int timer_initialized = 0; +static pthread_attr_t timer_thread_attr; +static timer_t timer_id; + +uint64_t get_clock(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} + +static void __realarm_timer(uint64_t next_time) +{ + uint64_t current_time = 0; + uint64_t timeout = 0; + struct itimerspec its; + + current_time = get_clock(); + if (current_time < next_time) { + timeout = next_time - current_time; + } else { + timeout = 1; + } + + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = timeout / 1000000000ULL; + its.it_value.tv_nsec = timeout % 1000000000ULL; + + timer_settime(timer_id, 0, &its, NULL); +} + +static void timer_nofity_thread(sigval_t sigev_value ATTRIBUTE_UNUSED) +{ + int write_info = 1; + int ret; + + ret = write(timer_fd[1], &write_info, sizeof(int)); +} + +int virTimerInitialize(void) +{ + struct sigevent timer_event; + struct itimerspec its; + struct virThread timer_thread; + struct timespec ts; + int ret; + + if (timer_initialized) + return 0; + + /* Make sure we can use system call clock_gettime(), so we can ignore the + * return value of clock_gettime() in the function get_clock(). + */ + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret != 0) + return -1; + + ret = pipe(timer_fd); + if (ret < 0) + return -1; + + pthread_attr_init(&timer_thread_attr); + pthread_attr_setdetachstate(&timer_thread_attr, 1); + + timer_event.sigev_notify = SIGEV_THREAD; + timer_event.sigev_notify_function = timer_nofity_thread; + timer_event.sigev_notify_attributes = &timer_thread_attr; + timer_event.sigev_value.sival_ptr = NULL; + ret = timer_create(CLOCK_MONOTONIC, &timer_event, &timer_id); + if (ret != 0) + return -1; + + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 0; + + /* Make sure we can use system call timer_settime(), so we can ignore the + * return value of timer_settime() in the function __realarm_timer(). + */ + ret = timer_settime(timer_id, 0, &its, NULL); + if (ret != 0) { + timer_delete(timer_id); + return -1; + } + + if (virThreadCreate(&timer_thread, 0, timer_loop, NULL) < 0) { + timer_delete(timer_id); + return -1; + } + + return 0; +} diff --git a/src/util/timer_win32.c b/src/util/timer_win32.c new file mode 100644 index 0000000..98248f7 --- /dev/null +++ b/src/util/timer_win32.c @@ -0,0 +1,128 @@ +/* + * timer.c: timer functions + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#include <windows.h> +#include <mmsystem.h> +#include <stdint.h> + +#include "threads.h" + +uint64_t clock_frequency = 0; +uint64_t ticks_per_second = 1000000000; +uint period = 0; +MMRESULT timer_id; + +uint64_t get_clock(void) +{ + LARGE_INTEGER time; + LARGE_INTEGER result; + uint64_t result_low, result_high; + + QueryPerformanceCounter(&time); + + /* calculate time * ticks_per_second / clock_frequency */ + result_high = (uint64_t)time.HighPart * ticks_per_second; + result_low = (uint64_t)time.LowPart * ticks_per_second; + result_high += result_low >> 32; + result_low = ((result_high % clock_frequency) << 32) + + result_low & 0xFFFFFFFF; + result.HighPart = result_high / clock_frequency; + result.LowPart = result_low / clock_frequency; + + return result.QuadPart; +} + +static void CALLBACK wakeup_timer(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, + DWORD_PTR dw2) +{ + int write_info = 1; + int ret; + + ret = write(timer_fd[1], &write_info, sizeof(int)); +} + +static void __realarm_timer(uint64_t next_time) +{ + uint64_t current_time = 0; + UINT timeout = 0; + UINT flags; + + current_time = get_clock(); + if (current_time + 100000ULL < next_time) { + timeout = (next_time - current_time) / 1000000ULL; + } else { + timeout = 1; + } + + flags = TIME_CALLBACK_FUNCTION | TIME_ONESHOT; + timer_id = timeSetEvent(timeout, period, wakeup_timer, + (DWORD_PTR)NULL, flags); +} + +int virTimerInitialize(void) +{ + LARGE_INTEGER frequency; + int ret; + TIMECAPS timecaps; + MMRESULT mmresult; + UINT flags; + struct virThread timer_thread; + + ret = pipe(timer_fd); + if (ret < 0) + return -1; + + ret = QueryPerformanceFrequency(&frequency); + if (ret != 0) { + return -1; + } + clock_frequency = frequency.QuadPart; + + memset(&timecaps, 0, sizeof(TIMECAPS)); + mmresult = timeGetDevCaps(&timecaps, sizeof(TIMECAPS)); + if (mmresult != MMSYSERR_NOERROR) + return -1; + period = timecaps.wPeriodMin; + + mmresult = timeBeginPeriod(period); + if (mmresult != TIMERR_NOERROR) + return -1; + + /* Make sure we can use timeSetEvent(), so we can ignore the return value + * of timeSetEvent() in the function __realarm_timer(). + */ + flags = TIME_CALLBACK_FUNCTION | TIME_ONESHOT; + mmresult = timeSetEvent(1000, period, wakeup_timer, (DWORD_PTR)NULL, flags); + if (!mmresult) { + timeEndPeriod(period); + return -1; + } + timeKillEvent(mmresult); + + if (virThreadCreate(&timer_thread, 0, timer_loop, NULL) < 0) { + timeEndPeriod(period); + return -1; + } + + return 0; +} -- 1.7.1

On Wed, Dec 15, 2010 at 10:34:25AM +0800, Wen Congyang wrote:
* src/util/timer.c src/util/timer.h src/util/timer_linux.c src/util/timer_win32.c: timer implementation * src/Makefile.am: build timer * src/libvirt_private.syms: Export public functions * src/libvirt.c: Initialize timer * configure.ac: check the functions in librt used by timer
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
--- configure.ac | 4 + src/Makefile.am | 6 +- src/libvirt.c | 2 + src/libvirt_private.syms | 6 ++ src/util/timer.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/timer.h | 37 ++++++++++ src/util/timer_linux.c | 130 ++++++++++++++++++++++++++++++++++++ src/util/timer_win32.c | 128 +++++++++++++++++++++++++++++++++++ 8 files changed, 477 insertions(+), 2 deletions(-) create mode 100644 src/util/timer.c create mode 100644 src/util/timer.h create mode 100644 src/util/timer_linux.c create mode 100644 src/util/timer_win32.c
diff --git a/configure.ac b/configure.ac index 64e76dc..49b77c5 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,10 @@ LIBS="$LIBS $LIB_PTHREAD" AC_CHECK_FUNCS([pthread_sigmask pthread_mutexattr_init]) LIBS=$old_libs
+dnl Availability of rt functions +AC_CHECK_LIB([rt],[timer_gettime],[]) +AC_CHECK_FUNCS([clock_gettime timer_create timer_settime timer_delete]) + dnl Availability of various common headers (non-fatal if missing). AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/syslimits.h sys/un.h \ sys/poll.h syslog.h mntent.h net/ethernet.h linux/magic.h \ diff --git a/src/Makefile.am b/src/Makefile.am index 196d8af..13a66be 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,9 +79,11 @@ UTIL_SOURCES = \ util/util.c util/util.h \ util/xml.c util/xml.h \ util/virtaudit.c util/virtaudit.h \ - util/virterror.c util/virterror_internal.h + util/virterror.c util/virterror_internal.h \ + util/timer.c util/timer.h
-EXTRA_DIST += util/threads-pthread.c util/threads-win32.c +EXTRA_DIST += util/threads-pthread.c util/threads-win32.c \ + util/timer_linux.c
# Internal generic driver infrastructure NODE_INFO_SOURCES = nodeinfo.h nodeinfo.c diff --git a/src/libvirt.c b/src/libvirt.c index 4188b45..a5578c0 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -40,6 +40,7 @@ #include "util.h" #include "memory.h" #include "configmake.h" +#include "timer.h"
#ifndef WITH_DRIVER_MODULES # ifdef WITH_TEST @@ -329,6 +330,7 @@ virInitialize(void)
if (virThreadInitialize() < 0 || virErrorInitialize() < 0 || + virTimerInitialize() < 0 || virRandomInitialize(time(NULL) ^ getpid())) return -1;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0e3033d..928316a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -787,6 +787,12 @@ virThreadJoin; virThreadSelf; virThreadSelfID;
+# timer.h +get_clock; +virNewTimer; +virFreeTimer; +virModTimer; +virDelTimer;
# usb.h usbDeviceFileIterate; diff --git a/src/util/timer.c b/src/util/timer.c new file mode 100644 index 0000000..2a62a39 --- /dev/null +++ b/src/util/timer.c @@ -0,0 +1,166 @@ +/* + * timer.c: timer functions + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#include <config.h> + +#include <unistd.h> + +#include "timer.h" +#include "virterror_internal.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +struct virTimer { + uint64_t expire_time; + virTimerCB function; + void *data; + virTimerPtr next; +}; + +static virTimerPtr timer_list = NULL; +static void realarm_timer(void); +static void __realarm_timer(uint64_t); +static int timer_fd[2] = {-1, -1}; + +virTimerPtr virNewTimer(virTimerCB callback, void *data) +{ + virTimerPtr timer = NULL; + + if (VIR_ALLOC(timer) < 0) { + virReportOOMError(); + return NULL; + } + + timer->function = callback; + timer->data = data; + + return timer; +} + +void virFreeTimer(virTimerPtr timer) +{ + VIR_FREE(timer); +} + +void virDelTimer(virTimerPtr timer) +{ + virTimerPtr *pt, t; + + pt = &timer_list; + for (t = *pt; t; t = t->next) { + if (t == timer) { + *pt = t->next; + break; + } + pt = &t->next; + }
Not thread-safe
+ + realarm_timer(); +} + +void virModTimer(virTimerPtr timer, uint64_t expire_time) +{ + virTimerPtr *pt, t; + + virDelTimer(timer); + + pt = &timer_list; + for (t = *pt; t; t = t->next) { + if (t->expire_time > expire_time) + break; + pt = &t->next; + } + timer->expire_time = expire_time; + timer->next = *pt; + *pt = timer;
Not thread-safe
+ + realarm_timer(); +} + +static void realarm_timer() +{ + if (!timer_list) + return; + + __realarm_timer(timer_list->expire_time); +} + +static void timer_handler(void) +{ + uint64_t current_time = 0; + virTimerPtr *timer_list_head = &timer_list; + virTimerPtr timer = NULL; + + if (!timer_list) + return; + + current_time = get_clock(); + for (timer = timer_list; timer; timer = timer->next) { + if (current_time < timer->expire_time) + break; + + /* remove timer from the list before calling the callback */ + *timer_list_head = timer->next; + timer->next = NULL; + timer->function(timer->data); + + /* timer->function may take a while, so we should update cuttent_time */
s/cuttent/current/
+ current_time = get_clock(); + } + + realarm_timer(); +} + +static void timer_loop(void *arg ATTRIBUTE_UNUSED) +{ + fd_set readfds; + int ret; + int readinfo = 0; + + while(1) { + FD_ZERO(&readfds); + FD_SET(timer_fd[0], &readfds); + + reselect: + ret = select(timer_fd[0] + 1, &readfds, NULL, NULL, NULL); + if (ret < 0) { + if (errno == EAGAIN || errno == EINTR) + goto reselect; + else + continue; + } + + ret = read(timer_fd[0], &readinfo, sizeof(int)); + timer_handler(); + } + + return; +} + +#if defined HAVE_TIMER_CREATE +#include "timer_linux.c" +#elif defined WIN32 +#include "timer_win32.c" +#else +# error "gnu timer is required" +#endif diff --git a/src/util/timer.h b/src/util/timer.h new file mode 100644 index 0000000..f521c1b --- /dev/null +++ b/src/util/timer.h @@ -0,0 +1,37 @@ +/* + * timer.h: structure and entry points for timer support + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#ifndef __VIR_TIMER_H__ +# define __VIR_TIMER_H__ + +#include <stdint.h> +typedef struct virTimer virTimer; +typedef virTimer *virTimerPtr; +typedef void (*virTimerCB)(void *); + +extern uint64_t get_clock(void); +extern virTimerPtr virNewTimer(virTimerCB, void *); +extern void virFreeTimer(virTimerPtr); +extern void virModTimer(virTimerPtr, uint64_t); +extern void virDelTimer(virTimerPtr); +extern int virTimerInitialize(void); +#endif /* __VIR_TIMER_H__ */ diff --git a/src/util/timer_linux.c b/src/util/timer_linux.c new file mode 100644 index 0000000..be483cf --- /dev/null +++ b/src/util/timer_linux.c @@ -0,0 +1,130 @@ +/* + * timer.c: timer functions + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <stddef.h> +#include <time.h> +#include <sys/time.h> +#include <signal.h> +#include <sys/select.h> +#include <pthread.h> + +#include "timer.h" +#include "threads.h" + +static int timer_initialized = 0; +static pthread_attr_t timer_thread_attr; +static timer_t timer_id; + +uint64_t get_clock(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} + +static void __realarm_timer(uint64_t next_time) +{ + uint64_t current_time = 0; + uint64_t timeout = 0; + struct itimerspec its; + + current_time = get_clock(); + if (current_time < next_time) { + timeout = next_time - current_time; + } else { + timeout = 1; + } + + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = timeout / 1000000000ULL; + its.it_value.tv_nsec = timeout % 1000000000ULL; + + timer_settime(timer_id, 0, &its, NULL); +} + +static void timer_nofity_thread(sigval_t sigev_value ATTRIBUTE_UNUSED)
s/nofity/notify/
+{ + int write_info = 1; + int ret; + + ret = write(timer_fd[1], &write_info, sizeof(int)); +} + +int virTimerInitialize(void) +{ + struct sigevent timer_event; + struct itimerspec its; + struct virThread timer_thread; + struct timespec ts; + int ret; + + if (timer_initialized) + return 0; + + /* Make sure we can use system call clock_gettime(), so we can ignore the + * return value of clock_gettime() in the function get_clock(). + */ + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret != 0) + return -1; + + ret = pipe(timer_fd); + if (ret < 0) + return -1; + + pthread_attr_init(&timer_thread_attr); + pthread_attr_setdetachstate(&timer_thread_attr, 1); + + timer_event.sigev_notify = SIGEV_THREAD; + timer_event.sigev_notify_function = timer_nofity_thread;
s/nofity/notify/
+ timer_event.sigev_notify_attributes = &timer_thread_attr; + timer_event.sigev_value.sival_ptr = NULL; + ret = timer_create(CLOCK_MONOTONIC, &timer_event, &timer_id); + if (ret != 0) + return -1; + + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 0; + + /* Make sure we can use system call timer_settime(), so we can ignore the + * return value of timer_settime() in the function __realarm_timer(). + */ + ret = timer_settime(timer_id, 0, &its, NULL); + if (ret != 0) { + timer_delete(timer_id); + return -1; + } + + if (virThreadCreate(&timer_thread, 0, timer_loop, NULL) < 0) { + timer_delete(timer_id); + return -1; + } + + return 0; +} diff --git a/src/util/timer_win32.c b/src/util/timer_win32.c new file mode 100644 index 0000000..98248f7 --- /dev/null +++ b/src/util/timer_win32.c @@ -0,0 +1,128 @@ +/* + * timer.c: timer functions + * + * Copyright (C) 2010 Fujitsu Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Wen Congyang <wency@cn.fujitsu.com> + */ + +#include <windows.h> +#include <mmsystem.h> +#include <stdint.h> + +#include "threads.h" + +uint64_t clock_frequency = 0; +uint64_t ticks_per_second = 1000000000; +uint period = 0; +MMRESULT timer_id; + +uint64_t get_clock(void) +{ + LARGE_INTEGER time; + LARGE_INTEGER result; + uint64_t result_low, result_high; + + QueryPerformanceCounter(&time); + + /* calculate time * ticks_per_second / clock_frequency */ + result_high = (uint64_t)time.HighPart * ticks_per_second; + result_low = (uint64_t)time.LowPart * ticks_per_second; + result_high += result_low >> 32; + result_low = ((result_high % clock_frequency) << 32) + + result_low & 0xFFFFFFFF; + result.HighPart = result_high / clock_frequency; + result.LowPart = result_low / clock_frequency; + + return result.QuadPart; +} + +static void CALLBACK wakeup_timer(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, + DWORD_PTR dw2) +{ + int write_info = 1; + int ret; + + ret = write(timer_fd[1], &write_info, sizeof(int)); +} + +static void __realarm_timer(uint64_t next_time) +{ + uint64_t current_time = 0; + UINT timeout = 0; + UINT flags; + + current_time = get_clock(); + if (current_time + 100000ULL < next_time) { + timeout = (next_time - current_time) / 1000000ULL; + } else { + timeout = 1; + } + + flags = TIME_CALLBACK_FUNCTION | TIME_ONESHOT; + timer_id = timeSetEvent(timeout, period, wakeup_timer, + (DWORD_PTR)NULL, flags); +} + +int virTimerInitialize(void) +{ + LARGE_INTEGER frequency; + int ret; + TIMECAPS timecaps; + MMRESULT mmresult; + UINT flags; + struct virThread timer_thread; + + ret = pipe(timer_fd); + if (ret < 0) + return -1; + + ret = QueryPerformanceFrequency(&frequency); + if (ret != 0) { + return -1; + } + clock_frequency = frequency.QuadPart; + + memset(&timecaps, 0, sizeof(TIMECAPS)); + mmresult = timeGetDevCaps(&timecaps, sizeof(TIMECAPS)); + if (mmresult != MMSYSERR_NOERROR) + return -1; + period = timecaps.wPeriodMin; + + mmresult = timeBeginPeriod(period); + if (mmresult != TIMERR_NOERROR) + return -1; + + /* Make sure we can use timeSetEvent(), so we can ignore the return value + * of timeSetEvent() in the function __realarm_timer(). + */ + flags = TIME_CALLBACK_FUNCTION | TIME_ONESHOT; + mmresult = timeSetEvent(1000, period, wakeup_timer, (DWORD_PTR)NULL, flags); + if (!mmresult) { + timeEndPeriod(period); + return -1; + } + timeKillEvent(mmresult); + + if (virThreadCreate(&timer_thread, 0, timer_loop, NULL) < 0) { + timeEndPeriod(period); + return -1; + } + + return 0; +} -- 1.7.1
However, I'd suggest providing APIs just wrapping around OS specific timer implementations. For linux they look like these: struct virTimer { timer_t timer; }; virTimerPtr virNewTimer(virTimerCB cb, void *opaque) { virTimerPtr timer; ... timer_event.sigev_notify = SIGEV_THREAD; timer_event.sigev_notify_function = cb; timer_event.sigev_notify_attributes = &timer_thread_attr; timer_event.sigev_value.sival_ptr = opaque; ret = timer_create(CLOCK_MONOTONIC, &timer_event, &timer->timer); ... } void virModTimer(virTimerPtr timer, uint64_t expire_time) { ... timer_settime(timer->timer, ...); } -- Thanks, Hu Tao

At 2010-12-15 14:32, Hu Tao Write:
On Wed, Dec 15, 2010 at 10:34:25AM +0800, Wen Congyang wrote:
* src/util/timer.c src/util/timer.h src/util/timer_linux.c src/util/timer_win32.c: timer implementation * src/Makefile.am: build timer * src/libvirt_private.syms: Export public functions * src/libvirt.c: Initialize timer * configure.ac: check the functions in librt used by timer
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
---
<snip>
However, I'd suggest providing APIs just wrapping around OS specific timer implementations. For linux they look like these:
struct virTimer { timer_t timer; };
virTimerPtr virNewTimer(virTimerCB cb, void *opaque) { virTimerPtr timer;
...
timer_event.sigev_notify = SIGEV_THREAD; timer_event.sigev_notify_function = cb; timer_event.sigev_notify_attributes = &timer_thread_attr; timer_event.sigev_value.sival_ptr = opaque;
ret = timer_create(CLOCK_MONOTONIC, &timer_event, &timer->timer);
... }
void virModTimer(virTimerPtr timer, uint64_t expire_time) { ...
timer_settime(timer->timer, ...); }
Good idea. Thanks.

On 12/14/2010 07:34 PM, Wen Congyang wrote: In addition to Hu's comments, and the fact that you are probably going to revise the exposed interface anyways, here's some additional points.
* src/util/timer.c src/util/timer.h src/util/timer_linux.c src/util/timer_win32.c: timer implementation * src/Makefile.am: build timer * src/libvirt_private.syms: Export public functions * src/libvirt.c: Initialize timer * configure.ac: check the functions in librt used by timer
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
-EXTRA_DIST += util/threads-pthread.c util/threads-win32.c +EXTRA_DIST += util/threads-pthread.c util/threads-win32.c \ + util/timer_linux.c
timer-win32.c? Also, I'd go with timer-linux.c, not timer_linux.c.
+# timer.h +get_clock;
Bad idea to pollute the namespace with get_clock; better would be something like virGetClock.
+virNewTimer; +virFreeTimer; +virModTimer; +virDelTimer;
# usb.h + +static virTimerPtr timer_list = NULL; +static void realarm_timer(void); +static void __realarm_timer(uint64_t);
It is dangerous to declare functions in the __ namespace, since that is reserved for libc and friends.
+uint64_t get_clock(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
You probably ought to check for overflow here. Dealing with raw nanoseconds is rather fine-grained; is it any better to go with micro or even milliseconds, or does libvirt really require something as precise as nanosecond timeouts? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 12/15/2010 08:20 AM, Eric Blake wrote:
On 12/14/2010 07:34 PM, Wen Congyang wrote:
In addition to Hu's comments, and the fact that you are probably going to revise the exposed interface anyways, here's some additional points.
One other point - how does this relate to the timeouts already implemented in places like daemon/event.c or src/util/event.c? Are those implementations already sufficient for your needs without having to write a new implementation? Or conversely, should your patch series be lengthened into rewriting those interfaces to take advantage of your new implementation in order to ease maintenance by focusing all timeout code into a single reusable interface? In other words, I'm still seeking a bit more justification for this patch. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Dec 15, 2010 at 08:24:44AM -0700, Eric Blake wrote:
On 12/15/2010 08:20 AM, Eric Blake wrote:
On 12/14/2010 07:34 PM, Wen Congyang wrote:
In addition to Hu's comments, and the fact that you are probably going to revise the exposed interface anyways, here's some additional points.
One other point - how does this relate to the timeouts already implemented in places like daemon/event.c or src/util/event.c? Are those implementations already sufficient for your needs without having to write a new implementation? Or conversely, should your patch series be lengthened into rewriting those interfaces to take advantage of your new implementation in order to ease maintenance by focusing all timeout code into a single reusable interface? In other words, I'm still seeking a bit more justification for this patch.
IMHO it should be sufficient for this new code to simply call the existing virEventAddTimeout() API, and run the event loop in the background thread. Daniel

At 12/15/2010 11:32 PM, Daniel P. Berrange Write:
On Wed, Dec 15, 2010 at 08:24:44AM -0700, Eric Blake wrote:
On 12/15/2010 08:20 AM, Eric Blake wrote:
On 12/14/2010 07:34 PM, Wen Congyang wrote:
In addition to Hu's comments, and the fact that you are probably going to revise the exposed interface anyways, here's some additional points.
One other point - how does this relate to the timeouts already implemented in places like daemon/event.c or src/util/event.c? Are those implementations already sufficient for your needs without having to write a new implementation? Or conversely, should your patch series be lengthened into rewriting those interfaces to take advantage of your new implementation in order to ease maintenance by focusing all timeout code into a single reusable interface? In other words, I'm still seeking a bit more justification for this patch.
IMHO it should be sufficient for this new code to simply call the existing virEventAddTimeout() API, and run the event loop in the background thread.
Hmm... I do not notice this API... Thanks for pointing this. I rough scan this API, it uses gettimeofday() to calculate the timeout. The time returns from gettimeofday can be changed by user, and it will cause some problems...
Daniel
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Thu, Dec 16, 2010 at 01:48:42PM +0800, Wen Congyang wrote:
At 12/15/2010 11:32 PM, Daniel P. Berrange Write:
On Wed, Dec 15, 2010 at 08:24:44AM -0700, Eric Blake wrote:
On 12/15/2010 08:20 AM, Eric Blake wrote:
On 12/14/2010 07:34 PM, Wen Congyang wrote:
In addition to Hu's comments, and the fact that you are probably going to revise the exposed interface anyways, here's some additional points.
One other point - how does this relate to the timeouts already implemented in places like daemon/event.c or src/util/event.c? Are those implementations already sufficient for your needs without having to write a new implementation? Or conversely, should your patch series be lengthened into rewriting those interfaces to take advantage of your new implementation in order to ease maintenance by focusing all timeout code into a single reusable interface? In other words, I'm still seeking a bit more justification for this patch.
IMHO it should be sufficient for this new code to simply call the existing virEventAddTimeout() API, and run the event loop in the background thread.
Hmm... I do not notice this API...
Thanks for pointing this.
I rough scan this API, it uses gettimeofday() to calculate the timeout. The time returns from gettimeofday can be changed by user, and it will cause some problems...
In theory yes, but in reality that's a non-issue. We've had this code for 4 years and never had any reports of such a problem. Daniel

On Wed, Dec 15, 2010 at 03:32:26PM +0000, Daniel P. Berrange wrote:
On Wed, Dec 15, 2010 at 08:24:44AM -0700, Eric Blake wrote:
On 12/15/2010 08:20 AM, Eric Blake wrote:
On 12/14/2010 07:34 PM, Wen Congyang wrote:
In addition to Hu's comments, and the fact that you are probably going to revise the exposed interface anyways, here's some additional points.
One other point - how does this relate to the timeouts already implemented in places like daemon/event.c or src/util/event.c? Are those implementations already sufficient for your needs without having to write a new implementation? Or conversely, should your patch series be lengthened into rewriting those interfaces to take advantage of your new implementation in order to ease maintenance by focusing all timeout code into a single reusable interface? In other words, I'm still seeking a bit more justification for this patch.
IMHO it should be sufficient for this new code to simply call the existing virEventAddTimeout() API, and run the event loop in the background thread.
But wouldn't it be a better idea to implement new timer APIs that focus only on timeout? I think it will make the code more modular and easier to maintain&improve. Althouth the virEventAddTimeout() API works well, but it has several disadvantages: - Not so easy to use: the user have to create a thread to run the event loop - All timers are not only serialized with each other but also with event handlers(if there are), if there are too many timers and event handlers it could cause performance impact. -- Thanks, Hu Tao

At 12/15/2010 11:20 PM, Eric Blake Write:
On 12/14/2010 07:34 PM, Wen Congyang wrote:
In addition to Hu's comments, and the fact that you are probably going to revise the exposed interface anyways, here's some additional points.
* src/util/timer.c src/util/timer.h src/util/timer_linux.c src/util/timer_win32.c: timer implementation * src/Makefile.am: build timer * src/libvirt_private.syms: Export public functions * src/libvirt.c: Initialize timer * configure.ac: check the functions in librt used by timer
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
-EXTRA_DIST += util/threads-pthread.c util/threads-win32.c +EXTRA_DIST += util/threads-pthread.c util/threads-win32.c \ + util/timer_linux.c
timer-win32.c? Also, I'd go with timer-linux.c, not timer_linux.c.
+# timer.h +get_clock;
Bad idea to pollute the namespace with get_clock; better would be something like virGetClock.
+virNewTimer; +virFreeTimer; +virModTimer; +virDelTimer;
# usb.h + +static virTimerPtr timer_list = NULL; +static void realarm_timer(void); +static void __realarm_timer(uint64_t);
It is dangerous to declare functions in the __ namespace, since that is reserved for libc and friends.
+uint64_t get_clock(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
You probably ought to check for overflow here. Dealing with raw nanoseconds is rather fine-grained; is it any better to go with micro or even milliseconds, or does libvirt really require something as precise as nanosecond timeouts?
Thanks for your comment. ts.tv_sec * 1000000000ULL will overflow only when the host OS runs 585 years... So it almost dose not overflow. I think we do not require nanosecond accuracy. millisecond accuracy is good enough.
participants (4)
-
Daniel P. Berrange
-
Eric Blake
-
Hu Tao
-
Wen Congyang