Moved silc_usleep from inline function to source file.
[runtime.git] / lib / silcutil / silctimer.h
index 79c57750da8c013294f89dd76d95291f429adc26..f63486941e4e59d4d2a47b9058bbab62a88666d8 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2007 Pekka Riikonen
+  Copyright (C) 2007 - 2008 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 
-/****h* silcutil/SILC Timer Interface
+/****h* silcutil/Timer Interface
+ *
+ * DESCRIPTION
  *
  * SILC Timer interface provides a simple way to measure time intervals.
  * The SILC Timer works with microsecond resolution, depending on platform.
  *
+ * The interface also provides utility functions to get currenet CPU tick
+ * count and to delay process/thread execution.
+ *
+ * EXAMPLE
+ *
+ * SilcTimerStruct timer;
+ *
+ * silc_timer_start(&timer);
+ * ... time passes ...
+ * silc_timer_stop(&timer);
+ * silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
+ *
  ***/
 
 #ifndef SILCTIMER_H
 #define SILCTIMER_H
 
-/****s* silcutil/SilcTimerAPI/SilcTimer
+/****s* silcutil/SilcTimer
+ *
+ * NAME
+ *
+ *    typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
+ *
+ * DESCRIPTION
+ *
+ *    The timer context.  The context is given as argument to all
+ *    silc_timer_* functions.
+ *
+ ***/
+/****s* silcutil/SilcTimerStruct
  *
  * NAME
  *
@@ -41,7 +67,7 @@
  ***/
 typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
 
-/****f* silcutil/SilcTimerAPI/silc_timer_start
+/****f* silcutil/silc_timer_start
  *
  * SYNOPSIS
  *
@@ -64,7 +90,7 @@ typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
  ***/
 void silc_timer_start(SilcTimer timer);
 
-/****f* silcutil/SilcTimerAPI/silc_timer_stop
+/****f* silcutil/silc_timer_stop
  *
  * SYNOPSIS
  *
@@ -78,7 +104,7 @@ void silc_timer_start(SilcTimer timer);
  ***/
 void silc_timer_stop(SilcTimer timer);
 
-/****f* silcutil/SilcTimerAPI/silc_timer_continue
+/****f* silcutil/silc_timer_continue
  *
  * SYNOPSIS
  *
@@ -91,7 +117,7 @@ void silc_timer_stop(SilcTimer timer);
  ***/
 void silc_timer_continue(SilcTimer timer);
 
-/****f* silcutil/SilcTimerAPI/silc_timer_value
+/****f* silcutil/silc_timer_value
  *
  * SYNOPSIS
  *
@@ -110,7 +136,7 @@ void silc_timer_value(SilcTimer timer,
                      SilcUInt64 *elapsed_time_seconds,
                      SilcUInt32 *elapsed_time_microseconds);
 
-/****f* silcutil/SilcTimerAPI/silc_timer_value_time
+/****f* silcutil/silc_timer_value_time
  *
  * SYNOPSIS
  *
@@ -126,7 +152,7 @@ void silc_timer_value(SilcTimer timer,
  ***/
 void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
 
-/****f* silcutil/SilcTimerAPI/silc_timer_start_time
+/****f* silcutil/silc_timer_start_time
  *
  * SYNOPSIS
  *
@@ -139,7 +165,7 @@ void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
  ***/
 void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
 
