[gnome-desktop] GnomeWallClock: Add support for Linux timerfd()



commit 5f65d7d58188ffc75ffded71c741892352411a0c
Author: Colin Walters <walters verbum org>
Date:   Thu Sep 1 12:27:54 2011 -0400

    GnomeWallClock: Add support for Linux timerfd()
    
    See https://bugzilla.gnome.org/show_bug.cgi?id=655129 for more background
    information on timerfd.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=657955

 configure.ac                             |   16 ++
 libgnome-desktop/Makefile.am             |    2 +
 libgnome-desktop/gnome-datetime-source.c |  283 ++++++++++++++++++++++++++++++
 libgnome-desktop/gnome-datetime-source.h |   38 ++++
 libgnome-desktop/gnome-wall-clock.c      |   18 ++-
 5 files changed, 350 insertions(+), 7 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b691686..35a3244 100644
--- a/configure.ac
+++ b/configure.ac
@@ -163,6 +163,22 @@ dnl pkg-config dependency checks
 
 PKG_CHECK_MODULES(GNOME_DESKTOP, gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED gtk+-3.0 >= $GTK_REQUIRED glib-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED gsettings-desktop-schemas >= $GSETTINGS_DESKTOP_SCHEMAS_REQUIRED $RANDR_PACKAGE xext >= $XEXT_REQUIRED)
 
+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
+
 dnl gnome-doc-utils stuff
 
 GNOME_DOC_INIT
diff --git a/libgnome-desktop/Makefile.am b/libgnome-desktop/Makefile.am
index 1e9525f..0c2080e 100644
--- a/libgnome-desktop/Makefile.am
+++ b/libgnome-desktop/Makefile.am
@@ -29,6 +29,8 @@ introspection_sources = 		\
 
 libgnome_desktop_3_la_SOURCES = 	\
 	$(introspection_sources)	\
+	gnome-datetime-source.h		\
+	gnome-datetime-source.c		\
 	gnome-rr-private.h		\
 	edid.h				\
 	private.h
