Re: glib: GTimer patch for elapsed time



Greetings,

On Wed, 2002-07-10 at 09:55, Owen Taylor wrote:
> 
> Gavin Baker <gavinb antonym org> writes:
> > [...]
> > 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.
> > [...]
> 
> This isn't a change we can make. 
> 
>  http://developer.gnome.org/doc/API/2.0/glib/glib-timers.html#g-timer-start
> 
> Describes the current behavior, and even if it didn't people would
> be very likely relying on the current behavior. An incompatible change
> could only be made for GLib-3.0, and even then we try very hard
> to avoid incompatible changes, or to make them in a way that is
> caught by the compiler.

Ok; understand.  (I'll keep that in mind for future patching too!)  I
have a clean solution now...

> On the other hand adding a g_timer_continue() that doesn't reset the
> timer to zero sounds reasonable to me.

I have rewritten the patch.  The existing behaviour is not changed.  I
have added code to g_timer_stop() to accumulate the total elapsed time,
and I have added a new function called g_timer_total_elapsed() that will
return the true elapsed time (between start-stop calls) since the last
g_timer_reset() (instead of modifying g_timer_elapsed()).

I also added g_timer_is_active() to return whether or not the timer is
running.  In the process, I also added docstrings for all the functions,
that are consistent with the API reference on the web.  I can update the
web reference if someone tells me where the source lives.

I have tested and verified that it works according to both the old and
the new ways.  Is this ok?  Shall I file this in bugzilla?

Thanks -

  ::gavin



Index: gtimer.h
===================================================================
RCS file: /cvs/gnome/glib/glib/gtimer.h,v
retrieving revision 1.3
diff -u -p -r1.3 gtimer.h
--- gtimer.h	2001/06/26 16:01:14	1.3
+++ gtimer.h	2002/07/15 13:48:31
@@ -46,6 +46,9 @@ void	g_timer_stop	(GTimer	  *timer);
 void	g_timer_reset	(GTimer	  *timer);
 gdouble g_timer_elapsed (GTimer	  *timer,
 			 gulong	  *microseconds);
+gdouble g_timer_total_elapsed (GTimer *timer,
+			 gulong	  *microseconds);
+gboolean g_timer_is_active (GTimer *timer);
 
 void    g_usleep        (gulong    microseconds);
 
 
Index: gtimer.c
===================================================================
RCS file: /cvs/gnome/glib/glib/gtimer.c,v
retrieving revision 1.16
diff -u -p -r1.16 gtimer.c
--- gtimer.c	2001/09/19 18:08:19	1.16
+++ gtimer.c	2002/07/15 13:48:41
@@ -51,9 +51,11 @@ 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 +69,13 @@ struct _GTimer
      gettimeofday (&v, NULL)
 #endif /* !G_OS_WIN32 */
 
+
+/**
+ *  g_timer_new:
+ *
+ *  Creates a new GTimer, and starts timing (ie. a #g_timer_start()
+ *  is implicitly called for you).
+ **/
 GTimer*
 g_timer_new (void)
 {
@@ -77,9 +86,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
+ *
+ *  Destroys a timer, freeing any associated resources.
+ **/
 void
 g_timer_destroy (GTimer *timer)
 {
@@ -88,6 +111,15 @@ g_timer_destroy (GTimer *timer)
   g_free (timer);
 }
 
+/**
+ *  g_timer_start:
+ *
+ *  @timer: the timer to start.
+ *
+ *  Marks a start time, so that future calls to #g_timer_elapsed()
+ *  will report the time since this was called.  The timer is made
+ *  active, and the total elapsed time is preserved.
+ **/
 void
 g_timer_start (GTimer *timer)
 {
@@ -98,6 +130,15 @@ g_timer_start (GTimer *timer)
   GETTIME (timer->start);
 }
 
+/**
+ *  g_timer_stop:
+ *
+ *  @timer: the timer to stop.
+ *
+ *  Marks an end time, so calls to #g_timer_elapsed() will return the
+ *  difference between the start time and this end time.  The timer is
+ *  made inactive, and  the total elapsed time is accumulated.
+ **/
 void
 g_timer_stop (GTimer *timer)
 {
@@ -106,16 +147,90 @@ g_timer_stop (GTimer *timer)
   timer->active = FALSE;
 
   GETTIME(timer->end);
+
+    /* Calculate the time elapsed since the last start */
+
+#ifdef G_OS_WIN32
+
+    /* Check for wraparound, which happens every 49.7 days. */
+    if (timer->end < timer->start)
+        timer->elapsed += (UINT_MAX - (timer->start - timer->end)) / 1000.0;
+    else
+        timer->elapsed += (timer->end - timer->start) / 1000.0;
+
+#else /* !G_OS_WIN32 */
+
+    if (timer->start.tv_usec > timer->end.tv_usec)
+    {
+        timer->end.tv_usec += G_USEC_PER_SEC;
+        timer->end.tv_sec--;
+    }
+
+    timer->elapsed.tv_usec += timer->end.tv_usec - timer->start.tv_usec;
+    timer->elapsed.tv_sec  += timer->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)
+{
+    g_return_val_if_fail (timer != NULL, FALSE);
+
+    return timer->active;
+}
+
+/**
+ *  g_timer_reset:
+ *
+ *  Stops the timer and zeroes the start time and the total 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.
+ *
+ *  If @timer has been started but not stopped, returns the time since
+ *  the timer was started.  If @timer has been stopped, obtains the
+ *  elapsed time between the time it was started and the time it was
+ *  stopped.
+ *
+ *  To get the total elapsed time since the timer was created or last
+ *  reset, use #g_timer_total_elapsed().
+ **/
 gdouble
 g_timer_elapsed (GTimer *timer,
 		 gulong *microseconds)
@@ -175,6 +290,99 @@ g_timer_elapsed (GTimer *timer,
   return total;
 }
 
+/**
+ *  g_timer_total_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.  Time will accumulate
+ *  until #g_timer_reset() is called.
+ **/
+gdouble
+g_timer_total_elapsed (GTimer *timer,
+                       gulong *microseconds)
+{
+  gdouble total;
+#ifdef G_OS_WIN32
+  DWORD end;
+#else
+  struct timeval end;
+#endif /* G_OS_WIN32 */
+
+  g_return_val_if_fail (timer != NULL, 0);
+
+  if (timer->active)
+      GETTIME(end);
+  else
+      end = timer->start;
+
+#ifdef G_OS_WIN32
+
+  /* Check for wraparound, which happens every 49.7 days. */
+  if (end < timer->start)
+    total = (UINT_MAX - (timer->start - end)) / 1000.0;
+  else
+    total = (end - timer->start) / 1000.0;
+
+  if (microseconds)
+    {
+      if (end < timer->start)
+	*microseconds =
+	  ((UINT_MAX - (timer->start - end)) % 1000) * 1000;
+      else
+	*microseconds =
+	  ((timer->end - timer->start) % 1000) * 1000;
+    }
+
+#else /* !G_OS_WIN32 */
+
+  if (timer->start.tv_usec > end.tv_usec)
+    {
+      end.tv_usec += G_USEC_PER_SEC;
+      end.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 = timer->elapsed.tv_sec + ((gdouble) timer->elapsed.tv_usec / 1e6);
+  if (total < 0)
+    {
+      total = 0;
+
+      if (microseconds)
+          *microseconds = 0;
+    }
+  else if (microseconds)
+      *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]