From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:36816) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qva97-0007MS-4D for qemu-devel@nongnu.org; Mon, 22 Aug 2011 15:26:54 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Qva95-0006Z0-8D for qemu-devel@nongnu.org; Mon, 22 Aug 2011 15:26:53 -0400 Received: from mail-yx0-f173.google.com ([209.85.213.173]:48721) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qva95-0006Yw-4T for qemu-devel@nongnu.org; Mon, 22 Aug 2011 15:26:51 -0400 Received: by yxt3 with SMTP id 3so4433274yxt.4 for ; Mon, 22 Aug 2011 12:26:50 -0700 (PDT) Message-ID: <4E52AD78.2040408@codemonkey.ws> Date: Mon, 22 Aug 2011 14:26:48 -0500 From: Anthony Liguori MIME-Version: 1.0 References: <1314040874-9259-1-git-send-email-aliguori@us.ibm.com> In-Reply-To: <1314040874-9259-1-git-send-email-aliguori@us.ibm.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH 1/2] main: add high resolution GSource based timer List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Anthony Liguori Cc: Paolo Bonzini , qemu-devel@nongnu.org, Stefan Hajnoczi , Jan Kiszka On 08/22/2011 02:21 PM, Anthony Liguori wrote: > This was originally written by Paolo Bonzini. > > Signed-off-by: Anthony Liguori FYI, all of these glib integration patches are available at: http://repo.or.cz/w/qemu/aliguori.git/shortlog/refs/heads/glib-main But be forewarned, they may eat your lunch and steal your stapler. Regards, Anthony Liguori > --- > Makefile.objs | 1 + > configure | 4 +- > ghrtimer.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ghrtimer.h | 49 +++++++++ > 4 files changed, 386 insertions(+), 2 deletions(-) > create mode 100644 ghrtimer.c > create mode 100644 ghrtimer.h > > diff --git a/Makefile.objs b/Makefile.objs > index d1f3e5d..e4267ed 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -9,6 +9,7 @@ qobject-obj-y += qerror.o error.o > oslib-obj-y = osdep.o > oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o > oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o > +oslib-obj-y += ghrtimer.o > > ####################################################################### > # coroutines > diff --git a/configure b/configure > index 8bbd694..6339c45 100755 > --- a/configure > +++ b/configure > @@ -1843,8 +1843,8 @@ fi > ########################################## > # glib support probe > if $pkg_config --modversion gthread-2.0> /dev/null 2>&1 ; then > - glib_cflags=`$pkg_config --cflags gthread-2.0 2>/dev/null` > - glib_libs=`$pkg_config --libs gthread-2.0 2>/dev/null` > + glib_cflags=`$pkg_config --cflags gthread-2.0 gobject-2.0 2>/dev/null` > + glib_libs=`$pkg_config --libs gthread-2.0 gobject-2.0 2>/dev/null` > LIBS="$glib_libs $LIBS" > libs_qga="$glib_libs $libs_qga" > else > diff --git a/ghrtimer.c b/ghrtimer.c > new file mode 100644 > index 0000000..1e7e1c2 > --- /dev/null > +++ b/ghrtimer.c > @@ -0,0 +1,334 @@ > +/* > + * timerfd GSource wrapper > + * > + * Copyright IBM, Corp. 2011 > + * Copyright Red Hat, Inc. 2011 > + * > + * Authors: > + * Anthony Liguori > + * Paolo Bonzini > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. > + * See the COPYING.LIB file in the top-level directory. > + * > + */ > + > +#include "config-host.h" > +#include > +#include "ghrtimer.h" > +#include > +#include > +#include > + > +#ifdef CONFIG_TIMERFD > +#include > +#endif > + > +struct _GHRTimer { > + GSource source; > + gint64 deadline; > + GPollFD poll; > + char pending; > +}; > + > +#define MIN_TIMER_REARM_NS 250000 > + > +#if GLIB_MAJOR_VERSION == 2&& GLIB_MINOR_VERSION<= 26 > +static inline guint64 muldiv64(guint64 a, guint32 b, guint32 c) > +{ > + guint64 rl = (a& 0xffffffff) * (guint64)b; > + guint64 rh = (a>> 32) * (guint64)b + (rl>> 32); > + rl&= 0xffffffff; > + return ((rh / c)<< 32) | ((((rh % c)<< 32) + rl) / c); > +} > + > +gint64 > +g_get_monotonic_time_ns (void) > +{ > +#if defined(__linux__) || (defined(__FreeBSD__)&& __FreeBSD_version>= 500000) \ > + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ > + || defined(__OpenBSD__) > + struct timespec ts; > + clock_gettime(CLOCK_MONOTONIC,&ts); > + return ts.tv_sec * 1000000000LL + ts.tv_nsec; > + > +#elif defined _WIN32 > + LARGE_INTEGER ti; > + static LARGE_INTEGER freq; > + if (freq.QuadPart == 0) { > + QueryPerformanceFrequency(&freq); > + } > + QueryPerformanceCounter(&ti); > + return muldiv64(ti.QuadPart, 1000000000, freq.QuadPart); > + > +#else > +#ifdef CONFIG_TIMERFD > +#error configuration problem, timerfd uses CLOCK_MONOTONIC > +#endif > + GTimeVal tv; > + g_get_current_time (&tv); > + return ((guint64) tv.tv_sec) * 1000000000 + tv.tv_usec * 1000; > +#endif > +} > + > +gint64 > +g_source_get_time_ns (GSource *source) > +{ > + return g_get_monotonic_time_ns (); > +} > + > +#else > +gint64 > +g_get_monotonic_time_ns (void) > +{ > + return g_get_monotonic_time () * 1000; > +} > + > +gint64 > +g_source_get_time_ns (GSource *source) > +{ > + return g_source_get_time (source) * 1000; > +} > +#endif > + > +gboolean > +g_hrtimer_pending (GHRTimer *timer) > +{ > + return timer->pending; > +} > + > +void > +g_hrtimer_rearm (GHRTimer *timer, > + gint64 us) > +{ > + return g_hrtimer_rearm_ns (timer, us * 1000); > +} > + > +void > +g_hrtimer_rearm_ns (GHRTimer *timer, > + gint64 ns) > +{ > + gint64 time = g_get_monotonic_time_ns (); > + timer->deadline = ns; > + if (ns< time) { > + timer->pending = TRUE; > + return; > + } > + > + timer->pending = FALSE; > + if (ns == G_HRTIMER_QUIESCE) { > + return; > + } > + > + if (ns - time< MIN_TIMER_REARM_NS) { > + timer->deadline = ns = time + MIN_TIMER_REARM_NS; > + } > +#ifdef CONFIG_TIMERFD > + if (timer->poll.fd != -1) { > + struct itimerspec new = { > + .it_interval = { 0, 0 }, > + .it_value = { ns / 1000000000, ns % 1000000000 } > + }; > + timerfd_settime(timer->poll.fd, TFD_TIMER_ABSTIME,&new, NULL); > + } > +#endif > +} > + > +static gboolean > +g_hrtimer_prepare (GSource *source, > + gint *timeout) > +{ > + GHRTimer *timer = (GHRTimer *) source; > + > + if (timer->deadline == G_HRTIMER_QUIESCE) { > + g_assert (!timer->pending); > + *timeout = -1; > + return FALSE; > + } > + > + if (timer->poll.fd == -1) { > + gint64 timeout_ns = timer->deadline - g_get_monotonic_time_ns (); > + if (timeout_ns< 0) { > + *timeout = 0; > + timer->pending = TRUE; > + } else { > + *timeout = timeout_ns / 1000000; > + } > + } else { > + *timeout = -1; > +#ifndef CONFIG_TIMERFD > + abort (); > +#endif > + } > + return timer->pending; > +} > + > +static gboolean > +g_hrtimer_check (GSource *source) > +{ > + GHRTimer *timer = (GHRTimer *) source; > + > + if (timer->deadline == G_HRTIMER_QUIESCE) { > + g_assert (!timer->pending); > + return FALSE; > + } > + > + if (timer->poll.fd == -1) { > + timer->pending |= (timer->deadline<= g_source_get_time_ns (source)); > + } else { > + long long overrun; > + timer->pending |= (timer->poll.revents& G_IO_IN) != 0; > + if (timer->pending) { > + if (read (timer->poll.fd, (char *)&overrun, sizeof (overrun))) { > + /* do nothing */ > + } > + } > +#ifndef CONFIG_TIMERFD > + abort (); > +#endif > + } > + > + return timer->pending; > +} > + > +static gboolean > +g_hrtimer_dispatch (GSource *source, > + GSourceFunc callback, > + gpointer user_data) > +{ > + GHRTimer *timer = (GHRTimer *) source; > + > + if (!callback) { > + g_warning ("Timer source dispatched without callback\n" > + "You must call g_source_set_callback()."); > + return TRUE; > + } > + > + timer->pending = FALSE; > + timer->deadline = G_HRTIMER_QUIESCE; > + if (user_data == NULL) > + user_data = timer; > + callback (user_data); > + return TRUE; > +} > + > +static void > +g_hrtimer_finalize (GSource *source) > +{ > + GHRTimer *timer = (GHRTimer *) source; > + > + if (timer->poll.fd != -1) { > + close (timer->poll.fd); > +#ifndef CONFIG_TIMERFD > + abort (); > +#endif > + } > +} > + > +static void > +g_hrtimer_closure_callback (gpointer data) > +{ > + GClosure *closure = data; > + g_closure_invoke (closure, NULL, 0, NULL, NULL); > +} > + > +static GSourceFuncs hrtimer_source_funcs = { > + g_hrtimer_prepare, > + g_hrtimer_check, > + g_hrtimer_dispatch, > + g_hrtimer_finalize, > + (GSourceFunc) g_hrtimer_closure_callback, > + (gpointer) g_cclosure_marshal_VOID__VOID > +}; > + > +GHRTimer * > +g_hrtimer_new (void) > +{ > + GHRTimer *timer; > + > + timer = (GHRTimer *) g_source_new (&hrtimer_source_funcs, > + sizeof (GHRTimer)); > + > +#ifdef CONFIG_TIMERFD > + timer->poll.fd = timerfd_create (CLOCK_MONOTONIC, 0); > + if (timer->poll.fd != -1) { > + fcntl(timer->poll.fd, F_SETFD, fcntl (timer->poll.fd, F_GETFD) | FD_CLOEXEC); > + fcntl(timer->poll.fd, F_SETFL, fcntl (timer->poll.fd, F_GETFL) | O_NONBLOCK); > + timer->poll.events = G_IO_IN; > + g_source_add_poll (&timer->source,&timer->poll); > + } > +#else > + timer->poll.fd = -1; > +#endif > + timer->deadline = G_HRTIMER_QUIESCE; > + return timer; > +} > +guint > +g_hrtimer_add (GHRTimer **timer, > + GSourceFunc func, > + gpointer user_data) > +{ > + return g_hrtimer_add_full (G_PRIORITY_DEFAULT, timer, func, user_data, NULL); > +} > + > +guint > +g_hrtimer_add_full (gint priority, > + GHRTimer **timer, > + GSourceFunc func, > + gpointer user_data, > + GDestroyNotify notify) > +{ > + GHRTimer *hrtimer; > + guint id; > + > + hrtimer = g_hrtimer_new (); > + if (priority != G_PRIORITY_DEFAULT) > + g_source_set_priority (&hrtimer->source, priority); > + > + g_source_set_callback (&hrtimer->source, (GSourceFunc) func, > + user_data, notify); > + id = g_source_attach (&hrtimer->source, NULL); > + > + *timer = hrtimer; > + return id; > +} > + > +#ifdef MAIN > +#include > + > +static int i = 3; > +static GMainLoop *loop; > + > +void > +rearm_timer (GHRTimer *timer) > +{ > + printf ("."); > + fflush (stdout); > + g_hrtimer_rearm_ns (timer, g_get_monotonic_time_ns () + 1000000000); > +} > + > +void > +hrtimer_callback (gpointer user_data) > +{ > + GHRTimer *timer = user_data; > + > + if (--i == 0) { > + printf ("\n"); > + fflush (stdout); > + g_main_loop_quit (loop); > + } else { > + rearm_timer (timer); > + } > +} > + > +int main() > +{ > + GHRTimer *timer; > + loop = g_main_loop_new (NULL, FALSE); > + g_hrtimer_add (&timer, (GSourceFunc) hrtimer_callback, NULL); > + rearm_timer (timer); > + g_main_loop_run (loop); > + g_source_unref ((GSource *) timer); > +} > +#endif > + > diff --git a/ghrtimer.h b/ghrtimer.h > new file mode 100644 > index 0000000..2cf6961 > --- /dev/null > +++ b/ghrtimer.h > @@ -0,0 +1,49 @@ > +/* > + * timerfd GSource wrapper > + * > + * Copyright IBM, Corp. 2011 > + * Copyright Red Hat, Inc. 2011 > + * > + * Authors: > + * Anthony Liguori > + * Paolo Bonzini > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later > + * See the COPYING.LIB file in the top-level directory. > + * > + */ > + > +#ifndef G_HRTIMER_H > +#define G_HRTIMER_H 1 > + > +#include > + > +#define G_HRTIMER_QUIESCE ((gint64) 0x7FFFFFFFFFFFFFFF) > + > +typedef struct _GHRTimer GHRTimer; > + > +gint64 g_get_monotonic_time_ns (void); > + > +gint64 g_source_get_time_ns (GSource *source); > + > +GHRTimer *g_hrtimer_new (void); > + > +gboolean g_hrtimer_pending (GHRTimer *timer); > + > +void g_hrtimer_rearm (GHRTimer *timer, > + gint64 usec); > + > +void g_hrtimer_rearm_ns (GHRTimer *timer, > + gint64 nsec); > + > +guint g_hrtimer_add (GHRTimer **timer, > + GSourceFunc callback, > + gpointer user_data); > + > +guint g_hrtimer_add_full (int priority, > + GHRTimer **timer, > + GSourceFunc callback, > + gpointer user_data, > + GDestroyNotify notify); > + > +#endif