glib: GTimer patch for elapsed time



Hi,

I recently worked on a project that used Gtk+ and timers, and I was
quite surprised to find that the GTimer does not properly track elapsed
time.

The GTimer in glib resets the clock at each call to gtk_timer_start(). 
Thus it is incapable of tracking true elapsed time across consecutive
calls to start-stop.  This rather defeats the purpose of having a timer
object, since you then have to implement elapsed time tracking yourself.

The GTimer code has been revisited for v2.0, but it is still effectively
broken.  I have written a patch (for the v2 code) to address this.  So
multiple calls to start-stop will now correctly count total elapsed
time, and it is not zeroed until you call reset.

I believe the interface is correct; it is modelled after a stopwatch,
with which we are all familiar.  The implementation just doesn't reflect
this design.

I presume the correct procedure is to file a bug and attach the patch?

Since this is my first contribution to gtk/glib, I have attached the
patch, for feedback first (Owen?).

Having gone this far, I would like to do some more work on streamlining
the implementation, and adding support to handle time manipulation (just
as the date support does), as well as some sort of call like:

    g_time_snprintf( elapsed, buf, sizeof(buf), "%h:%m:%s" );

    ==>

    "15:32:47"

I have started work on this, and will post more soon.

ciao -

    ::gavin




--- gtimer.c.~1.16.~	Thu Sep 20 04:08:19 2001
+++ gtimer.c	Tue Jul  9 23:20:37 2002
@@ -50,10 +50,10 @@ struct _GTimer
 {
 #ifdef G_OS_WIN32
   DWORD start;
-  DWORD end;
+  DWORD elapsed;
 #else /* !G_OS_WIN32 */
   struct timeval start;
-  struct timeval end;
+  struct timeval elapsed;
 #endif /* !G_OS_WIN32 */
 
   guint active : 1;
@@ -67,6 +67,12 @@ struct _GTimer
      gettimeofday (&v, NULL)
 #endif /* !G_OS_WIN32 */
 
+
+/**
+ *  g_timer_new:
+ *
+ *  Creates a new GTimer.  It starts running and active.
+ **/
 GTimer*
 g_timer_new (void)
 {
@@ -77,9 +83,23 @@ g_timer_new (void)
 
   GETTIME (timer->start);
 
+#ifdef G_OS_WIN32
+  timer->elapsed = 0;
+#else
+  timer->elapsed.tv_usec = 0;
+  timer->elapsed.tv_sec = 0;
+#endif /* G_OS_WIN32 */
+
   return timer;
 }
 
+/**
+ *  g_timer_destroy:
+ *
+ *  @timer: the timer instance to destroy
+ *
+ *  Releases any resources associated with the @timer instance.
+ **/
 void
 g_timer_destroy (GTimer *timer)
 {
@@ -88,78 +108,182 @@ g_timer_destroy (GTimer *timer)
   g_free (timer);
 }
 
+/**
+ *  g_timer_start:
+ *
+ *  @timer: the timer to start.
+ *
+ *  Makes the timer active, and starts running.  Total elapsed time
+ *  is preserved.
+ **/
 void
 g_timer_start (GTimer *timer)
 {
   g_return_if_fail (timer != NULL);
+  g_return_if_fail (timer->active == FALSE );
 
   timer->active = TRUE;
 
   GETTIME (timer->start);
 }
 
+/**
+ *  g_timer_stop:
+ *
+ *  @timer: the timer to stop.
+ *
+ *  Makes the timer inactive, and stops running.  Total elapsed time
+ *  is preserved.
+ **/
 void
 g_timer_stop (GTimer *timer)
 {
-  g_return_if_fail (timer != NULL);
+#ifdef G_OS_WIN32
+    DWORD end;
+#else /* !G_OS_WIN32 */
+    struct timeval end;
+#endif /* !G_OS_WIN32 */
 
-  timer->active = FALSE;
+    g_return_if_fail (timer != NULL);
+    g_return_if_fail (timer->active == TRUE);
+
+    timer->active = FALSE;
+
+    GETTIME (end);
+
+    /* Calculate the time elapsed since the last start */
+
+#ifdef G_OS_WIN32
+
+    /* Check for wraparound, which happens every 49.7 days. */
+    if (end < timer->start)
+        timer->elapsed += (UINT_MAX - (timer->start - end)) / 1000.0;
+    else
+        timer->elapsed += (end - timer->start) / 1000.0;
+
+#else /* !G_OS_WIN32 */
+
+    if (timer->start.tv_usec > end.tv_usec)
+    {
+        end.tv_usec += G_USEC_PER_SEC;
+        end.tv_sec--;
+    }
 
-  GETTIME(timer->end);
+    timer->elapsed.tv_usec += end.tv_usec - timer->start.tv_usec;
+    timer->elapsed.tv_sec  += end.tv_sec - timer->start.tv_sec;
+
+    /* Normalise elapsed time */
+    while (timer->elapsed.tv_usec > G_USEC_PER_SEC)
+    {
+        timer->elapsed.tv_usec -= G_USEC_PER_SEC;
+        timer->elapsed.tv_sec++;
+    }
+
+#endif
+}
+
+/**
+ *  g_timer_is_active:
+ *
+ *  Returns whether or not the timer is currently running.
+ **/
+gboolean
+g_timer_is_active (GTimer *timer)
+{
+    return timer->active;
 }
 
+/**
+ *  g_timer_reset:
+ *
+ *  Stops the timer and zeroes the elapsed time.
+ *
+ **/
 void
 g_timer_reset (GTimer *timer)
 {
   g_return_if_fail (timer != NULL);
 
+  timer->active = FALSE;
+
   GETTIME (timer->start);
+
+#ifdef G_OS_WIN32
+  timer->elapsed = 0;
+#else
+  timer->elapsed.tv_usec = 0;
+  timer->elapsed.tv_sec = 0;
+#endif /* G_OS_WIN32 */
 }
 
+/**
+ *  g_timer_elapsed:
+ *
+ *  @timer:         The timer instance to query
+ *  @microseconds:  The number of microseconds since the last second,
+ *                  or NULL if this is not required.
+ *
+ *  Returns the total time, in fractional seconds, elapsed for this
+ *  timer.  Elapsed time is defined as the total time run between
+ *  successive start-stop calls.  If inactive, returns the total
+ *  elapsed time.  If active, returns the total elapsed time,
+ *  including the time since the last start call.
+ **/
 gdouble
 g_timer_elapsed (GTimer *timer,
 		 gulong *microseconds)
 {
   gdouble total;
-#ifndef G_OS_WIN32
-  struct timeval elapsed;
+#ifdef G_OS_WIN32
+  DWORD end;
+#else
+  struct timeval end;
 #endif /* G_OS_WIN32 */
 
   g_return_val_if_fail (timer != NULL, 0);
 
-#ifdef G_OS_WIN32
   if (timer->active)
-    timer->end = GetTickCount ();
+      GETTIME(end);
+  else
+      end = timer->start;
+
+#ifdef G_OS_WIN32
 
   /* Check for wraparound, which happens every 49.7 days. */
-  if (timer->end < timer->start)
-    total = (UINT_MAX - (timer->start - timer->end)) / 1000.0;
+  if (end < timer->start)
+    total = (UINT_MAX - (timer->start - end)) / 1000.0;
   else
-    total = (timer->end - timer->start) / 1000.0;
+    total = (end - timer->start) / 1000.0;
 
   if (microseconds)
     {
-      if (timer->end < timer->start)
+      if (end < timer->start)
 	*microseconds =
-	  ((UINT_MAX - (timer->start - timer->end)) % 1000) * 1000;
+	  ((UINT_MAX - (timer->start - end)) % 1000) * 1000;
       else
 	*microseconds =
 	  ((timer->end - timer->start) % 1000) * 1000;
     }
+
 #else /* !G_OS_WIN32 */
-  if (timer->active)
-    gettimeofday (&timer->end, NULL);
 
-  if (timer->start.tv_usec > timer->end.tv_usec)
+  if (timer->start.tv_usec > end.tv_usec)
     {
-      timer->end.tv_usec += G_USEC_PER_SEC;
-      timer->end.tv_sec--;
+      end.tv_usec += G_USEC_PER_SEC;
+      end.tv_sec--;
     }
 
-  elapsed.tv_usec = timer->end.tv_usec - timer->start.tv_usec;
-  elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
+  timer->elapsed.tv_usec += end.tv_usec - timer->start.tv_usec;
+  timer->elapsed.tv_sec  += end.tv_sec - timer->start.tv_sec;
+
+  /* Normalise elapsed time */
+  while (timer->elapsed.tv_usec > G_USEC_PER_SEC)
+  {
+      timer->elapsed.tv_usec -= G_USEC_PER_SEC;
+      timer->elapsed.tv_sec++;
+  }
 
-  total = elapsed.tv_sec + ((gdouble) elapsed.tv_usec / 1e6);
+  total = timer->elapsed.tv_sec + ((gdouble) timer->elapsed.tv_usec / 1e6);
   if (total < 0)
     {
       total = 0;
@@ -168,13 +292,22 @@ g_timer_elapsed (GTimer *timer,
 	*microseconds = 0;
     }
   else if (microseconds)
-    *microseconds = elapsed.tv_usec;
+    *microseconds = timer->elapsed.tv_usec;
 
 #endif /* !G_OS_WIN32 */
 
   return total;
 }
 
+/**
+ *  g_usleep:
+ *
+ *  @microseconds:  Number of microseconds to sleep for
+ *
+ *  Puts the current thread to sleep for the given number of microseconds.
+ *  This call is blocking, and the sleep time is a minimum value; depending
+ *  on system load and scheduling, it may sleep longer.
+ **/
 void
 g_usleep (gulong microseconds)
 {




-- 
Gavin Baker // gavinb*antonym_org // Linux|Python|Esperanto|MIDI|Cheese




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