* 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@xxxxxxxxxxxxxx> --- 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@xxxxxxxxxxxxxx> + */ + +#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@xxxxxxxxxxxxxx> + */ + +#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@xxxxxxxxxxxxxx> + */ + +#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@xxxxxxxxxxxxxx> + */ + +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list