diff --git a/libgnome-desktop/gnome-datetime-source.c b/libgnome-desktop/gnome-datetime-source.c
new file mode 100644
index 0000000..05ec80a
--- /dev/null
+++ b/libgnome-desktop/gnome-datetime-source.c
@@ -0,0 +1,283 @@
+/* -*- mode: C; c-file-style: "linux"; indent-tabs-mode: t -*-
+ * gdatetime-source.c - copy&paste from https://bugzilla.gnome.org/show_bug.cgi?id=655129
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-datetime-source.h"
+
+#ifdef HAVE_TIMERFD
+#include <sys/timerfd.h>
+#include <unistd.h>
+#include <string.h>
+#endif
+
+typedef struct _GDateTimeSource GDateTimeSource;
+struct _GDateTimeSource
+{
+	GSource     source;
+  
+	gint64      real_expiration;
+	gint64      wakeup_expiration;
+
+	gboolean    cancel_on_set : 1;
+	gboolean    initially_expired : 1;
+
+	GPollFD     pollfd;
+};
+
+static inline void
+g_datetime_source_reschedule (GDateTimeSource *datetime_source,
+			      gint64                from_monotonic)
+{
+	datetime_source->wakeup_expiration = from_monotonic + G_TIME_SPAN_SECOND;
+}
+
+static gboolean
+g_datetime_source_is_expired (GDateTimeSource *datetime_source)
+{
+	gint64 real_now;
+
+	real_now = g_get_real_time ();
+
+	if (datetime_source->initially_expired)
+		return TRUE;
+  
+	if (datetime_source->real_expiration <= real_now)
+		return TRUE;
+
+	/* We can't really detect without system support when things change;
+	 * so just trigger every second.
+	 */
+	if (datetime_source->cancel_on_set)
+		return TRUE;
+
+	return FALSE;
+}
+
+/* In prepare, we're just checking the monotonic time against
+ * our projected wakeup.
+ */
+static gboolean
+g_datetime_source_prepare (GSource *source,
+			   gint    *timeout)
+{
+	GDateTimeSource *datetime_source = (GDateTimeSource*)source;
+	gint64 monotonic_now;
+
+#ifdef HAVE_TIMERFD
+	if (datetime_source->pollfd.fd != -1) {
+		*timeout = -1;
+		return datetime_source->initially_expired;  /* Should be TRUE at most one time, FALSE forever after */
+	}
+#endif
+
+	monotonic_now = g_source_get_time (source);
+
+	if (monotonic_now < datetime_source->wakeup_expiration) {
+		/* Round up to ensure that we don't try again too early */
+		*timeout = (datetime_source->wakeup_expiration - monotonic_now + 999) / 1000;
+		return FALSE;
+	}
+
+	*timeout = 0;
+	return g_datetime_source_is_expired (datetime_source);
+}
+
+/* In check, we're looking at the wall clock.
+ */
+static gboolean 
+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;
+
+	g_datetime_source_reschedule (datetime_source, g_source_get_time (source));
+  
+	return FALSE;
+}
+
+static gboolean
+g_datetime_source_dispatch (GSource    *source, 
+			    GSourceFunc callback,
+			    gpointer    user_data)
+{
+	GDateTimeSource *datetime_source = (GDateTimeSource*)source;
+
+	datetime_source->initially_expired = FALSE;
+
+	if (!callback) {
+		g_warning ("Timeout source dispatched without callback\n"
+			   "You must call g_source_set_callback().");
+		return FALSE;
+	}
+  
+	(callback) (user_data);
+
+	/* Always false as this source is documented to run once */
+	return FALSE;
+}
+
+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 = {
+	g_datetime_source_prepare,
+	g_datetime_source_check,
+	g_datetime_source_dispatch,
+	g_datetime_source_finalize
+};
+
+#ifdef HAVE_TIMERFD
+static gboolean
+g_datetime_source_init_timerfd (GDateTimeSource *datetime_source,
+				gint64           expected_now_seconds,
+				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;
+	}
+
+	/* Now we need to check that the clock didn't go backwards before we
+	 * had the timerfd set up.  See
+	 * https://bugzilla.gnome.org/show_bug.cgi?id=655129
+	 */
+	clock_gettime (CLOCK_REALTIME, &its.it_value);
+	if (its.it_value.tv_sec < expected_now_seconds)
+		datetime_source->initially_expired = TRUE;
+
+	datetime_source->pollfd.events = G_IO_IN;
+
+	g_source_add_poll ((GSource*) datetime_source, &datetime_source->pollfd);
+
+	return TRUE;
+}
+#endif
+
+/**
+ * _gnome_date_time_source_new:
+ * @now: The expected current time
+ * @expiry: Time to await
+ * @cancel_on_set: Also invoke callback if the system clock changes discontiguously
+ *
+ * This function is designed for programs that want to schedule an
+ * event based on real (wall clock) time, as returned by
+ * g_get_real_time().  For example, HOUR:MINUTE wall-clock displays
+ * and calendaring software.  The callback will be invoked when the
+ * specified wall clock time @expiry is reached.  This includes
+ * events such as the system clock being set past the given time.
+ *
+ * Compare versus g_timeout_source_new() which is defined to use
+ * monotonic time as returned by g_get_monotonic_time().
+ *
+ * The parameter @now is necessary to avoid a race condition in
+ * between getting the current time and calling this function.
+ *
+ * If @cancel_on_set is given, the callback will also be invoked at
+ * most a second after the system clock is changed.  This includes
+ * being set backwards or forwards, and system
+ * resume from suspend.  Not all operating systems allow detecting all
+ * relevant events efficiently - this function may cause the process
+ * to wake up once a second in those cases.
+ *
+ * A wall clock display should use @cancel_on_set; a calendaring
+ * program shouldn't need to.
+ *
+ * Note that the return value from the associated callback will be
+ * ignored; this is a one time watch.
+ *
+ * <note><para>This function currently does not detect time zone
+ * changes.  On Linux, your program should also monitor the
+ * <literal>/etc/timezone</literal> file using
+ * #GFileMonitor.</para></note>
+ *
+ * <example id="gdatetime-example-watch"><title>Clock example</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; parse="text" href="../../../../glib/tests/glib-clock.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
+ *
+ * Return value: A newly-constructed #GSource
+ *
+ * Since: 2.30
+ **/
+GSource *
+_gnome_datetime_source_new (GDateTime  *now,
+			    GDateTime  *expiry,
+			    gboolean    cancel_on_set)
+{
+	GDateTimeSource *datetime_source;
+	gint64 unix_seconds;
+
+	unix_seconds = g_date_time_to_unix (expiry);
+
+	datetime_source = (GDateTimeSource*) g_source_new (&g_datetime_source_funcs, sizeof (GDateTimeSource));
+
+	datetime_source->cancel_on_set = cancel_on_set;
+
+#ifdef HAVE_TIMERFD
+	{
+		gint64 expected_now_seconds = g_date_time_to_unix (now);
+		if (g_datetime_source_init_timerfd (datetime_source, expected_now_seconds, 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;
+}
+
diff --git a/libgnome-desktop/gnome-datetime-source.h b/libgnome-desktop/gnome-datetime-source.h
new file mode 100644
index 0000000..2c111c3
--- /dev/null
+++ b/libgnome-desktop/gnome-datetime-source.h
@@ -0,0 +1,38 @@
+/* gnome-rr.h
+ *
+ * Copyright 2011, Red Hat, Inc.
+ * 
+ * This file is part of the Gnome Library.
+ * 
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#ifndef GNOME_DATETIME_SOURCE_H
+#define GNOME_DATETIME_SOURCE_H
+
+#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
+#error    This is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API
+#endif
+
+#include <glib.h>
+
+GSource *_gnome_datetime_source_new (GDateTime *now,
+				     GDateTime *expiry,
+				     gboolean   cancel_on_set);
+
+#endif /* GNOME_DATETIME_SOURCE_H */
diff --git a/libgnome-desktop/gnome-wall-clock.c b/libgnome-desktop/gnome-wall-clock.c
index 2989b8c..0c4a79d 100644
--- a/libgnome-desktop/gnome-wall-clock.c
+++ b/libgnome-desktop/gnome-wall-clock.c
@@ -29,6 +29,7 @@
 #define GNOME_DESKTOP_USE_UNSTABLE_API
 #include "gnome-wall-clock.h"
 #include <gdesktop-enums.h>
+#include "gnome-datetime-source.h"
 
 struct _GnomeWallClockPrivate {
 	guint clock_update_id;
@@ -162,23 +163,26 @@ update_clock (gpointer data)
 	GSource *source;
 	GDateTime *now;
 	GDateTime *expiry;
-  
+
+	clock_format = g_settings_get_enum (self->priv->desktop_settings, "clock-format");
+	show_date = g_settings_get_boolean (self->priv->desktop_settings, "clock-show-date");
+	show_seconds = g_settings_get_boolean (self->priv->desktop_settings, "clock-show-seconds");
+
 	now = g_date_time_new_now_local ();
-	expiry = g_date_time_add_seconds (now, 60 - g_date_time_get_second (now));
+	if (show_seconds)
+		expiry = g_date_time_add_seconds (now, 1);
+	else
+		expiry = g_date_time_add_seconds (now, 60 - g_date_time_get_second (now));
   
 	if (self->priv->clock_update_id)
 		g_source_remove (self->priv->clock_update_id);
   
-	source = g_timeout_source_new_seconds (1);
+	source = _gnome_datetime_source_new (now, expiry, TRUE);
 	g_source_set_priority (source, G_PRIORITY_HIGH);
 	g_source_set_callback (source, update_clock, self, NULL);
 	self->priv->clock_update_id = g_source_attach (source, NULL);
 	g_source_unref (source);
 
-	clock_format = g_settings_get_enum (self->priv->desktop_settings, "clock-format");
-	show_date = g_settings_get_boolean (self->priv->desktop_settings, "clock-show-date");
-	show_seconds = g_settings_get_boolean (self->priv->desktop_settings, "clock-show-seconds");
-
 	if (clock_format == G_DESKTOP_CLOCK_FORMAT_24H) {
 		if (show_date)
 			/* Translators: This is the time format with date used



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