Support VERSION_REVTYPE git builds on cleanup_checkout.sh
[freeciv.git] / utility / timing.c
blobfed9c0a53862c56a1d5f65594f156bd1eee5957c
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 /**********************************************************************
15 Measuring times; original author: David Pfitzner <dwp@mso.anu.edu.au>
17 We assume we have at least ANSI/ISO C timing functions, so
18 that we can use:
19 clock_t clock() for CPU times
20 time_t time() for user-time
21 If we have HAVE_GETTIMEOFDAY we use gettimeofday() for user-time
22 to get (usually) better resolution than time().
24 As well as measuring single time intervals, these functions
25 support accumulating the time from multiple separate intervals.
27 Notice the struct timer is an opaque type: modules outside timing.c
28 can only use it as a pointer (cf FILE type). This is done for two
29 main reasons:
31 1. General principle of data hiding and encapsulation
33 2. Means we don't have to include fc_config.h and possibly system
34 specific header files in timing.h. Such stuff is confined
35 inside timing.c.
37 However there is a disadvantage: any code using a timer must do
38 memory allocation and deallocation for it. Some of the functions
39 below are intended to make this reasonably convenient; see function
40 comments.
41 ***********************************************************************/
43 #ifdef HAVE_CONFIG_H
44 #include <fc_config.h>
45 #endif
47 #include <time.h>
49 #ifdef HAVE_GETTIMEOFDAY
50 #include <sys/time.h>
51 #include <unistd.h>
52 #endif
54 #ifdef HAVE_FTIME
55 # include <sys/timeb.h>
56 #endif
58 /* utility */
59 #include "log.h"
60 #include "mem.h"
61 #include "shared.h" /* TRUE, FALSE */
62 #include "support.h"
64 #include "timing.h"
66 #ifndef CLOCKS_PER_SEC
67 #ifdef CLOCKS_PER_SECOND
68 #define CLOCKS_PER_SEC CLOCKS_PER_SECOND
69 #else
70 #define CLOCKS_PER_SEC 1000000 /* wild guess!! */
71 #endif
72 #endif
74 #define N_USEC_PER_SEC 1000000L /* not 1000! :-) */
76 enum timer_state {
77 TIMER_STARTED,
78 TIMER_STOPPED
81 struct timer {
82 /* type: */
83 enum timer_timetype type;
84 enum timer_use use;
85 enum timer_state state;
87 /* this is accumulated time for previous timings: */
88 double sec;
89 long usec; /* not always used, in which case zero,
90 or if used may be negative, but >= -1000000 */
92 /* this is start of current timing, if state==TIMER_STARTED: */
93 union {
94 clock_t c;
95 #ifdef HAVE_GETTIMEOFDAY
96 struct timeval tv;
97 #elif HAVE_FTIME
98 struct timeb tp;
99 #else
100 time_t t;
101 #endif
102 } start;
105 /**********************************************************************
106 Report if clock() returns -1, but only the first time.
107 Ignore this timer from now on.
108 ***********************************************************************/
109 static void report_clock_failed(struct timer *t)
111 static bool first = TRUE;
113 if (first) {
114 log_test("clock() returned -1, ignoring timer");
115 first = FALSE;
117 t->use = TIMER_IGNORE;
120 #ifdef HAVE_GETTIMEOFDAY
121 /**********************************************************************
122 Report if gettimeofday() returns -1, but only the first time.
123 Ignore this timer from now on.
124 ***********************************************************************/
125 static void report_gettimeofday_failed(struct timer *t)
127 static bool first = TRUE;
129 if (first) {
130 log_test("gettimeofday() returned -1, ignoring timer");
131 first = FALSE;
133 t->use = TIMER_IGNORE;
135 #elif !defined HAVE_FTIME
136 /**********************************************************************
137 Report if time() returns -1, but only the first time.
138 Ignore this timer from now on.
139 ***********************************************************************/
140 static void report_time_failed(struct timer *t)
142 static bool first = TRUE;
144 if (first) {
145 log_test("time() returned -1, ignoring timer");
146 first = FALSE;
148 t->use = TIMER_IGNORE;
150 #endif
153 /**********************************************************************
154 Allocate a new timer with specified "type" and "use".
155 The timer is created as cleared, and stopped.
156 ***********************************************************************/
157 struct timer *timer_new(enum timer_timetype type, enum timer_use use)
159 return timer_renew(NULL, type, use);
162 /**********************************************************************
163 Allocate a new timer, or reuse t, with specified "type" and "use".
164 The timer is created as cleared, and stopped.
165 If t is NULL, allocate and return a new timer, else
166 just re-initialise t and return t.
167 This is intended to be useful to allocate a static t just once, eg:
169 static struct timer *t = NULL;
170 t = timer_renew(t, TIMER_CPU, TIMER_USE);
171 ... stuff ...
172 log_verbose("That took %g seconds.", timer_read_seconds(t));
173 ... never free t ...
175 ***********************************************************************/
176 struct timer *timer_renew(struct timer *t, enum timer_timetype type,
177 enum timer_use use)
179 if (!t) {
180 t = (struct timer *)fc_malloc(sizeof(struct timer));
182 t->type = type;
183 t->use = use;
184 timer_clear(t);
185 return t;
188 /**********************************************************************
189 Free the memory associated with a timer.
190 ***********************************************************************/
191 void timer_destroy(struct timer *t)
193 if (t != NULL) {
194 free(t);
198 /**********************************************************************
199 Return whether timer is in use.
200 t may be NULL, in which case returns 0
201 ***********************************************************************/
202 bool timer_in_use(struct timer *t)
204 return (t && t->use != TIMER_IGNORE);
207 /**********************************************************************
208 Reset accumulated time to zero, and stop timer if going.
209 That is, this may be called whether t is started or stopped;
210 in either case the timer is in the stopped state after this function.
211 ***********************************************************************/
212 void timer_clear(struct timer *t)
214 fc_assert_ret(NULL != t);
215 t->state = TIMER_STOPPED;
216 t->sec = 0.0;
217 t->usec = 0;
220 /**********************************************************************
221 Start timing, adding to previous accumulated time if timer has not
222 been cleared. A warning is printed if the timer is already started.
223 ***********************************************************************/
224 void timer_start(struct timer *t)
226 fc_assert_ret(NULL != t);
228 if (t->use == TIMER_IGNORE) {
229 return;
231 if (t->state == TIMER_STARTED) {
232 log_error("tried to start already started timer");
233 return;
235 if (t->type == TIMER_CPU) {
236 t->start.c = clock();
237 if (t->start.c == (clock_t) -1) {
238 report_clock_failed(t);
239 return;
241 } else {
242 #ifdef HAVE_GETTIMEOFDAY
243 int ret = gettimeofday(&t->start.tv, NULL);
244 if (ret == -1) {
245 report_gettimeofday_failed(t);
246 return;
248 #elif defined HAVE_FTIME
249 ftime(&t->start.tp);
250 #else
251 t->start.t = time(NULL);
252 if (t->start.t == (time_t) -1) {
253 report_time_failed(t);
254 return;
256 #endif
258 t->state = TIMER_STARTED;
261 /**********************************************************************
262 Stop timing, and accumulate time so far.
263 (The current time is stored in t->start, so that timer_read_seconds
264 can call this to take a point reading if the timer is active.)
265 A warning is printed if the timer is already stopped.
266 ***********************************************************************/
267 void timer_stop(struct timer *t)
269 fc_assert_ret(NULL != t);
271 if (t->use == TIMER_IGNORE) {
272 return;
274 if (t->state == TIMER_STOPPED) {
275 log_error("tried to stop already stopped timer");
276 return;
278 if (t->type == TIMER_CPU) {
279 clock_t now = clock();
280 if (now == (clock_t) -1) {
281 report_clock_failed(t);
282 return;
284 t->sec += (now - t->start.c) / (double)CLOCKS_PER_SEC;
285 t->start.c = now;
286 } else {
287 #ifdef HAVE_GETTIMEOFDAY
288 struct timeval now;
289 int ret = gettimeofday(&now, NULL);
290 if (ret == -1) {
291 report_gettimeofday_failed(t);
292 return;
294 t->usec += (now.tv_usec - t->start.tv.tv_usec);
295 t->sec += (now.tv_sec - t->start.tv.tv_sec);
296 if (t->usec < 0) {
297 t->usec += N_USEC_PER_SEC;
298 t->sec -= 1.0;
299 } else if (t->usec >= N_USEC_PER_SEC) {
300 long sec = t->usec / N_USEC_PER_SEC;
301 t->sec += sec;
302 t->usec -= sec * N_USEC_PER_SEC;
304 t->start.tv = now;
305 #elif defined HAVE_FTIME
306 struct timeb now;
308 ftime(&now);
309 t->usec += 1000 * ((long)now.millitm - (long)t->start.tp.millitm);
310 t->sec += now.time - t->start.tp.time;
311 if (t->usec < 0) {
312 t->usec += N_USEC_PER_SEC;
313 t->sec -= 1.0;
314 } else if (t->usec >= N_USEC_PER_SEC) {
315 long sec = t->usec / N_USEC_PER_SEC;
316 t->sec += sec;
317 t->usec -= sec * N_USEC_PER_SEC;
319 t->start.tp = now;
320 #else
321 time_t now = time(NULL);
322 if (now == (time_t) -1) {
323 report_time_failed(t);
324 return;
326 t->sec += difftime(now, t->start.t);
327 t->start.t = now;
328 #endif
330 t->state = TIMER_STOPPED;
333 /**********************************************************************
334 Read value from timer. If the timer is not stopped, this stops the
335 timer, reads it (and accumulates), and then restarts it.
336 Returns 0.0 for unused timers.
337 ***********************************************************************/
338 double timer_read_seconds(struct timer *t)
340 fc_assert_ret_val(NULL != t, -1.0);
342 if (t->use == TIMER_IGNORE) {
343 return 0.0;
345 if (t->state == TIMER_STARTED) {
346 timer_stop(t);
347 t->state = TIMER_STARTED;
349 return t->sec + t->usec / (double)N_USEC_PER_SEC;
352 /**********************************************************************
353 Sleeps until the given number of microseconds have elapsed since the
354 timer was started. Leaves the timer running.
355 Must be called with an active, running user timer.
356 (If timer is broken or in wrong state, just sleep for entire interval.)
357 ***********************************************************************/
358 void timer_usleep_since_start(struct timer *t, long usec)
360 #ifdef HAVE_GETTIMEOFDAY
361 int ret;
362 struct timeval tv_now;
363 long elapsed_usec;
364 long wait_usec;
366 fc_assert_ret(NULL != t);
368 ret = gettimeofday(&tv_now, NULL);
370 if ((ret == -1) ||
371 (t->type != TIMER_USER) ||
372 (t->use != TIMER_ACTIVE) ||
373 (t->state != TIMER_STARTED)) {
374 fc_usleep(usec);
375 return;
378 elapsed_usec =
379 (tv_now.tv_sec - t->start.tv.tv_sec) * N_USEC_PER_SEC +
380 (tv_now.tv_usec - t->start.tv.tv_usec);
381 wait_usec = usec - elapsed_usec;
383 if (wait_usec > 0)
384 fc_usleep(wait_usec);
385 #elif HAVE_FTIME
386 struct timeb now;
387 long elapsed_usec, wait_usec;
389 ftime(&now);
391 elapsed_usec = (now.time - t->start.tp.time) * N_USEC_PER_SEC
392 + (now.millitm - t->start.tp.millitm);
393 wait_usec = usec - elapsed_usec;
395 if (wait_usec > 0) {
396 fc_usleep(wait_usec);
398 #else
399 fc_usleep(usec);
400 #endif