Forgotten macros
[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   if (g_thread_supported ())
327     {
328       static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
329       static GCond* cond = NULL;
330       GTimeVal end_time;
331       
332       g_get_current_time (&end_time);
333       if (microseconds > G_MAXLONG)
334         {
335           microseconds -= G_MAXLONG;
336           g_time_val_add (&end_time, G_MAXLONG);
337         }
338       g_time_val_add (&end_time, microseconds);
339
340       g_static_mutex_lock (&mutex);
341       
342       if (!cond)
343         cond = g_cond_new ();
344       
345       while (g_cond_timed_wait (cond, g_static_mutex_get_mutex (&mutex), 
346                                 &end_time))
347         /* do nothing */;
348       
349       g_static_mutex_unlock (&mutex);
350     }
351   else
352     {
353       struct timeval tv;
354       tv.tv_sec = microseconds / G_USEC_PER_SEC;
355       tv.tv_usec = microseconds % G_USEC_PER_SEC;
356       select(0, NULL, NULL, NULL, &tv);
357     }
358 # endif /* !HAVE_NANOSLEEP */
359 #endif /* !G_OS_WIN32 */
360 }
361
362 /**
363  * g_time_val_add:
364  * @time_: a #GTimeVal
365  * @microseconds: number of microseconds to add to @time
366  *
367  * Adds the given number of microseconds to @time_. @microseconds can
368  * also be negative to decrease the value of @time_.
369  **/
370 void 
371 g_time_val_add (GTimeVal *time_, glong microseconds)
372 {
373   g_return_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC);
374
375   if (microseconds >= 0)
376     {
377       time_->tv_usec += microseconds % G_USEC_PER_SEC;
378       time_->tv_sec += microseconds / G_USEC_PER_SEC;
379       if (time_->tv_usec >= G_USEC_PER_SEC)
380        {
381          time_->tv_usec -= G_USEC_PER_SEC;
382          time_->tv_sec++;
383        }
384     }
385   else
386     {
387       microseconds *= -1;
388       time_->tv_usec -= microseconds % G_USEC_PER_SEC;
389       time_->tv_sec -= microseconds / G_USEC_PER_SEC;
390       if (time_->tv_usec < 0)
391        {
392          time_->tv_usec += G_USEC_PER_SEC;
393          time_->tv_sec--;
394        }      
395     }
396 }
397
398 /* converts a broken down date representation, relative to UTC, to
399  * a timestamp; it uses timegm() if it's available.
400  */
401 static time_t
402 mktime_utc (struct tm *tm)
403 {
404   time_t retval;
405   
406 #ifndef HAVE_TIMEGM
407   static const gint days_before[] =
408   {
409     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
410   };
411 #endif
412
413 #ifndef HAVE_TIMEGM
414   if (tm->tm_mon < 0 || tm->tm_mon > 11)
415     return (time_t) -1;
416
417   retval = (tm->tm_year - 70) * 365;
418   retval += (tm->tm_year - 68) / 4;
419   retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
420   
421   if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
422     retval -= 1;
423   
424   retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
425 #else
426   retval = timegm (tm);
427 #endif /* !HAVE_TIMEGM */
428   
429   return retval;
430 }
431
432 /**
433  * g_time_val_from_iso8601:
434  * @iso_date: a ISO 8601 encoded date string
435  * @time_: a #GTimeVal
436  *
437  * Converts a string containing an ISO 8601 encoded date and time
438  * to a #GTimeVal and puts it into @time_.
439  *
440  * Return value: %TRUE if the conversion was successful.
441  *
442  * Since: 2.12
443  */
444 gboolean
445 g_time_val_from_iso8601 (const gchar *iso_date,
446                          GTimeVal    *time_)
447 {
448   struct tm tm;
449   long val;
450
451   g_return_val_if_fail (iso_date != NULL, FALSE);
452   g_return_val_if_fail (time_ != NULL, FALSE);
453
454   val = strtoul (iso_date, (char **)&iso_date, 10);
455   if (*iso_date == '-')
456     {
457       /* YYYY-MM-DD */
458       tm.tm_year = val - 1900;
459       iso_date++;
460       tm.tm_mon = strtoul (iso_date, (char **)&iso_date, 10) - 1;
461       
462       if (*iso_date++ != '-')
463         return FALSE;
464       
465       tm.tm_mday = strtoul (iso_date, (char **)&iso_date, 10);
466     }
467   else
468     {
469       /* YYYYMMDD */
470       tm.tm_mday = val % 100;
471       tm.tm_mon = (val % 10000) / 100 - 1;
472       tm.tm_year = val / 10000 - 1900;
473     }
474
475   if (*iso_date++ != 'T')
476     return FALSE;
477   
478   val = strtoul (iso_date, (char **)&iso_date, 10);
479   if (*iso_date == ':')
480     {
481       /* hh:mm:ss */
482       tm.tm_hour = val;
483       iso_date++;
484       tm.tm_min = strtoul (iso_date, (char **)&iso_date, 10);
485       
486       if (*iso_date++ != ':')
487         return FALSE;
488       
489       tm.tm_sec = strtoul (iso_date, (char **)&iso_date, 10);
490     }
491   else
492     {
493       /* hhmmss */
494       tm.tm_sec = val % 100;
495       tm.tm_min = (val % 10000) / 100;
496       tm.tm_hour = val / 10000;
497     }
498
499   time_->tv_sec = mktime_utc (&tm);
500   time_->tv_usec = 1;
501   
502   if (*iso_date == '.')
503     time_->tv_usec = strtoul (iso_date + 1, (char **)&iso_date, 10);
504     
505   if (*iso_date == '+' || *iso_date == '-')
506     {
507       gint sign = (*iso_date == '+') ? -1 : 1;
508       
509       val = 60 * strtoul (iso_date + 1, (char **)&iso_date, 10);
510       
511       if (*iso_date == ':')
512         val = 60 * val + strtoul (iso_date + 1, NULL, 10);
513       else
514         val = 60 * (val / 100) + (val % 100);
515
516       time_->tv_sec += (time_t) (val * sign);
517     }
518
519   return TRUE;
520 }
521
522 /**
523  * g_time_val_to_iso8601:
524  * @time_: a #GTimeVal
525  * 
526  * Converts @time_ into a ISO 8601 encoded string, relative to the
527  * Coordinated Universal Time (UTC).
528  *
529  * Return value: a newly allocated string containing a ISO 8601 date
530  *
531  * Since: 2.12
532  */
533 gchar *
534 g_time_val_to_iso8601 (GTimeVal *time_)
535 {
536   gchar *retval;
537
538   g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
539
540 #define ISO_8601_LEN    21
541 #define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
542   retval = g_new0 (gchar, ISO_8601_LEN + 1);
543   
544   strftime (retval, ISO_8601_LEN,
545             ISO_8601_FORMAT,
546             gmtime (&(time_->tv_sec)));
547   
548   return retval;
549 }
550
551 #define __G_TIMER_C__
552 #include "galiasdef.c"