4d62a7697d23ff6438ed2baacecd9c02308ad89b
[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 #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_MONOTONIC_CLOCK) 
56 #define USE_CLOCK_GETTIME 1
57 #endif
58
59 struct _GTimer
60 {
61 #ifdef G_OS_WIN32
62   guint64 start;
63   guint64 end;
64 #elif USE_CLOCK_GETTIME 
65   struct timespec start;
66   struct timespec end;
67   gint clock;
68 #else /* uses gettimeofday */
69   struct timeval start;
70   struct timeval end;
71 #endif 
72
73   guint active : 1;
74 };
75
76 #ifdef G_OS_WIN32
77 #  define GETTIME(v)                            \
78      GetSystemTimeAsFileTime ((FILETIME *)&v)
79 #elif USE_CLOCK_GETTIME
80 #  define GETTIME(v)                            \
81      clock_gettime (posix_clock, &v)
82 #else
83 #  define GETTIME(v)                            \
84      gettimeofday (&v, NULL)
85 #endif
86
87 #ifdef USE_CLOCK_GETTIME
88 static gint posix_clock = 0;
89
90 static void
91 init_posix_clock (void)
92 {
93   static gboolean initialized = FALSE;
94
95   if (!initialized)
96     {
97       initialized = TRUE;
98       if (sysconf (_SC_MONOTONIC_CLOCK) >= 0)
99         posix_clock = CLOCK_MONOTONIC;
100       else
101         posix_clock = CLOCK_REALTIME;
102     }
103 }
104 #endif
105
106 GTimer*
107 g_timer_new (void)
108 {
109   GTimer *timer;
110
111   timer = g_new (GTimer, 1);
112   timer->active = TRUE;
113
114 #ifdef USE_CLOCK_GETTIME
115   init_posix_clock ();
116 #endif
117
118   GETTIME (timer->start);
119
120   return timer;
121 }
122
123 void
124 g_timer_destroy (GTimer *timer)
125 {
126   g_return_if_fail (timer != NULL);
127
128   g_free (timer);
129 }
130
131 void
132 g_timer_start (GTimer *timer)
133 {
134   g_return_if_fail (timer != NULL);
135
136   timer->active = TRUE;
137
138   GETTIME (timer->start);
139 }
140
141 void
142 g_timer_stop (GTimer *timer)
143 {
144   g_return_if_fail (timer != NULL);
145
146   timer->active = FALSE;
147
148   GETTIME(timer->end);
149 }
150
151 void
152 g_timer_reset (GTimer *timer)
153 {
154   g_return_if_fail (timer != NULL);
155
156   GETTIME (timer->start);
157 }
158
159 void
160 g_timer_continue (GTimer *timer)
161 {
162 #ifdef G_OS_WIN32
163   guint64 elapsed;
164 #elif USE_CLOCK_GETTIME
165   struct timespec elapsed;
166 #else
167   struct timeval elapsed;
168 #endif
169
170   g_return_if_fail (timer != NULL);
171   g_return_if_fail (timer->active == FALSE);
172
173   /* Get elapsed time and reset timer start time
174    *  to the current time minus the previously
175    *  elapsed interval.
176    */
177
178 #ifdef G_OS_WIN32
179
180   elapsed = timer->end - timer->start;
181
182   GETTIME (timer->start);
183
184   timer->start -= elapsed;
185
186 #elif USE_CLOCK_GETTIME
187
188   if (timer->start.tv_nsec > timer->end.tv_nsec)
189     {
190       timer->end.tv_nsec += G_NSEC_PER_SEC;
191       timer->end.tv_sec--;
192     }
193
194   elapsed.tv_nsec = timer->end.tv_nsec - timer->start.tv_nsec;
195   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
196
197   GETTIME (timer->start);
198
199   if (timer->start.tv_nsec < elapsed.tv_nsec)
200     {
201       timer->start.tv_nsec += G_NSEC_PER_SEC;
202       timer->start.tv_sec--;
203     }
204
205   timer->start.tv_nsec -= elapsed.tv_nsec;
206   timer->start.tv_sec -= elapsed.tv_sec;
207
208 #else
209
210   if (timer->start.tv_usec > timer->end.tv_usec)
211     {
212       timer->end.tv_usec += G_USEC_PER_SEC;
213       timer->end.tv_sec--;
214     }
215
216   elapsed.tv_usec = timer->end.tv_usec - timer->start.tv_usec;
217   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
218
219   GETTIME (timer->start);
220
221   if (timer->start.tv_usec < elapsed.tv_usec)
222     {
223       timer->start.tv_usec += G_USEC_PER_SEC;
224       timer->start.tv_sec--;
225     }
226
227   timer->start.tv_usec -= elapsed.tv_usec;
228   timer->start.tv_sec -= elapsed.tv_sec;
229
230 #endif /* !G_OS_WIN32 */
231
232   timer->active = TRUE;
233 }
234
235 gdouble
236 g_timer_elapsed (GTimer *timer,
237                  gulong *microseconds)
238 {
239   gdouble total;
240 #ifdef G_OS_WIN32
241   gint64 elapsed;
242 #elif USE_CLOCK_GETTIME
243   struct timespec elapsed;
244 #else
245   struct timeval elapsed;
246 #endif 
247
248   g_return_val_if_fail (timer != NULL, 0);
249
250 #ifdef G_OS_WIN32
251   if (timer->active)
252     GETTIME (timer->end);
253
254   elapsed = timer->end - timer->start;
255
256   total = elapsed / 1e7;
257
258   if (microseconds)
259     *microseconds = (elapsed / 10) % 1000000;
260 #elif USE_CLOCK_GETTIME
261   if (timer->active)
262     GETTIME (timer->end);
263
264   if (timer->start.tv_nsec > timer->end.tv_nsec)
265     {
266       timer->end.tv_nsec += G_NSEC_PER_SEC;
267       timer->end.tv_sec--;
268     }
269
270   elapsed.tv_nsec = timer->end.tv_nsec - timer->start.tv_nsec;
271   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
272
273   total = elapsed.tv_sec + ((gdouble) elapsed.tv_nsec / (gdouble) G_NSEC_PER_SEC);
274   if (total < 0)
275     {
276       total = 0;
277
278       if (microseconds)
279         *microseconds = 0;
280     }
281   else if (microseconds)
282     *microseconds = elapsed.tv_nsec / 1000;
283
284 #else
285   if (timer->active)
286     GETTIME (timer->end);
287
288   if (timer->start.tv_usec > timer->end.tv_usec)
289     {
290       timer->end.tv_usec += G_USEC_PER_SEC;
291       timer->end.tv_sec--;
292     }
293
294   elapsed.tv_usec = timer->end.tv_usec - timer->start.tv_usec;
295   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;
296
297   total = elapsed.tv_sec + ((gdouble) elapsed.tv_usec / (gdouble) G_USEC_PER_SEC);
298   if (total < 0)
299     {
300       total = 0;
301
302       if (microseconds)
303         *microseconds = 0;
304     }
305   else if (microseconds)
306     *microseconds = elapsed.tv_usec;
307
308 #endif
309
310   return total;
311 }
312
313 void
314 g_usleep (gulong microseconds)
315 {
316 #ifdef G_OS_WIN32
317   Sleep (microseconds / 1000);
318 #else /* !G_OS_WIN32 */
319 # ifdef HAVE_NANOSLEEP
320   struct timespec request, remaining;
321   request.tv_sec = microseconds / G_USEC_PER_SEC;
322   request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
323   while (nanosleep (&request, &remaining) == -1 && errno == EINTR)
324     request = remaining;
325 # else /* !HAVE_NANOSLEEP */
326 #  ifdef HAVE_NSLEEP
327   /* on AIX, nsleep is analogous to nanosleep */
328   struct timespec request, remaining;
329   request.tv_sec = microseconds / G_USEC_PER_SEC;
330   request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
331   while (nsleep (&request, &remaining) == -1 && errno == EINTR)
332     request = remaining;
333 #  else /* !HAVE_NSLEEP */
334   if (g_thread_supported ())
335     {
336       static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
337       static GCond* cond = NULL;
338       GTimeVal end_time;
339       
340       g_get_current_time (&end_time);
341       if (microseconds > G_MAXLONG)
342         {
343           microseconds -= G_MAXLONG;
344           g_time_val_add (&end_time, G_MAXLONG);
345         }
346       g_time_val_add (&end_time, microseconds);
347
348       g_static_mutex_lock (&mutex);
349       
350       if (!cond)
351         cond = g_cond_new ();
352       
353       while (g_cond_timed_wait (cond, g_static_mutex_get_mutex (&mutex), 
354                                 &end_time))
355         /* do nothing */;
356       
357       g_static_mutex_unlock (&mutex);
358     }
359   else
360     {
361       struct timeval tv;
362       tv.tv_sec = microseconds / G_USEC_PER_SEC;
363       tv.tv_usec = microseconds % G_USEC_PER_SEC;
364       select(0, NULL, NULL, NULL, &tv);
365     }
366 #  endif /* !HAVE_NSLEEP */
367 # endif /* !HAVE_NANOSLEEP */
368 #endif /* !G_OS_WIN32 */
369 }
370
371 /**
372  * g_time_val_add:
373  * @time_: a #GTimeVal
374  * @microseconds: number of microseconds to add to @time
375  *
376  * Adds the given number of microseconds to @time_. @microseconds can
377  * also be negative to decrease the value of @time_.
378  **/
379 void 
380 g_time_val_add (GTimeVal *time_, glong microseconds)
381 {
382   g_return_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC);
383
384   if (microseconds >= 0)
385     {
386       time_->tv_usec += microseconds % G_USEC_PER_SEC;
387       time_->tv_sec += microseconds / G_USEC_PER_SEC;
388       if (time_->tv_usec >= G_USEC_PER_SEC)
389        {
390          time_->tv_usec -= G_USEC_PER_SEC;
391          time_->tv_sec++;
392        }
393     }
394   else
395     {
396       microseconds *= -1;
397       time_->tv_usec -= microseconds % G_USEC_PER_SEC;
398       time_->tv_sec -= microseconds / G_USEC_PER_SEC;
399       if (time_->tv_usec < 0)
400        {
401          time_->tv_usec += G_USEC_PER_SEC;
402          time_->tv_sec--;
403        }      
404     }
405 }
406
407 /* converts a broken down date representation, relative to UTC, to
408  * a timestamp; it uses timegm() if it's available.
409  */
410 static time_t
411 mktime_utc (struct tm *tm)
412 {
413   time_t retval;
414   
415 #ifndef HAVE_TIMEGM
416   static const gint days_before[] =
417   {
418     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
419   };
420 #endif
421
422 #ifndef HAVE_TIMEGM
423   if (tm->tm_mon < 0 || tm->tm_mon > 11)
424     return (time_t) -1;
425
426   retval = (tm->tm_year - 70) * 365;
427   retval += (tm->tm_year - 68) / 4;
428   retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
429   
430   if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
431     retval -= 1;
432   
433   retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
434 #else
435   retval = timegm (tm);
436 #endif /* !HAVE_TIMEGM */
437   
438   return retval;
439 }
440
441 /**
442  * g_time_val_from_iso8601:
443  * @iso_date: a ISO 8601 encoded date string
444  * @time_: a #GTimeVal
445  *
446  * Converts a string containing an ISO 8601 encoded date and time
447  * to a #GTimeVal and puts it into @time_.
448  *
449  * Return value: %TRUE if the conversion was successful.
450  *
451  * Since: 2.12
452  */
453 gboolean
454 g_time_val_from_iso8601 (const gchar *iso_date,
455                          GTimeVal    *time_)
456 {
457   struct tm tm;
458   long val;
459
460   g_return_val_if_fail (iso_date != NULL, FALSE);
461   g_return_val_if_fail (time_ != NULL, FALSE);
462
463   val = strtoul (iso_date, (char **)&iso_date, 10);
464   if (*iso_date == '-')
465     {
466       /* YYYY-MM-DD */
467       tm.tm_year = val - 1900;
468       iso_date++;
469       tm.tm_mon = strtoul (iso_date, (char **)&iso_date, 10) - 1;
470       
471       if (*iso_date++ != '-')
472         return FALSE;
473       
474       tm.tm_mday = strtoul (iso_date, (char **)&iso_date, 10);
475     }
476   else
477     {
478       /* YYYYMMDD */
479       tm.tm_mday = val % 100;
480       tm.tm_mon = (val % 10000) / 100 - 1;
481       tm.tm_year = val / 10000 - 1900;
482     }
483
484   if (*iso_date++ != 'T')
485     return FALSE;
486   
487   val = strtoul (iso_date, (char **)&iso_date, 10);
488   if (*iso_date == ':')
489     {
490       /* hh:mm:ss */
491       tm.tm_hour = val;
492       iso_date++;
493       tm.tm_min = strtoul (iso_date, (char **)&iso_date, 10);
494       
495       if (*iso_date++ != ':')
496         return FALSE;
497       
498       tm.tm_sec = strtoul (iso_date, (char **)&iso_date, 10);
499     }
500   else
501     {
502       /* hhmmss */
503       tm.tm_sec = val % 100;
504       tm.tm_min = (val % 10000) / 100;
505       tm.tm_hour = val / 10000;
506     }
507
508   time_->tv_sec = mktime_utc (&tm);
509   time_->tv_usec = 1;
510   
511   if (*iso_date == '.')
512     time_->tv_usec = strtoul (iso_date + 1, (char **)&iso_date, 10);
513     
514   if (*iso_date == '+' || *iso_date == '-')
515     {
516       gint sign = (*iso_date == '+') ? -1 : 1;
517       
518       val = 60 * strtoul (iso_date + 1, (char **)&iso_date, 10);
519       
520       if (*iso_date == ':')
521         val = 60 * val + strtoul (iso_date + 1, NULL, 10);
522       else
523         val = 60 * (val / 100) + (val % 100);
524
525       time_->tv_sec += (time_t) (val * sign);
526     }
527
528   return TRUE;
529 }
530
531 /**
532  * g_time_val_to_iso8601:
533  * @time_: a #GTimeVal
534  * 
535  * Converts @time_ into a ISO 8601 encoded string, relative to the
536  * Coordinated Universal Time (UTC).
537  *
538  * Return value: a newly allocated string containing a ISO 8601 date
539  *
540  * Since: 2.12
541  */
542 gchar *
543 g_time_val_to_iso8601 (GTimeVal *time_)
544 {
545   gchar *retval;
546
547   g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
548
549 #define ISO_8601_LEN    21
550 #define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
551   retval = g_new0 (gchar, ISO_8601_LEN + 1);
552   
553   strftime (retval, ISO_8601_LEN,
554             ISO_8601_FORMAT,
555             gmtime (&(time_->tv_sec)));
556   
557   return retval;
558 }
559
560 #define __G_TIMER_C__
561 #include "galiasdef.c"