Use Posix monotonic clocks instead of gettimeofday when available.
[platform/upstream/glib.git] / glib / gtimer.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GLib Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 /* 
28  * MT safe
29  */
30
31 #include "config.h"
32 #include "glibconfig.h"
33
34 #include <stdlib.h>
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif /* HAVE_UNISTD_H */
39
40 #ifndef G_OS_WIN32
41 #include <sys/time.h>
42 #include <time.h>
43 #include <errno.h>
44 #endif /* G_OS_WIN32 */
45
46 #ifdef G_OS_WIN32
47 #include <windows.h>
48 #endif /* G_OS_WIN32 */
49
50 #include "glib.h"
51 #include "galias.h"
52
53 #define G_NSEC_PER_SEC 1000000000
54
55 struct _GTimer
56 {
57 #ifdef G_OS_WIN32
58   guint64 start;
59   guint64 end;
60 #elif HAVE_CLOCK_GETTIME 
61   struct timespec start;
62   struct timespec end;
63   gint clock;
64 #else /* uses gettimeofday */
65   struct timeval start;
66   struct timeval end;
67 #endif 
68
69   guint active : 1;
70 };
71
72 #ifdef G_OS_WIN32
73 #  define GETTIME(v)                            \
74      GetSystemTimeAsFileTime ((FILETIME *)&v)
75 #elif HAVE_CLOCK_GETTIME
76 #  define GETTIME(v)                            \
77      clock_gettime (posix_clock, &v)
78 #else
79 #  define GETTIME(v)                            \
80      gettimeofday (&v, NULL)
81 #endif
82
83 #ifdef HAVE_CLOCK_GETTIME
84 static gint posix_clock = 0;
85
86 static void
87 init_posix_clock (void)
88 {
89   static gboolean initialized = FALSE;
90
91   if (!initialized)
92     {
93       initialized = TRUE;
94 #if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK >= 0
95       if (sysconf (_SC_MONOTONIC_CLOCK) >= 0)
96         posix_clock = CLOCK_MONOTONIC;
97       else
98 #endif
99         posix_clock = CLOCK_REALTIME;
100     }
101 }
102 #endif
103
104 GTimer*
105 g_timer_new (void)
106 {
107   GTimer *timer;
108
109   timer = g_new (GTimer, 1);
110   timer->active = TRUE;
111
112 #ifdef HAVE_CLOCK_GETTIME
113   init_posix_clock ();
114 #endif
115
116   GETTIME (timer->start);
117
118   return timer;
119 }
120
121 void
122 g_timer_destroy (GTimer *timer)
123 {
124   g_return_if_fail (timer != NULL);
125
126   g_free (timer);
127 }
128
129 void
130 g_timer_start (GTimer *timer)
131 {
132   g_return_if_fail (timer != NULL);
133
134   timer->active = TRUE;
135
136   GETTIME (timer->start);
137 }
138
139 void
140 g_timer_stop (GTimer *timer)
141 {
142   g_return_if_fail (timer != NULL);
143
144   timer->active = FALSE;
145
146   GETTIME(timer->end);
147 }
148
149 void
150 g_timer_reset (GTimer *timer)
151 {
152   g_return_if_fail (timer != NULL);
153
154   GETTIME (timer->start);
155 }
156
157 void
158 g_timer_continue (GTimer *timer)
159 {
160 #ifdef G_OS_WIN32
161   guint64 elapsed;
162 #elif HAVE_CLOCK_GETTIME
163   struct timespec elapsed;
164 #else
165   struct timeval elapsed;
166 #endif
167
168   g_return_if_fail (timer != NULL);
169   g_return_if_fail (timer->active == FALSE);
170
171   /* Get elapsed time and reset timer start time
172    *  to the current time minus the previously
173    *  elapsed interval.
174    */
175
176 #ifdef G_OS_WIN32
177
178   elapsed = timer->end - timer->start;
179
180   GETTIME (timer->start);
181
182   timer->start -= elapsed;
183
184 #elif HAVE_CLOCK_GETTIME
185
186   if (timer->start.tv_nsec > timer->end.tv_nsec)
187     {
188       timer->end.tv_nsec += G_NSEC_PER_SEC;
189       timer->end.tv_sec--;
190     }
191
192   elapsed.tv_nsec = timer->end.tv_nsec - timer->start.tv_nsec;
193   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
194
195   GETTIME (timer->start);
196
197   if (timer->start.tv_nsec < elapsed.tv_nsec)
198     {
199       timer->start.tv_nsec += G_NSEC_PER_SEC;
200       timer->start.tv_sec--;
201     }
202
203   timer->start.tv_nsec -= elapsed.tv_nsec;
204   timer->start.tv_sec -= elapsed.tv_sec;
205
206 #else
207
208   if (timer->start.tv_usec > timer->end.tv_usec)
209     {
210       timer->end.tv_usec += G_USEC_PER_SEC;
211       timer->end.tv_sec--;
212     }
213
214   elapsed.tv_usec = timer->end.tv_usec - timer->start.tv_usec;
215   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
216
217   GETTIME (timer->start);
218
219   if (timer->start.tv_usec < elapsed.tv_usec)
220     {
221       timer->start.tv_usec += G_USEC_PER_SEC;
222       timer->start.tv_sec--;
223     }
224
225   timer->start.tv_usec -= elapsed.tv_usec;
226   timer->start.tv_sec -= elapsed.tv_sec;
227
228 #endif /* !G_OS_WIN32 */
229
230   timer->active = TRUE;
231 }
232
233 gdouble
234 g_timer_elapsed (GTimer *timer,
235                  gulong *microseconds)
236 {
237   gdouble total;
238 #ifdef G_OS_WIN32
239   gint64 elapsed;
240 #elif HAVE_CLOCK_GETTIME
241   struct timespec elapsed;
242 #else
243   struct timeval elapsed;
244 #endif 
245
246   g_return_val_if_fail (timer != NULL, 0);
247
248 #ifdef G_OS_WIN32
249   if (timer->active)
250     GETTIME (timer->end);
251
252   elapsed = timer->end - timer->start;
253
254   total = elapsed / 1e7;
255
256   if (microseconds)
257     *microseconds = (elapsed / 10) % 1000000;
258 #elif HAVE_CLOCK_GETTIME
259   if (timer->active)
260     GETTIME (timer->end);
261
262   if (timer->start.tv_nsec > timer->end.tv_nsec)
263     {
264       timer->end.tv_nsec += G_NSEC_PER_SEC;
265       timer->end.tv_sec--;
266     }
267
268   elapsed.tv_nsec = timer->end.tv_nsec - timer->start.tv_nsec;
269   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
270
271   total = elapsed.tv_sec + ((gdouble) elapsed.tv_nsec / (gdouble) G_NSEC_PER_SEC);
272   if (total < 0)
273     {
274       total = 0;
275
276       if (microseconds)
277         *microseconds = 0;
278     }
279   else if (microseconds)
280     *microseconds = elapsed.tv_nsec / 1000;
281
282 #else
283   if (timer->active)
284     GETTIME (timer->end);
285
286   if (timer->start.tv_usec > timer->end.tv_usec)
287     {
288       timer->end.tv_usec += G_USEC_PER_SEC;
289       timer->end.tv_sec--;
290     }
291
292   elapsed.tv_usec = timer->end.tv_usec - timer->start.tv_usec;
293   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
294
295   total = elapsed.tv_sec + ((gdouble) elapsed.tv_usec / (gdouble) G_USEC_PER_SEC);
296   if (total < 0)
297     {
298       total = 0;
299
300       if (microseconds)
301         *microseconds = 0;
302     }
303   else if (microseconds)
304     *microseconds = elapsed.tv_usec;
305
306 #endif
307
308   return total;
309 }
310
311 void
312 g_usleep (gulong microseconds)
313 {
314 #ifdef G_OS_WIN32
315   Sleep (microseconds / 1000);
316 #else /* !G_OS_WIN32 */
317 # ifdef HAVE_NANOSLEEP
318   struct timespec request, remaining;
319   request.tv_sec = microseconds / G_USEC_PER_SEC;
320   request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
321   while (nanosleep (&request, &remaining) == -1 && errno == EINTR)
322     request = remaining;
323 # else /* !HAVE_NANOSLEEP */
324   if (g_thread_supported ())
325     {
326       static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
327       static GCond* cond = NULL;
328       GTimeVal end_time;
329       
330       g_get_current_time (&end_time);
331       if (microseconds > G_MAXLONG)
332         {
333           microseconds -= G_MAXLONG;
334           g_time_val_add (&end_time, G_MAXLONG);
335         }
336       g_time_val_add (&end_time, microseconds);
337
338       g_static_mutex_lock (&mutex);
339       
340       if (!cond)
341         cond = g_cond_new ();
342       
343       while (g_cond_timed_wait (cond, g_static_mutex_get_mutex (&mutex), 
344                                 &end_time))
345         /* do nothing */;
346       
347       g_static_mutex_unlock (&mutex);
348     }
349   else
350     {
351       struct timeval tv;
352       tv.tv_sec = microseconds / G_USEC_PER_SEC;
353       tv.tv_usec = microseconds % G_USEC_PER_SEC;
354       select(0, NULL, NULL, NULL, &tv);
355     }
356 # endif /* !HAVE_NANOSLEEP */
357 #endif /* !G_OS_WIN32 */
358 }
359
360 /**
361  * g_time_val_add:
362  * @time_: a #GTimeVal
363  * @microseconds: number of microseconds to add to @time
364  *
365  * Adds the given number of microseconds to @time_. @microseconds can
366  * also be negative to decrease the value of @time_.
367  **/
368 void 
369 g_time_val_add (GTimeVal *time_, glong microseconds)
370 {
371   g_return_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC);
372
373   if (microseconds >= 0)
374     {
375       time_->tv_usec += microseconds % G_USEC_PER_SEC;
376       time_->tv_sec += microseconds / G_USEC_PER_SEC;
377       if (time_->tv_usec >= G_USEC_PER_SEC)
378        {
379          time_->tv_usec -= G_USEC_PER_SEC;
380          time_->tv_sec++;
381        }
382     }
383   else
384     {
385       microseconds *= -1;
386       time_->tv_usec -= microseconds % G_USEC_PER_SEC;
387       time_->tv_sec -= microseconds / G_USEC_PER_SEC;
388       if (time_->tv_usec < 0)
389        {
390          time_->tv_usec += G_USEC_PER_SEC;
391          time_->tv_sec--;
392        }      
393     }
394 }
395
396 /* converts a broken down date representation, relative to UTC, to
397  * a timestamp; it uses timegm() if it's available.
398  */
399 static time_t
400 mktime_utc (struct tm *tm)
401 {
402   time_t retval;
403   
404 #ifndef HAVE_TIMEGM
405   static const gint days_before[] =
406   {
407     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
408   };
409 #endif
410
411 #ifndef HAVE_TIMEGM
412   if (tm->tm_mon < 0 || tm->tm_mon > 11)
413     return (time_t) -1;
414
415   retval = (tm->tm_year - 70) * 365;
416   retval += (tm->tm_year - 68) / 4;
417   retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
418   
419   if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
420     retval -= 1;
421   
422   retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
423 #else
424   retval = timegm (tm);
425 #endif /* !HAVE_TIMEGM */
426   
427   return retval;
428 }
429
430 /**
431  * g_time_val_from_iso8601:
432  * @iso_date: a ISO 8601 encoded date string
433  * @time_: a #GTimeVal
434  *
435  * Converts a string containing an ISO 8601 encoded date and time
436  * to a #GTimeVal and puts it into @time_.
437  *
438  * Return value: %TRUE if the conversion was successful.
439  *
440  * Since: 2.12
441  */
442 gboolean
443 g_time_val_from_iso8601 (const gchar *iso_date,
444                          GTimeVal    *time_)
445 {
446   struct tm tm;
447   long val;
448
449   g_return_val_if_fail (iso_date != NULL, FALSE);
450   g_return_val_if_fail (time_ != NULL, FALSE);
451
452   val = strtoul (iso_date, (char **)&iso_date, 10);
453   if (*iso_date == '-')
454     {
455       /* YYYY-MM-DD */
456       tm.tm_year = val - 1900;
457       iso_date++;
458       tm.tm_mon = strtoul (iso_date, (char **)&iso_date, 10) - 1;
459       
460       if (*iso_date++ != '-')
461         return FALSE;
462       
463       tm.tm_mday = strtoul (iso_date, (char **)&iso_date, 10);
464     }
465   else
466     {
467       /* YYYYMMDD */
468       tm.tm_mday = val % 100;
469       tm.tm_mon = (val % 10000) / 100 - 1;
470       tm.tm_year = val / 10000 - 1900;
471     }
472
473   if (*iso_date++ != 'T')
474     return FALSE;
475   
476   val = strtoul (iso_date, (char **)&iso_date, 10);
477   if (*iso_date == ':')
478     {
479       /* hh:mm:ss */
480       tm.tm_hour = val;
481       iso_date++;
482       tm.tm_min = strtoul (iso_date, (char **)&iso_date, 10);
483       
484       if (*iso_date++ != ':')
485         return FALSE;
486       
487       tm.tm_sec = strtoul (iso_date, (char **)&iso_date, 10);
488     }
489   else
490     {
491       /* hhmmss */
492       tm.tm_sec = val % 100;
493       tm.tm_min = (val % 10000) / 100;
494       tm.tm_hour = val / 10000;
495     }
496
497   time_->tv_sec = mktime_utc (&tm);
498   time_->tv_usec = 1;
499   
500   if (*iso_date == '.')
501     time_->tv_usec = strtoul (iso_date + 1, (char **)&iso_date, 10);
502     
503   if (*iso_date == '+' || *iso_date == '-')
504     {
505       gint sign = (*iso_date == '+') ? -1 : 1;
506       
507       val = 60 * strtoul (iso_date + 1, (char **)&iso_date, 10);
508       
509       if (*iso_date == ':')
510         val = 60 * val + strtoul (iso_date + 1, NULL, 10);
511       else
512         val = 60 * (val / 100) + (val % 100);
513
514       time_->tv_sec += (time_t) (val * sign);
515     }
516
517   return TRUE;
518 }
519
520 /**
521  * g_time_val_to_iso8601:
522  * @time_: a #GTimeVal
523  * 
524  * Converts @time_ into a ISO 8601 encoded string, relative to the
525  * Coordinated Universal Time (UTC).
526  *
527  * Return value: a newly allocated string containing a ISO 8601 date
528  *
529  * Since: 2.12
530  */
531 gchar *
532 g_time_val_to_iso8601 (GTimeVal *time_)
533 {
534   gchar *retval;
535
536   g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
537
538 #define ISO_8601_LEN    21
539 #define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
540   retval = g_new0 (gchar, ISO_8601_LEN + 1);
541   
542   strftime (retval, ISO_8601_LEN,
543             ISO_8601_FORMAT,
544             gmtime (&(time_->tv_sec)));
545   
546   return retval;
547 }
548
549 #define __G_TIMER_C__
550 #include "galiasdef.c"