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

Wen Congyang wency at cn.fujitsu.com
Wed Dec 15 02:34:25 UTC 2010


* 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;
+    }
+
+    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 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)
+{
+    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 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




More information about the libvir-list mailing list