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)
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
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
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
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
41 ***********************************************************************/
44 #include <fc_config.h>
49 #ifdef HAVE_GETTIMEOFDAY
55 # include <sys/timeb.h>
61 #include "shared.h" /* TRUE, FALSE */
66 #ifndef CLOCKS_PER_SEC
67 #ifdef CLOCKS_PER_SECOND
68 #define CLOCKS_PER_SEC CLOCKS_PER_SECOND
70 #define CLOCKS_PER_SEC 1000000 /* wild guess!! */
74 #define N_USEC_PER_SEC 1000000L /* not 1000! :-) */
83 enum timer_timetype type
;
85 enum timer_state state
;
87 /* this is accumulated time for previous timings: */
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: */
95 #ifdef HAVE_GETTIMEOFDAY
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
;
114 log_test("clock() returned -1, ignoring timer");
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
;
130 log_test("gettimeofday() returned -1, ignoring timer");
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
;
145 log_test("time() returned -1, ignoring timer");
148 t
->use
= TIMER_IGNORE
;
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);
172 log_verbose("That took %g seconds.", timer_read_seconds(t));
175 ***********************************************************************/
176 struct timer
*timer_renew(struct timer
*t
, enum timer_timetype type
,
180 t
= (struct timer
*)fc_malloc(sizeof(struct timer
));
188 /**********************************************************************
189 Free the memory associated with a timer.
190 ***********************************************************************/
191 void timer_destroy(struct timer
*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
;
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
) {
231 if (t
->state
== TIMER_STARTED
) {
232 log_error("tried to start already started timer");
235 if (t
->type
== TIMER_CPU
) {
236 t
->start
.c
= clock();
237 if (t
->start
.c
== (clock_t) -1) {
238 report_clock_failed(t
);
242 #ifdef HAVE_GETTIMEOFDAY
243 int ret
= gettimeofday(&t
->start
.tv
, NULL
);
245 report_gettimeofday_failed(t
);
248 #elif defined HAVE_FTIME
251 t
->start
.t
= time(NULL
);
252 if (t
->start
.t
== (time_t) -1) {
253 report_time_failed(t
);
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
) {
274 if (t
->state
== TIMER_STOPPED
) {
275 log_error("tried to stop already stopped timer");
278 if (t
->type
== TIMER_CPU
) {
279 clock_t now
= clock();
280 if (now
== (clock_t) -1) {
281 report_clock_failed(t
);
284 t
->sec
+= (now
- t
->start
.c
) / (double)CLOCKS_PER_SEC
;
287 #ifdef HAVE_GETTIMEOFDAY
289 int ret
= gettimeofday(&now
, NULL
);
291 report_gettimeofday_failed(t
);
294 t
->usec
+= (now
.tv_usec
- t
->start
.tv
.tv_usec
);
295 t
->sec
+= (now
.tv_sec
- t
->start
.tv
.tv_sec
);
297 t
->usec
+= N_USEC_PER_SEC
;
299 } else if (t
->usec
>= N_USEC_PER_SEC
) {
300 long sec
= t
->usec
/ N_USEC_PER_SEC
;
302 t
->usec
-= sec
* N_USEC_PER_SEC
;
305 #elif defined HAVE_FTIME
309 t
->usec
+= 1000 * ((long)now
.millitm
- (long)t
->start
.tp
.millitm
);
310 t
->sec
+= now
.time
- t
->start
.tp
.time
;
312 t
->usec
+= N_USEC_PER_SEC
;
314 } else if (t
->usec
>= N_USEC_PER_SEC
) {
315 long sec
= t
->usec
/ N_USEC_PER_SEC
;
317 t
->usec
-= sec
* N_USEC_PER_SEC
;
321 time_t now
= time(NULL
);
322 if (now
== (time_t) -1) {
323 report_time_failed(t
);
326 t
->sec
+= difftime(now
, t
->start
.t
);
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
) {
345 if (t
->state
== TIMER_STARTED
) {
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
362 struct timeval tv_now
;
366 fc_assert_ret(NULL
!= t
);
368 ret
= gettimeofday(&tv_now
, NULL
);
371 (t
->type
!= TIMER_USER
) ||
372 (t
->use
!= TIMER_ACTIVE
) ||
373 (t
->state
!= TIMER_STARTED
)) {
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
;
384 fc_usleep(wait_usec
);
387 long elapsed_usec
, wait_usec
;
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
;
396 fc_usleep(wait_usec
);