SILC Runtime Toolkit 1.2 Beta 1
[runtime.git] / lib / silcutil / silctimer.h
1 /*
2
3   silctimer.h
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 - 2008 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 /****h* silcutil/Timer Interface
21  *
22  * DESCRIPTION
23  *
24  * SILC Timer interface provides a simple way to measure time intervals.
25  * The SILC Timer works with microsecond resolution, depending on platform.
26  *
27  * The interface also provides utility functions to get currenet CPU tick
28  * count and to delay process/thread execution.
29  *
30  * EXAMPLE
31  *
32  * SilcTimerStruct timer;
33  *
34  * silc_timer_start(&timer);
35  * ... time passes ...
36  * silc_timer_stop(&timer);
37  * silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
38  *
39  ***/
40
41 #ifndef SILCTIMER_H
42 #define SILCTIMER_H
43
44 /****s* silcutil/SilcTimer
45  *
46  * NAME
47  *
48  *    typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
49  *
50  * DESCRIPTION
51  *
52  *    The timer context.  The context is given as argument to all
53  *    silc_timer_* functions.
54  *
55  ***/
56 typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
57
58 /****f* silcutil/silc_timer_start
59  *
60  * SYNOPSIS
61  *
62  *    SilcBool silc_timer_start(SilcTimer timer);
63  *
64  * DESCRIPTION
65  *
66  *    Starts the timer.  If the timer is already running this will reset
67  *    the timer and continue.
68  *
69  * EXAMPLE
70  *
71  *    SilcTimerStruct timer;
72  *
73  *    silc_timer_start(&timer);
74  *    ... time passes ...
75  *    silc_timer_stop(&timer);
76  *    silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
77  *
78  ***/
79 void silc_timer_start(SilcTimer timer);
80
81 /****f* silcutil/silc_timer_stop
82  *
83  * SYNOPSIS
84  *
85  *    void silc_timer_stop(SilcTimer timer);
86  *
87  * DESCRIPTION
88  *
89  *    Stop the timer.  The elapsed time can be retrieved by calling the
90  *    silc_timer_value function.
91  *
92  ***/
93 void silc_timer_stop(SilcTimer timer);
94
95 /****f* silcutil/silc_timer_continue
96  *
97  * SYNOPSIS
98  *
99  *    void silc_timer_continue(SilcTimer timer);
100  *
101  * DESCRIPTION
102  *
103  *    Continue stopped timer.  If timer is running already this does nothing.
104  *
105  ***/
106 void silc_timer_continue(SilcTimer timer);
107
108 /****f* silcutil/silc_timer_value
109  *
110  * SYNOPSIS
111  *
112  *    void silc_timer_value(SilcTimer timer,
113  *                          SilcUInt64 *elapsed_time_seconds,
114  *                          SilcUInt32 *elapsed_time_microseconds);
115  *
116  * DESCRIPTION
117  *
118  *    Returns either the current value or the end value of the timer.  If the
119  *    timer is currently running this returns the currently elapsed time.  If
120  *    the timer is stopped this returns the cumulative elapsed time.
121  *
122  ***/
123 void silc_timer_value(SilcTimer timer,
124                       SilcUInt64 *elapsed_time_seconds,
125                       SilcUInt32 *elapsed_time_microseconds);
126
127 /****f* silcutil/silc_timer_value_time
128  *
129  * SYNOPSIS
130  *
131  *    void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
132  *
133  * DESCRIPTION
134  *
135  *    Same as silc_timer_value but returns the elapsed time to `ret_time'
136  *    SilcTime structure as absolute date and time.  This is useful if the
137  *    returned time needs to be converted into some other format such as
138  *    time and date strings.
139  *
140  ***/
141 void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
142
143 /****f* silcutil/silc_timer_start_time
144  *
145  * SYNOPSIS
146  *
147  *    void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
148  *
149  * DESCRIPTION
150  *
151  *    Returns the timer's start time into `ret_start_time' SilcTime structure.
152  *
153  ***/
154 void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
155
156 /****f* silcutil/silc_timer_is_running
157  *
158  * SYNOPSIS
159  *
160  *    SilcBool silc_timer_is_running(SilcTimer timer);
161  *
162  * DESCRIPTION
163  *
164  *    Returns TRUE if the timer is currently running, FALSE otherwise.
165  *
166  ***/
167 SilcBool silc_timer_is_running(SilcTimer timer);
168
169 #include "silctimer_i.h"
170
171 /****f* silcutil/silc_timer_tick
172  *
173  * SYNOPSIS
174  *
175  *    SilcUInt64 silc_timer_tick(SilcTimer &timer, SilcBool adjust)
176  *
177  * DESCRIPTION
178  *
179  *    Returns the current CPU tick count.  You should call the
180  *    silc_timer_synchronize before using this function to make sure the
181  *    overhead of measuring the CPU tick count is not included in the
182  *    tick count.  If the `adjust' is TRUE and the silc_timer_synchronize
183  *    has been called the returned value is adjusted to be more accurate.
184  *
185  * EXAMPLE
186  *
187  *    // Synchronize timer for more accurate CPU tick counts
188  *    silc_timer_synchronize(&timer);
189  *    start = silc_timer_tick(&timer, FALSE);
190  *    do_something();
191  *    stop = silc_timer_tick(&timer, TRUE);
192  *
193  ***/
194
195 static inline
196 SilcUInt64 silc_timer_tick(SilcTimer timer, SilcBool adjust)
197 {
198 #if defined(__GNUC__) || defined(__ICC)
199 #if defined(SILC_I486)
200   SilcUInt64 x;
201   asm volatile ("rdtsc" : "=A" (x));
202   return adjust ? x - timer->sync_tdiff : x;
203
204 #elif defined(SILC_X86_64)
205   SilcUInt64 x;
206   SilcUInt32 hi, lo;
207   asm volatile ("rdtsc" : "=a" (lo), "=d" (hi));
208   x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
209   return adjust ? x - timer->sync_tdiff : x;
210
211 #elif defined(SILC_POWERPC)
212   SilcUInt32 hi, lo, tmp;
213   asm volatile ("0:            \n\t"
214                 "mftbu   %0    \n\t"
215                 "mftb    %1    \n\t"
216                 "mftbu   %2    \n\t"
217                 "cmpw    %2,%0 \n\t"
218                 "bne     0b    \n"
219                 : "=r" (hi), "=r" (lo), "=r" (tmp));
220   x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
221   return adjust ? x - timer->sync_tdiff : x;
222 #endif /* SILC_I486 */
223
224 #elif defined(SILC_WIN32)
225   __asm rdtsc
226
227 #else
228   return 0;
229 #endif /* __GNUC__ || __ICC */
230 }
231
232 /****f* silcutil/silc_timer_synchronize
233  *
234  * SYNOPSIS
235  *
236  *    void silc_timer_synchronize(SilcTimer timer);
237  *
238  * DESCRIPTION
239  *
240  *    Synchronizes the `timer'.  This call will attempt to synchronize the
241  *    timer for more accurate results with high resolution timing.  Call
242  *    this before you start using the `timer'.
243  *
244  * EXAMPLE
245  *
246  *    // Synchronize timer
247  *    silc_timer_synchronize(&timer);
248  *    silc_timer_start(&timer);
249  *    ... time passes ...
250  *    silc_timer_stop(&timer);
251  *    silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
252  *
253  ***/
254
255 static inline
256 void silc_timer_synchronize(SilcTimer timer)
257 {
258   SilcUInt32 tdiff, cumu, i;
259   SilcUInt64 t1, t2;
260
261   /* Sync normal timer */
262   for (i = 0, cumu = 0; i < 5; i++) {
263     silc_timer_start(timer);
264     silc_timer_stop(timer);
265     silc_timer_value(timer, NULL, &tdiff);
266     cumu += (int)tdiff;
267   }
268
269   timer->sync_diff = cumu;
270   if (timer->sync_diff > 5)
271     timer->sync_diff /= 5;
272
273   /* Sync CPU tick count */
274   cumu = 0;
275   t1 = silc_timer_tick(timer, FALSE);
276   t2 = silc_timer_tick(timer, FALSE);
277   cumu += (t2 - t1);
278   t1 = silc_timer_tick(timer, FALSE);
279   t2 = silc_timer_tick(timer, FALSE);
280   cumu += (t2 - t1);
281   t1 = silc_timer_tick(timer, FALSE);
282   t2 = silc_timer_tick(timer, FALSE);
283   cumu += (t2 - t1);
284
285   timer->sync_tdiff = cumu / 3;
286
287   t1 = silc_timer_tick(timer, FALSE);
288   t2 = silc_timer_tick(timer, TRUE);
289   timer->sync_tdiff += (int)(t2 - t1);
290 }
291
292 /****f* silcutil/silc_usleep
293  *
294  * SYNOPSIS
295  *
296  *    void silc_usleep(unsigned long microseconds);
297  *
298  * DESCRIPTION
299  *
300  *    Delays the execution of process/thread for the specified amount of
301  *    time, which is in microseconds.
302  *
303  * NOTES
304  *
305  *    The delay is only approximate and on some platforms the resolution is
306  *    in fact milliseconds.
307  *
308  ***/
309 static inline
310 void silc_usleep(unsigned long microseconds)
311 {
312 #ifdef SILC_UNIX
313 #ifdef HAVE_NANOSLEEP
314   struct timespec tv;
315   tv.tv_sec = 0;
316   tv.tv_nsec = microseconds * 1000;
317 #endif /* HAVE_NANOSLEEP */
318 #endif /* SILC_UNIX */
319
320 #ifdef SILC_UNIX
321 #ifdef HAVE_NANOSLEEP
322   nanosleep(&tv, NULL);
323 #else
324   usleep(microseconds);
325 #endif /* HAVE_NANOSLEEP */
326 #endif /* SILC_UNIX */
327 #ifdef SILC_WIN32
328   Sleep(microseconds / 1000);
329 #endif /* SILC_WIN32 */
330 #ifdef SILC_SYMBIAN
331   silc_symbian_usleep(microseconds);
332 #endif /* SILC_SYMBIAN */
333 }
334
335 #endif /* SILCTIMER_H */