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@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; > + } 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@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) 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@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 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list