Add functions for approximate timeouts
authorMatthias Clasen <matthiasc@src.gnome.org>
Sun, 10 Sep 2006 05:44:46 +0000 (05:44 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Sun, 10 Sep 2006 05:44:46 +0000 (05:44 +0000)
ChangeLog
docs/reference/ChangeLog
docs/reference/glib/glib-sections.txt
glib/glib.symbols
glib/gmain.c
glib/gmain.h

index 0a9ecb8..7cf3e9d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2006-09-10  Matthias Clasen  <mclasen@redhat.com>
 
+       * glib/glib.symbols:
+       * glib/gmain.[hc]: Add functions to create approximate
+       timeouts.  (#353942, Arjan van de Ven)
+
        * glib/gstdio.c (g_rename): Initialize save_errno.
        (#355206, Mike Edenfield)
 
index 26ea442..073401e 100644 (file)
@@ -1,3 +1,7 @@
+2006-09-10  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/glib-sections.txt: Add new functions
+
 2006-08-28  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/compiling.sgml: Add a note about G_DISABLE_DEPRECATED.
index 62e3480..24ab189 100644 (file)
@@ -437,8 +437,10 @@ g_main_set_poll_func
 
 <SUBSECTION>
 g_timeout_source_new
+g_timeout_source_new_seconds
 g_timeout_add
 g_timeout_add_full
+g_timeout_add_seconds
 
 <SUBSECTION>
 g_idle_source_new
index 0058353..1411e6f 100644 (file)
@@ -617,8 +617,10 @@ g_idle_add_full
 g_idle_remove_by_data
 g_idle_source_new
 g_timeout_add
+g_timeout_add_seconds
 g_timeout_add_full
 g_timeout_source_new
+g_timeout_source_new_seconds
 #endif
 #endif
 
index ed20837..5b3359a 100644 (file)
@@ -41,6 +41,7 @@
 #include <signal.h>
 #include <sys/types.h>
 #include <time.h>
+#include <stdlib.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif /* HAVE_SYS_TIME_H */
@@ -179,6 +180,7 @@ struct _GTimeoutSource
   GSource     source;
   GTimeVal    expiration;
   guint       interval;
+  guint              granularity;
 };
 
 struct _GChildWatchSource
@@ -281,6 +283,8 @@ static gint child_watch_wake_up_pipe[2] = {0, 0};
 G_LOCK_DEFINE_STATIC (main_context_list);
 static GSList *main_context_list = NULL;
 
+static gint timer_perturb = -1;
+
 GSourceFuncs g_timeout_funcs =
 {
   g_timeout_prepare,
@@ -3337,6 +3341,55 @@ g_timeout_set_expiration (GTimeoutSource *timeout_source,
       timeout_source->expiration.tv_usec -= 1000000;
       timeout_source->expiration.tv_sec++;
     }
+  if (timer_perturb==-1)
+    {
+      /*
+       * we want a per machine/session unique 'random' value; try the dbus
+       * address first, that has a UUID in it. If there is no dbus, use the
+       * hostname for hashing.
+       */
+      const char *session_bus_address = getenv("DBUS_SESSION_BUS_ADDRESS");
+      if (!session_bus_address)
+         session_bus_address = getenv("HOSTNAME");
+      if (session_bus_address)
+         timer_perturb = g_str_hash(session_bus_address);
+      else
+         timer_perturb = 0;
+    }
+  if (timeout_source->granularity)
+    {
+      gint remainder;
+      gint gran; /* in usecs */
+      gint perturb;
+
+      gran = timeout_source->granularity * 1000;
+      perturb = timer_perturb % gran;
+      /*
+       * We want to give each machine a per machine pertubation;
+       * shift time back first, and forward later after the rounding
+       */
+
+      timeout_source->expiration.tv_usec -= perturb;
+      if (timeout_source->expiration.tv_usec < 0)
+      {
+          timeout_source->expiration.tv_usec += 1000000;
+          timeout_source->expiration.tv_sec--;
+      }
+
+      remainder = timeout_source->expiration.tv_usec % gran;
+      if (remainder >= gran/4) /* round up */
+        timeout_source->expiration.tv_usec += gran;
+      timeout_source->expiration.tv_usec -= remainder;
+      /* shift back */
+      timeout_source->expiration.tv_usec += perturb;
+
+      /* the rounding may have overflown tv_usec */
+      while (timeout_source->expiration.tv_usec > 1000000)
+        {
+          timeout_source->expiration.tv_usec -= 1000000;
+          timeout_source->expiration.tv_sec++;
+        }
+    }
 }
 
 static gboolean
@@ -3460,6 +3513,39 @@ g_timeout_source_new (guint interval)
 }
 
 /**
+ * g_timeout_source_new_seconds:
+ * @interval: the timeout interval in seconds
+ *
+ * Creates a new timeout source.
+ *
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_attach() before it will be
+ * executed.
+ * The scheduling granularity/accuracy of this timeout source will be
+ * in seconds.
+ *
+ * Return value: the newly-created timeout source
+ *
+ * Since: 2.14 
+ **/
+GSource *
+g_timeout_source_new_seconds (guint interval)
+{
+  GSource *source = g_source_new (&g_timeout_funcs, sizeof (GTimeoutSource));
+  GTimeoutSource *timeout_source = (GTimeoutSource *)source;
+  GTimeVal current_time;
+
+  timeout_source->interval = 1000*interval;
+  timeout_source->granularity = 1000;
+
+  g_get_current_time (&current_time);
+  g_timeout_set_expiration (timeout_source, &current_time);
+
+  return source;
+}
+
+
+/**
  * g_timeout_add_full:
  * @priority: the priority of the idle source. Typically this will be in the
  *            range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
@@ -3526,10 +3612,15 @@ g_timeout_add_full (gint           priority,
  * After each call to the timeout function, the time of the next
  * timeout is recalculated based on the current time and the given interval
  * (it does not try to 'catch up' time lost in delays).
- * 
+ *
+ * If you want to have a timer in the "seconds" range and do not care
+ * about the exact time of the first call of the timer, use the
+ * g_timeout_add_seconds() function; this function allows for more
+ * optimizations and more efficient system power usage.
+ *
  * Return value: the ID (greater than 0) of the event source.
  **/
-guint 
+guint
 g_timeout_add (guint32        interval,
               GSourceFunc    function,
               gpointer       data)
@@ -3538,6 +3629,62 @@ g_timeout_add (guint32        interval,
                             interval, function, data, NULL);
 }
 
