Added SILC Thread Queue API
[silc.git] / lib / silcutil / silctimer.h
1 /*
2
3   silctimer.h
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 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/SILC 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  * EXAMPLE
28  *
29  * SilcTimerStruct timer;
30  *
31  * silc_timer_start(&timer);
32  * ... time passes ...
33  * silc_timer_stop(&timer);
34  * silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
35  *
36  ***/
37
38 #ifndef SILCTIMER_H
39 #define SILCTIMER_H
40
41 /****s* silcutil/SilcTimerAPI/SilcTimer
42  *
43  * NAME
44  *
45  *    typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
46  *
47  * DESCRIPTION
48  *
49  *    The timer context.  The context is given as argument to all
50  *    silc_timer_* functions.
51  *
52  ***/
53 typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
54
55 /****f* silcutil/SilcTimerAPI/silc_timer_start
56  *
57  * SYNOPSIS
58  *
59  *    SilcBool silc_timer_start(SilcTimer timer);
60  *
61  * DESCRIPTION
62  *
63  *    Starts the timer.  If the timer is already running this will reset
64  *    the timer and continue.
65  *
66  * EXAMPLE
67  *
68  *    SilcTimerStruct timer;
69  *
70  *    silc_timer_start(&timer);
71  *    ... time passes ...
72  *    silc_timer_stop(&timer);
73  *    silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
74  *
75  ***/
76 void silc_timer_start(SilcTimer timer);
77
78 /****f* silcutil/SilcTimerAPI/silc_timer_stop
79  *
80  * SYNOPSIS
81  *
82  *    void silc_timer_stop(SilcTimer timer);
83  *
84  * DESCRIPTION
85  *
86  *    Stop the timer.  The elapsed time can be retrieved by calling the
87  *    silc_timer_value function.
88  *
89  ***/
90 void silc_timer_stop(SilcTimer timer);
91
92 /****f* silcutil/SilcTimerAPI/silc_timer_continue
93  *
94  * SYNOPSIS
95  *
96  *    void silc_timer_continue(SilcTimer timer);
97  *
98  * DESCRIPTION
99  *
100  *    Continue stopped timer.  If timer is running already this does nothing.
101  *
102  ***/
103 void silc_timer_continue(SilcTimer timer);
104
105 /****f* silcutil/SilcTimerAPI/silc_timer_value
106  *
107  * SYNOPSIS
108  *
109  *    void silc_timer_value(SilcTimer timer,
110  *                          SilcUInt64 *elapsed_time_seconds,
111  *                          SilcUInt32 *elapsed_time_microseconds);
112  *
113  * DESCRIPTION
114  *
115  *    Returns either the current value or the end value of the timer.  If the
116  *    timer is currently running this returns the currently elapsed time.  If
117  *    the timer is stopped this returns the cumulative elapsed time.
118  *
119  ***/
120 void silc_timer_value(SilcTimer timer,
121                       SilcUInt64 *elapsed_time_seconds,
122                       SilcUInt32 *elapsed_time_microseconds);
123
124 /****f* silcutil/SilcTimerAPI/silc_timer_value_time
125  *
126  * SYNOPSIS
127  *
128  *    void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
129  *
130  * DESCRIPTION
131  *
132  *    Same as silc_timer_value but returns the elapsed time to `ret_time'
133  *    SilcTime structure as absolute date and time.  This is useful if the
134  *    returned time needs to be converted into some other format such as
135  *    time and date strings.
136  *
137  ***/
138 void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
139
140 /****f* silcutil/SilcTimerAPI/silc_timer_start_time
141  *
142  * SYNOPSIS
143  *
144  *    void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
145  *
146  * DESCRIPTION
147  *
148  *    Returns the timer's start time into `ret_start_time' SilcTime structure.
149  *
150  ***/
151 void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
152
153 /****f* silcutil/SilcTimerAPI/silc_timer_is_running
154  *
155  * SYNOPSIS
156  *
157  *    SilcBool silc_timer_is_running(SilcTimer timer);
158  *
159  * DESCRIPTION
160  *
161  *    Returns TRUE if the timer is currently running, FALSE otherwise.
162  *
163  ***/
164 SilcBool silc_timer_is_running(SilcTimer timer);
165
166 #include "silctimer_i.h"
167
168 /****f* silcutil/SilcTimerAPI/silc_timer_tick
169  *
170  * SYNOPSIS
171  *
172  *    SilcUInt64 silc_timer_tick(SilcTimer &timer, SilcBool adjust)
173  *
174  * DESCRIPTION
175  *
176  *    Returns the current CPU tick count.  You should call the
177  *    silc_timer_synchronize before using this function to make sure the
178  *    overhead of measuring the CPU tick count is not included in the
179  *    tick count.  If the `adjust' is TRUE and the silc_timer_synchronize
180  *    has been called the returned value is adjusted to be more accurate.
181  *
182  * EXAMPLES
183  *
184  *    // Synchronize timer for more accurate CPU tick counts
185  *    silc_timer_synchronize(&timer);
186  *    start = silc_timer_tick(&timer, FALSE);
187  *    do_something();
188  *    stop = silc_timer_tick(&timer, TRUE);
189  *
190  ***/
191
192 static inline
193 SilcUInt64 silc_timer_tick(SilcTimer timer, SilcBool adjust)
194 {
195 #if defined(__GNUC__) || defined(__ICC)
196 #if defined(SILC_I486)
197   SilcUInt64 x;
198   asm volatile ("rdtsc" : "=A" (x));
199   return adjust ? x - timer->sync_tdiff : x;
200
201 #elif defined(SILC_X86_64)
202   SilcUInt64 x;
203   SilcUInt32 hi, lo;
204   asm volatile ("rdtsc" : "=a" (lo), "=d" (hi));
205   x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
206   return adjust ? x - timer->sync_tdiff : x;
207
208 #elif defined(SILC_POWERPC)
209   SilcUInt32 hi, lo, tmp;
210   asm volatile ("0:            \n\t"
211                 "mftbu   %0    \n\t"
212                 "mftb    %1    \n\t"
213                 "mftbu   %2    \n\t"
214                 "cmpw    %2,%0 \n\t"
215                 "bne     0b    \n"
216                 : "=r" (hi), "=r" (lo), "=r" (tmp));
217   x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
218   return adjust ? x - timer->sync_tdiff : x;
219 #endif /* SILC_I486 */
220
221 #elif defined(SILC_WIN32)
222   __asm rdtsc
223
224 #else
225   return 0;
226 #endif /* __GNUC__ || __ICC */
227 }
228
229 /****f* silcutil/SilcTimerAPI/silc_timer_synchronize
230  *
231  * SYNOPSIS
232  *
233  *    void silc_timer_synchronize(SilcTimer timer);
234  *
235  * DESCRIPTION
236  *
237  *    Synchronizes the `timer'.  This call will attempt to synchronize the
238  *    timer for more accurate results with high resolution timing.  Call
239  *    this before you start using the `timer'.
240  *
241  * EXAMPLE
242  *
243  *    // Synchronize timer
244  *    silc_timer_synchronize(&timer);
245  *    silc_timer_start(&timer);
246  *    ... time passes ...
247  *    silc_timer_stop(&timer);
248  *    silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
249  *
250  ***/
251
252 static inline
253 void silc_timer_synchronize(SilcTimer timer)
254 {
255   SilcUInt32 tdiff, cumu, i;
256   SilcUInt64 t1, t2;
257
258   /* Sync normal timer */
259   for (i = 0, cumu = 0; i < 5; i++) {
260     silc_timer_start(timer);
261     silc_timer_stop(timer);
262     silc_timer_value(timer, NULL, &tdiff);
263     cumu += (int)tdiff;
264   }
265
266   timer->sync_diff = cumu;
267   if (timer->sync_diff > 5)
268     timer->sync_diff /= 5;
269
270   /* Sync CPU tick count */
271   cumu = 0;
272   t1 = silc_timer_tick(timer, FALSE);
273   t2 = silc_timer_tick(timer, FALSE);
274   cumu += (t2 - t1);
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
282   timer->sync_tdiff = cumu / 3;
283
284   t1 = silc_timer_tick(timer, FALSE);
285   t2 = silc_timer_tick(timer, TRUE);
286   timer->sync_tdiff += (int)(t2 - t1);
287 }
288
289 #endif /* SILCTIMER_H */