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

Hu Tao hutao at cn.fujitsu.com
Wed Dec 15 06:32:45 UTC 2010


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 at 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 at 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 at 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 at 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 at 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




More information about the libvir-list mailing list