+/**
+ * g_timeout_add_seconds:
+ * @interval: the time between calls to the function, in seconds
+ * @function: function to call
+ * @data:     data to pass to @function
+ *
+ * Sets a function to be called at regular intervals, with the default
+ * priority, #G_PRIORITY_DEFAULT.  The function is called repeatedly
+ * until it returns %FALSE, at which point the timeout is automatically
+ * destroyed and the function will not be called again.
+ *
+ * Unlike g_timeout_add(), this function operates at whole second granularity.
+ * The initial starting point of the timer is determined by the implementation
+ * and the implementation is expected to group multiple timers together so that
+ * they fire all at the same time.
+ * To allow this grouping, the @interval to the first timer is rounded
+ * and can deviate up to one second from the specified interval.
+ * Subsequent timer iterations will generally run at the specified interval.
+ *
+ * Note that timeout functions may be delayed, due to the processing of other
+ * event sources. Thus they should not be relied on for precise timing.
+ * After each call to the timeout function, the time of the next
+ * timeout is recalculated based on the current time and the given @interval
+ *
+ * If you want timing more precise than whole seconds, use g_timeout_add()
+ * instead.
+ *
+ * The grouping of timers to fire at the same time results in a more power
+ * and CPU efficient behavior so if your timer is in multiples of seconds
+ * and you don't require the first timer exactly 1 second from now, the
+ * use of g_timeout_add_second() is prefered over g_timeout_add().
+ *
+ * Return value: the ID (greater than 0) of the event source.
+ *
+ * Since: 2.14
+ **/
+guint
+g_timeout_add_seconds (guint32        interval,
+              GSourceFunc    function,
+              gpointer       data)
+{
+  GSource *source;
+  guint id;
+
+  g_return_val_if_fail (function != NULL, 0);
+
+  source = g_timeout_source_new_seconds (interval);
+
+  g_source_set_callback (source, function, data, NULL);
+  id = g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  return id;
+}
+
+
 /* Child watch functions */
 
 #ifdef G_OS_WIN32
index 035b615..8e8dd3f 100644 (file)
@@ -256,6 +256,7 @@ void     g_source_get_current_time (GSource        *source,
 GSource *g_idle_source_new        (void);
 GSource *g_child_watch_source_new (GPid pid);
 GSource *g_timeout_source_new     (guint interval);
+GSource *g_timeout_source_new_seconds (guint interval);
 
 /* Miscellaneous functions
  */
@@ -298,6 +299,9 @@ guint    g_timeout_add_full     (gint            priority,
 guint    g_timeout_add          (guint           interval,
                                 GSourceFunc     function,
                                 gpointer        data);
+guint    g_timeout_add_seconds  (guint           interval,
+                                GSourceFunc     function,
+                                gpointer        data);
 guint    g_child_watch_add_full (gint            priority,
                                 GPid            pid,
                                 GChildWatchFunc function,