-/****f* silcutil/SilcTimerAPI/silc_timer_is_running
+/****f* silcutil/silc_timer_is_running
  *
  * SYNOPSIS
  *
@@ -154,4 +180,147 @@ SilcBool silc_timer_is_running(SilcTimer timer);
 
 #include "silctimer_i.h"
 
+/****f* silcutil/silc_timer_tick
+ *
+ * SYNOPSIS
+ *
+ *    SilcUInt64 silc_timer_tick(SilcTimer &timer, SilcBool adjust)
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current CPU tick count.  You should call the
+ *    silc_timer_synchronize before using this function to make sure the
+ *    overhead of measuring the CPU tick count is not included in the
+ *    tick count.  If the `adjust' is TRUE and the silc_timer_synchronize
+ *    has been called the returned value is adjusted to be more accurate.
+ *
+ * EXAMPLE
+ *
+ *    // Synchronize timer for more accurate CPU tick counts
+ *    silc_timer_synchronize(&timer);
+ *    start = silc_timer_tick(&timer, FALSE);
+ *    do_something();
+ *    stop = silc_timer_tick(&timer, TRUE);
+ *
+ ***/
+
+static inline
+SilcUInt64 silc_timer_tick(SilcTimer timer, SilcBool adjust)
+{
+#if defined(__GNUC__) || defined(__ICC)
+#if defined(SILC_I486)
+  SilcUInt64 x;
+  asm volatile ("rdtsc" : "=A" (x));
+  return adjust ? x - timer->sync_tdiff : x;
+
+#elif defined(SILC_X86_64)
+  SilcUInt64 x;
+  SilcUInt32 hi, lo;
+  asm volatile ("rdtsc" : "=a" (lo), "=d" (hi));
+  x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
+  return adjust ? x - timer->sync_tdiff : x;
+
+#elif defined(SILC_POWERPC)
+  SilcUInt32 hi, lo, tmp;
+  asm volatile ("0:            \n\t"
+                "mftbu   %0    \n\t"
+                "mftb    %1    \n\t"
+                "mftbu   %2    \n\t"
+                "cmpw    %2,%0 \n\t"
+                "bne     0b    \n"
+                : "=r" (hi), "=r" (lo), "=r" (tmp));
+  x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
+  return adjust ? x - timer->sync_tdiff : x;
+
+#else
+  return 0;
+#endif /* SILC_I486 */
+
+#elif defined(SILC_WIN32)
+  __asm rdtsc
+
+#else
+  return 0;
+#endif /* __GNUC__ || __ICC */
+}
+
+/****f* silcutil/silc_timer_synchronize
+ *
+ * SYNOPSIS
+ *
+ *    void silc_timer_synchronize(SilcTimer timer);
+ *
+ * DESCRIPTION
+ *
+ *    Synchronizes the `timer'.  This call will attempt to synchronize the
+ *    timer for more accurate results with high resolution timing.  Call
+ *    this before you start using the `timer'.
+ *
+ * EXAMPLE
+ *
+ *    // Synchronize timer
+ *    silc_timer_synchronize(&timer);
+ *    silc_timer_start(&timer);
+ *    ... time passes ...
+ *    silc_timer_stop(&timer);
+ *    silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
+ *
+ ***/
+
+static inline
+void silc_timer_synchronize(SilcTimer timer)
+{
+  SilcUInt32 tdiff, cumu, i;
+  SilcUInt64 t1, t2;
+
+  /* Sync normal timer */
+  for (i = 0, cumu = 0; i < 5; i++) {
+    silc_timer_start(timer);
+    silc_timer_stop(timer);
+    silc_timer_value(timer, NULL, &tdiff);
+    cumu += (int)tdiff;
+  }
+
+  timer->sync_diff = cumu;
+  if (timer->sync_diff > 5)
+    timer->sync_diff /= 5;
+
+  /* Sync CPU tick count */
+  cumu = 0;
+  t1 = silc_timer_tick(timer, FALSE);
+  t2 = silc_timer_tick(timer, FALSE);
+  cumu += (t2 - t1);
+  t1 = silc_timer_tick(timer, FALSE);
+  t2 = silc_timer_tick(timer, FALSE);
+  cumu += (t2 - t1);
+  t1 = silc_timer_tick(timer, FALSE);
+  t2 = silc_timer_tick(timer, FALSE);
+  cumu += (t2 - t1);
+
+  timer->sync_tdiff = cumu / 3;
+
+  t1 = silc_timer_tick(timer, FALSE);
+  t2 = silc_timer_tick(timer, TRUE);
+  timer->sync_tdiff += (int)(t2 - t1);
+}
+
+/****f* silcutil/silc_usleep
+ *
+ * SYNOPSIS
+ *
+ *    void silc_usleep(unsigned long microseconds);
+ *
+ * DESCRIPTION
+ *
+ *    Delays the execution of process/thread for the specified amount of
+ *    time, which is in microseconds.
+ *
+ * NOTES
+ *
+ *    The delay is only approximate and on some platforms the resolution is
+ *    in fact milliseconds.
+ *
+ ***/
+void silc_usleep(unsigned long microseconds);
+
 #endif /* SILCTIMER_H */