[glib] Add Linux timerfd_create() backend for g_date_time_source_new()



commit 5763c631473539746646697e6a775f6eacaa08e2
Author: Colin Walters <walters verbum org>
Date:   Wed Aug 17 13:09:27 2011 -0400

    Add Linux timerfd_create() backend for g_date_time_source_new()
    
    This makes the source efficient on Linux.
    
    Tested on Fedora 15 x86_64 + updates, kernel-2.6.40-4.fc15.x86_64
    Also tested fallback code for unsupported flag TFD_TIMER_CANCEL_ON_SET
    on kernel 2.6.38.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=655129

 configure.ac     |   17 +++++++++++
 glib/gdatetime.c |   79 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 94 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index ffd2a67..b51b06f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2665,6 +2665,23 @@ if test x"$glib_cv_eventfd" = x"yes"; then
 fi
 AM_CONDITIONAL(HAVE_EVENTFD, [test "$glib_cv_eventfd" = "yes"])
 
+AC_CACHE_CHECK(for timerfd_create(2) system call,
+    glib_cv_timerfd,AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+#include <sys/timerfd.h>
+#include <unistd.h>
+],[
+int
+main (void)
+{
+  timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC);
+  return 0;
+}
+])],glib_cv_timerfd=yes,glib_cv_timerfd=no))
+if test x"$glib_cv_timerfd" = x"yes"; then
+  AC_DEFINE(HAVE_TIMERFD, 1, [we have the timerfd_create(2) system call])
+fi
+AM_CONDITIONAL(HAVE_TIMERFD, [test "$glib_cv_timerfd" = "yes"])
+
 dnl ****************************************
 dnl *** GLib POLL* compatibility defines ***
 dnl ****************************************
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
index d0a8f73..7885823 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -63,6 +63,7 @@
 #include "gatomic.h"
 #include "gfileutils.h"
 #include "ghash.h"
+#include "giochannel.h"
 #include "gmain.h"
 #include "gmappedfile.h"
 #include "gstrfuncs.h"
@@ -75,6 +76,9 @@
 #ifndef G_OS_WIN32
 #include <sys/time.h>
 #include <time.h>
+#ifdef HAVE_TIMERFD
+#include <sys/timerfd.h>
+#endif
 #endif /* !G_OS_WIN32 */
 
 /**
@@ -2596,6 +2600,8 @@ struct _GDateTimeSource
   gint64      wakeup_expiration;
 
   gboolean    cancel_on_set;
+
+  GPollFD     pollfd;
 };
 
 static inline void
@@ -2632,7 +2638,17 @@ g_datetime_source_prepare (GSource *source,
 			  gint    *timeout)
 {
   GDateTimeSource *datetime_source = (GDateTimeSource*)source;
-  gint64 monotonic_now = g_source_get_time (source);
+  gint64 monotonic_now;
+
+#ifdef HAVE_TIMERFD
+  if (datetime_source->pollfd.fd != -1)
+    {
+      *timeout = -1;
+      return FALSE;
+    }
+#endif
+
+  monotonic_now = g_source_get_time (source);
 
   if (monotonic_now < datetime_source->wakeup_expiration)
     {
@@ -2652,6 +2668,11 @@ g_datetime_source_check (GSource  *source)
 {
   GDateTimeSource *datetime_source = (GDateTimeSource*)source;
 
+#ifdef HAVE_TIMERFD
+  if (datetime_source->pollfd.fd != -1)
+    return datetime_source->pollfd.revents != 0;
+#endif
+
   if (g_datetime_source_is_expired (datetime_source))
     return TRUE;
 
@@ -2681,6 +2702,11 @@ g_datetime_source_dispatch (GSource    *source,
 static void
 g_datetime_source_finalize (GSource *source)
 {
+#ifdef HAVE_TIMERFD
+  GDateTimeSource *datetime_source = (GDateTimeSource*)source;
+  if (datetime_source->pollfd.fd != -1)
+    close (datetime_source->pollfd.fd);
+#endif
 }
 
 static GSourceFuncs g_datetime_source_funcs = {
@@ -2690,6 +2716,45 @@ static GSourceFuncs g_datetime_source_funcs = {
   g_datetime_source_finalize
 };
 
+#ifdef HAVE_TIMERFD
+static gboolean
+g_datetime_source_init_timerfd (GDateTimeSource *datetime_source,
+				gint64           unix_seconds)
+{
+  struct itimerspec its;
+  int settime_flags;
+
+  datetime_source->pollfd.fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC);
+  if (datetime_source->pollfd.fd == -1)
+    return FALSE;
+
+  memset (&its, 0, sizeof (its));
+  its.it_value.tv_sec = (time_t) unix_seconds;
+
+  /* http://article.gmane.org/gmane.linux.kernel/1132138 */
+#ifndef TFD_TIMER_CANCEL_ON_SET
+#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+#endif
+
+  settime_flags = TFD_TIMER_ABSTIME;
+  if (datetime_source->cancel_on_set)
+    settime_flags |= TFD_TIMER_CANCEL_ON_SET;
+
+  if (timerfd_settime (datetime_source->pollfd.fd, settime_flags, &its, NULL) < 0)
+    {
+      close (datetime_source->pollfd.fd);
+      datetime_source->pollfd.fd = -1;
+      return FALSE;
+    }
+
+  datetime_source->pollfd.events = G_IO_IN;
+
+  g_source_add_poll ((GSource*) datetime_source, &datetime_source->pollfd);
+
+  return TRUE;
+}
+#endif
+
 /**
  * g_date_time_source_new:
  * @datetime: Time to await
@@ -2733,11 +2798,21 @@ g_date_time_source_new (GDateTime  *datetime,
 			gboolean    cancel_on_set)
 {
   GDateTimeSource *datetime_source;
+  gint64 unix_seconds;
+
+  unix_seconds = g_date_time_to_unix (datetime);
 
   datetime_source = (GDateTimeSource*) g_source_new (&g_datetime_source_funcs, sizeof (GDateTimeSource));
 
   datetime_source->cancel_on_set = cancel_on_set;
-  datetime_source->real_expiration = g_date_time_to_unix (datetime) * 1000000;
+
+#ifdef HAVE_TIMERFD
+  if (g_datetime_source_init_timerfd (datetime_source, unix_seconds))
+    return (GSource*)datetime_source;
+  /* Fall through to non-timerfd code */
+#endif
+
+  datetime_source->real_expiration = unix_seconds * 1000000;
   g_datetime_source_reschedule (datetime_source, g_get_monotonic_time ());
 
   return (GSource*)datetime_source;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]