Moved silc_usleep from inline function to source file.
[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 /****s* silcutil/SilcTimerStruct
57  *
58  * NAME
59  *
60  *    typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
61  *
62  * DESCRIPTION
63  *
64  *    The timer context.  The context is given as argument to all
65  *    silc_timer_* functions.
66  *
67  ***/
68 typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
69
70 /****f* silcutil/silc_timer_start
71  *
72  * SYNOPSIS
73  *
74  *    SilcBool silc_timer_start(SilcTimer timer);
75  *
76  * DESCRIPTION
77  *
78  *    Starts the timer.  If the timer is already running this will reset
79  *    the timer and continue.
80  *
81  * EXAMPLE
82  *
83  *    SilcTimerStruct timer;
84  *
85  *    silc_timer_start(&timer);
86  *    ... time passes ...
87  *    silc_timer_stop(&timer);
88  *    silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
89  *
90  ***/
91 void silc_timer_start(SilcTimer timer);
92
93 /****f* silcutil/silc_timer_stop
94  *
95  * SYNOPSIS
96  *
97  *    void silc_timer_stop(SilcTimer timer);
98  *
99  * DESCRIPTION
100  *
101  *    Stop the timer.  The elapsed time can be retrieved by calling the
102  *    silc_timer_value function.
103  *
104  ***/
105 void silc_timer_stop(SilcTimer timer);
106
107 /****f* silcutil/silc_timer_continue
108  *
109  * SYNOPSIS
110  *
111  *    void silc_timer_continue(SilcTimer timer);
112  *
113  * DESCRIPTION
114  *
115  *    Continue stopped timer.  If timer is running already this does nothing.
116  *
117  ***/
118 void silc_timer_continue(SilcTimer timer);
119
120 /****f* silcutil/silc_timer_value
121  *
122  * SYNOPSIS
123  *
124  *    void silc_timer_value(SilcTimer timer,
125  *                          SilcUInt64 *elapsed_time_seconds,
126  *                          SilcUInt32 *elapsed_time_microseconds);
127  *
128  * DESCRIPTION
129  *
130  *    Returns either the current value or the end value of the timer.  If the
131  *    timer is currently running this returns the currently elapsed time.  If
132  *    the timer is stopped this returns the cumulative elapsed time.
133  *
134  ***/
135 void silc_timer_value(SilcTimer timer,
136                       SilcUInt64 *elapsed_time_seconds,
137                       SilcUInt32 *elapsed_time_microseconds);
138
139 /****f* silcutil/silc_timer_value_time
140  *
141  * SYNOPSIS
142  *
143  *    void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
144  *
145  * DESCRIPTION
146  *
147  *    Same as silc_timer_value but returns the elapsed time to `ret_time'
148  *    SilcTime structure as absolute date and time.  This is useful if the
149  *    returned time needs to be converted into some other format such as
150  *    time and date strings.
151  *
152  ***/
153 void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
154
155 /****f* silcutil/silc_timer_start_time
156  *
157  * SYNOPSIS
158  *
159  *    void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
160  *
161  * DESCRIPTION
162  *
163  *    Returns the timer's start time into `ret_start_time' SilcTime structure.
164  *
165  ***/
166 void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
167
168 /****f* silcutil/silc_timer_is_running
169  *
170  * SYNOPSIS
171  *
172  *    SilcBool silc_timer_is_running(SilcTimer timer);
173  *
174  * DESCRIPTION
175  *
176  *    Returns TRUE if the timer is currently running, FALSE otherwise.
177  *
178  ***/
179 SilcBool silc_timer_is_running(SilcTimer timer);
180
181 #include "silctimer_i.h"
182
183 /****f* silcutil/silc_timer_tick
184  *
185  * SYNOPSIS
186  *
187  *    SilcUInt64 silc_timer_tick(SilcTimer &timer, SilcBool adjust)
188  *
189  * DESCRIPTION
190  *
191  *    Returns the current CPU tick count.  You should call the
192  *    silc_timer_synchronize before using this function to make sure the
193  *    overhead of measuring the CPU tick count is not included in the
194  *    tick count.  If the `adjust' is TRUE and the silc_timer_synchronize
195  *    has been called the returned value is adjusted to be more accurate.
196  *
197  * EXAMPLE
198  *
199  *    // Synchronize timer for more accurate CPU tick counts
200  *    silc_timer_synchronize(&timer);
201  *    start = silc_timer_tick(&timer, FALSE);
202  *    do_something();
203  *    stop = silc_timer_tick(&timer, TRUE);
204  *
205  ***/
206
207 static inline
208 SilcUInt64 silc_timer_tick(SilcTimer timer, SilcBool adjust)
209 {
210 #if defined(__GNUC__) || defined(__ICC)
211 #if defined(SILC_I486)
212   SilcUInt64 x;
213   asm volatile ("rdtsc" : "=A" (x));
214   return adjust ? x - timer->sync_tdiff : x;
215
216 #elif defined(SILC_X86_64)
217   SilcUInt64 x;
218   SilcUInt32 hi, lo;
219   asm volatile ("rdtsc" : "=a" (lo), "=d" (hi));
220   x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
221   return adjust ? x - timer->sync_tdiff : x;
222
223 #elif defined(SILC_POWERPC)
224   SilcUInt32 hi, lo, tmp;
225   asm volatile ("0:            \n\t"
226                 "mftbu   %0    \n\t"
227                 "mftb    %1    \n\t"
228                 "mftbu   %2    \n\t"
229                 "cmpw    %2,%0 \n\t"
230                 "bne     0b    \n"
231                 : "=r" (hi), "=r" (lo), "=r" (tmp));
232   x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
233   return adjust ? x - timer->sync_tdiff : x;
234
235 #else
236   return 0;
237 #endif /* SILC_I486 */
238
239 #elif defined(SILC_WIN32)
240   __asm rdtsc
241
242 #else
243   return 0;
244 #endif /* __GNUC__ || __ICC */
245 }
246
247 /****f* silcutil/silc_timer_synchronize
248  *
249  * SYNOPSIS
250  *
251  *    void silc_timer_synchronize(SilcTimer timer);
252  *
253  * DESCRIPTION
254  *
255  *    Synchronizes the `timer'.  This call will attempt to synchronize the
256  *    timer for more accurate results with high resolution timing.  Call
257  *    this before you start using the `timer'.
258  *
259  * EXAMPLE
260  *
261  *    // Synchronize timer
262  *    silc_timer_synchronize(&timer);
263  *    silc_timer_start(&timer);
264  *    ... time passes ...
265  *    silc_timer_stop(&timer);
266  *    silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
267  *
268  ***/
269
270 static inline
271 void silc_timer_synchronize(SilcTimer timer)
272 {
273   SilcUInt32 tdiff, cumu, i;
274   SilcUInt64 t1, t2;
275
276   /* Sync normal timer */
277   for (i = 0, cumu = 0; i < 5; i++) {
278     silc_timer_start(timer);
279     silc_timer_stop(timer);
280     silc_timer_value(timer, NULL, &tdiff);
281     cumu += (int)tdiff;
282   }
283
284   timer->sync_diff = cumu;
285   if (timer->sync_diff > 5)
286     timer->sync_diff /= 5;
287
288   /* Sync CPU tick count */
289   cumu = 0;
290   t1 = silc_timer_tick(timer, FALSE);
291   t2 = silc_timer_tick(timer, FALSE);
292   cumu += (t2 - t1);
293   t1 = silc_timer_tick(timer, FALSE);
294   t2 = silc_timer_tick(timer, FALSE);
295   cumu += (t2 - t1);
296   t1 = silc_timer_tick(timer, FALSE);
297   t2 = silc_timer_tick(timer, FALSE);
298   cumu += (t2 - t1);
299
300   timer->sync_tdiff = cumu / 3;
301
302   t1 = silc_timer_tick(timer, FALSE);
303   t2 = silc_timer_tick(timer, TRUE);
304   timer->sync_tdiff += (int)(t2 - t1);
305 }
306
307 /****f* silcutil/silc_usleep
308  *
309  * SYNOPSIS
310  *
311  *    void silc_usleep(unsigned long microseconds);
312  *
313  * DESCRIPTION
314  *
315  *    Delays the execution of process/thread for the specified amount of
316  *    time, which is in microseconds.
317  *
318  * NOTES
319  *
320  *    The delay is only approximate and on some platforms the resolution is
321  *    in fact milliseconds.
322  *
323  ***/
324 void silc_usleep(unsigned long microseconds);
325
326 #endif /* SILCTIMER_H */