gtimer.c 13.4 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1 2 3 4
/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Owen Taylor's avatar
Owen Taylor committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Owen Taylor's avatar
Owen Taylor committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Owen Taylor's avatar
Owen Taylor committed
15 16 17 18
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19

20
/*
21
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
 */

27 28 29 30
/* 
 * MT safe
 */

31
#include "config.h"
32
#include "glibconfig.h"
33

Emmanuele Bassi's avatar
Emmanuele Bassi committed
34 35
#include <stdlib.h>

36 37 38
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
39

40
#ifndef G_OS_WIN32
41
#include <sys/time.h>
42 43
#include <time.h>
#include <errno.h>
44
#endif /* G_OS_WIN32 */
Owen Taylor's avatar
Owen Taylor committed
45

46
#ifdef G_OS_WIN32
47
#include <windows.h>
48
#endif /* G_OS_WIN32 */
Owen Taylor's avatar
Owen Taylor committed
49

50
#include "glib.h"
51
#include "gthread.h"
52

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
/**
 * SECTION: timers
 * @title: Timers
 * @short_description: keep track of elapsed time
 *
 * #GTimer records a start time, and counts microseconds elapsed since
 * that time. This is done somewhat differently on different platforms,
 * and can be tricky to get exactly right, so #GTimer provides a
 * portable/convenient interface.
 *
 * <note><para>
 *  #GTimer uses a higher-quality clock when thread support is available.
 *  Therefore, calling g_thread_init() while timers are running may lead to
 *  unreliable results. It is best to call g_thread_init() before starting any
 *  timers, if you are using threads at all.
 * </para></note>
 **/

71
#define G_NSEC_PER_SEC 1000000000
72

73
#define GETTIME(v) (v = g_thread_gettime ())
74

75 76 77 78 79
/**
 * GTimer:
 *
 * Opaque datatype that records a start time.
 **/
80
struct _GTimer
Owen Taylor's avatar
Owen Taylor committed
81
{
82 83
  guint64 start;
  guint64 end;
Owen Taylor's avatar
Owen Taylor committed
84

85 86
  guint active : 1;
};
Owen Taylor's avatar
Owen Taylor committed
87

88 89 90 91 92 93 94
/**
 * g_timer_new:
 * @Returns: a new #GTimer.
 *
 * Creates a new timer, and starts timing (i.e. g_timer_start() is
 * implicitly called for you).
 **/
Owen Taylor's avatar
Owen Taylor committed
95 96 97
GTimer*
g_timer_new (void)
{
98
  GTimer *timer;
Owen Taylor's avatar
Owen Taylor committed
99

100
  timer = g_new (GTimer, 1);
Owen Taylor's avatar
Owen Taylor committed
101 102
  timer->active = TRUE;

103
  GETTIME (timer->start);
Owen Taylor's avatar
Owen Taylor committed
104

105
  return timer;
Owen Taylor's avatar
Owen Taylor committed
106 107
}

108 109 110 111 112 113
/**
 * g_timer_destroy:
 * @timer: a #GTimer to destroy.
 *
 * Destroys a timer, freeing associated resources.
 **/
Owen Taylor's avatar
Owen Taylor committed
114 115 116
void
g_timer_destroy (GTimer *timer)
{
117
  g_return_if_fail (timer != NULL);
Owen Taylor's avatar
Owen Taylor committed
118 119 120 121

  g_free (timer);
}

122 123 124 125 126 127 128 129 130
/**
 * g_timer_start:
 * @timer: a #GTimer.
 *
 * Marks a start time, so that future calls to g_timer_elapsed() will
 * report the time since g_timer_start() was called. g_timer_new()
 * automatically marks the start time, so no need to call
 * g_timer_start() immediately after creating the timer.
 **/
Owen Taylor's avatar
Owen Taylor committed
131 132 133
void
g_timer_start (GTimer *timer)
{
134
  g_return_if_fail (timer != NULL);
Owen Taylor's avatar
Owen Taylor committed
135

136
  timer->active = TRUE;
137

138
  GETTIME (timer->start);
Owen Taylor's avatar
Owen Taylor committed
139 140
}

141 142 143 144 145 146 147
/**
 * g_timer_stop:
 * @timer: a #GTimer.
 *
 * Marks an end time, so calls to g_timer_elapsed() will return the
 * difference between this end time and the start time.
 **/
Owen Taylor's avatar
Owen Taylor committed
148 149 150
void
g_timer_stop (GTimer *timer)
{
151
  g_return_if_fail (timer != NULL);
Owen Taylor's avatar
Owen Taylor committed
152

153
  timer->active = FALSE;
154

155
  GETTIME (timer->end);
Owen Taylor's avatar
Owen Taylor committed
156 157
}

158 159 160 161 162 163 164 165
/**
 * g_timer_reset:
 * @timer: a #GTimer.
 *
 * This function is useless; it's fine to call g_timer_start() on an
 * already-started timer to reset the start time, so g_timer_reset()
 * serves no purpose.
 **/
Owen Taylor's avatar
Owen Taylor committed
166 167 168
void
g_timer_reset (GTimer *timer)
{
169
  g_return_if_fail (timer != NULL);
Owen Taylor's avatar
Owen Taylor committed
170

171
  GETTIME (timer->start);
Owen Taylor's avatar
Owen Taylor committed
172 173
}

174 175 176 177 178 179 180
/**
 * g_timer_continue:
 * @timer: a #GTimer.
 *
 * Resumes a timer that has previously been stopped with
 * g_timer_stop(). g_timer_stop() must be called before using this
 * function.
181 182
 *
 * Since: 2.4
183
 **/
184 185 186
void
g_timer_continue (GTimer *timer)
{
187
  guint64 elapsed;
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

  g_return_if_fail (timer != NULL);
  g_return_if_fail (timer->active == FALSE);

  /* Get elapsed time and reset timer start time
   *  to the current time minus the previously
   *  elapsed interval.
   */

  elapsed = timer->end - timer->start;

  GETTIME (timer->start);

  timer->start -= elapsed;

  timer->active = TRUE;
}

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
/**
 * g_timer_elapsed:
 * @timer: a #GTimer.
 * @microseconds: return location for the fractional part of seconds
 *                elapsed, in microseconds (that is, the total number
 *                of microseconds elapsed, modulo 1000000), or %NULL
 * @Returns: seconds elapsed as a floating point value, including any
 *           fractional part.
 *
 * If @timer has been started but not stopped, obtains the time since
 * the timer was started. If @timer has been stopped, obtains the
 * elapsed time between the time it was started and the time it was
 * stopped. The return value is the number of seconds elapsed,
 * including any fractional part. The @microseconds out parameter is
 * essentially useless.
 *
 * <warning><para>
 *  Calling initialization functions, in particular g_thread_init(), while a
 *  timer is running will cause invalid return values from this function.
 * </para></warning>
 **/
Owen Taylor's avatar
Owen Taylor committed
227 228 229 230 231
gdouble
g_timer_elapsed (GTimer *timer,
		 gulong *microseconds)
{
  gdouble total;
232
  gint64 elapsed;
Owen Taylor's avatar
Owen Taylor committed
233

234
  g_return_val_if_fail (timer != NULL, 0);
Owen Taylor's avatar
Owen Taylor committed
235

236
  if (timer->active)
237
    GETTIME (timer->end);
238

239 240
  elapsed = timer->end - timer->start;

241
  total = elapsed / 1e9;
242 243

  if (microseconds)
244
    *microseconds = (elapsed / 1000) % 1000000;
Owen Taylor's avatar
Owen Taylor committed
245 246 247

  return total;
}
248 249 250 251

void
g_usleep (gulong microseconds)
{
252
#ifdef G_OS_WIN32
253
  Sleep (microseconds / 1000);
254 255 256 257 258
#else /* !G_OS_WIN32 */
# ifdef HAVE_NANOSLEEP
  struct timespec request, remaining;
  request.tv_sec = microseconds / G_USEC_PER_SEC;
  request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
259 260
  while (nanosleep (&request, &remaining) == -1 && errno == EINTR)
    request = remaining;
261
# else /* !HAVE_NANOSLEEP */
262 263 264 265 266 267 268 269
#  ifdef HAVE_NSLEEP
  /* on AIX, nsleep is analogous to nanosleep */
  struct timespec request, remaining;
  request.tv_sec = microseconds / G_USEC_PER_SEC;
  request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
  while (nsleep (&request, &remaining) == -1 && errno == EINTR)
    request = remaining;
#  else /* !HAVE_NSLEEP */
270 271 272 273 274 275 276
  if (g_thread_supported ())
    {
      static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
      static GCond* cond = NULL;
      GTimeVal end_time;
      
      g_get_current_time (&end_time);
277
      if (microseconds > G_MAXLONG)
278
	{
279 280
	  microseconds -= G_MAXLONG;
	  g_time_val_add (&end_time, G_MAXLONG);
281
	}
282 283
      g_time_val_add (&end_time, microseconds);

284 285 286 287 288 289 290 291 292 293 294 295 296 297
      g_static_mutex_lock (&mutex);
      
      if (!cond)
	cond = g_cond_new ();
      
      while (g_cond_timed_wait (cond, g_static_mutex_get_mutex (&mutex), 
				&end_time))
	/* do nothing */;
      
      g_static_mutex_unlock (&mutex);
    }
  else
    {
      struct timeval tv;
298 299
      tv.tv_sec = microseconds / G_USEC_PER_SEC;
      tv.tv_usec = microseconds % G_USEC_PER_SEC;
300 301
      select(0, NULL, NULL, NULL, &tv);
    }
302
#  endif /* !HAVE_NSLEEP */
303 304
# endif /* !HAVE_NANOSLEEP */
#endif /* !G_OS_WIN32 */
305 306
}

307 308
/**
 * g_time_val_add:
309
 * @time_: a #GTimeVal
310 311
 * @microseconds: number of microseconds to add to @time
 *
312 313
 * Adds the given number of microseconds to @time_. @microseconds can
 * also be negative to decrease the value of @time_.
314 315
 **/
void 
316
g_time_val_add (GTimeVal *time_, glong microseconds)
317
{
318
  g_return_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC);
319 320 321

  if (microseconds >= 0)
    {
322 323 324
      time_->tv_usec += microseconds % G_USEC_PER_SEC;
      time_->tv_sec += microseconds / G_USEC_PER_SEC;
      if (time_->tv_usec >= G_USEC_PER_SEC)
325
       {
326 327
         time_->tv_usec -= G_USEC_PER_SEC;
         time_->tv_sec++;
328 329 330 331 332
       }
    }
  else
    {
      microseconds *= -1;
333 334 335
      time_->tv_usec -= microseconds % G_USEC_PER_SEC;
      time_->tv_sec -= microseconds / G_USEC_PER_SEC;
      if (time_->tv_usec < 0)
336
       {
337 338
         time_->tv_usec += G_USEC_PER_SEC;
         time_->tv_sec--;
339 340 341
       }      
    }
}
342

Emmanuele Bassi's avatar
Emmanuele Bassi committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
/* converts a broken down date representation, relative to UTC, to
 * a timestamp; it uses timegm() if it's available.
 */
static time_t
mktime_utc (struct tm *tm)
{
  time_t retval;
  
#ifndef HAVE_TIMEGM
  static const gint days_before[] =
  {
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
  };
#endif

#ifndef HAVE_TIMEGM
  if (tm->tm_mon < 0 || tm->tm_mon > 11)
    return (time_t) -1;

  retval = (tm->tm_year - 70) * 365;
  retval += (tm->tm_year - 68) / 4;
  retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
  
366
  if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
Emmanuele Bassi's avatar
Emmanuele Bassi committed
367 368 369 370 371 372 373 374 375 376 377 378
    retval -= 1;
  
  retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
#else
  retval = timegm (tm);
#endif /* !HAVE_TIMEGM */
  
  return retval;
}

/**
 * g_time_val_from_iso8601:
379
 * @iso_date: an ISO 8601 encoded date string
Emmanuele Bassi's avatar
Emmanuele Bassi committed
380 381 382 383 384 385 386
 * @time_: a #GTimeVal
 *
 * Converts a string containing an ISO 8601 encoded date and time
 * to a #GTimeVal and puts it into @time_.
 *
 * Return value: %TRUE if the conversion was successful.
 *
Matthias Clasen's avatar
Matthias Clasen committed
387
 * Since: 2.12
Emmanuele Bassi's avatar
Emmanuele Bassi committed
388 389 390 391 392
 */
gboolean
g_time_val_from_iso8601 (const gchar *iso_date,
			 GTimeVal    *time_)
{
393
  struct tm tm = {0};
Emmanuele Bassi's avatar
Emmanuele Bassi committed
394 395 396 397 398
  long val;

  g_return_val_if_fail (iso_date != NULL, FALSE);
  g_return_val_if_fail (time_ != NULL, FALSE);

399 400 401 402 403
  /* Ensure that the first character is a digit,
   * the first digit of the date, otherwise we don't
   * have an ISO 8601 date */
  while (g_ascii_isspace (*iso_date))
    iso_date++;
404

405 406
  if (*iso_date == '\0')
    return FALSE;
407 408

  if (!g_ascii_isdigit (*iso_date) && *iso_date != '-' && *iso_date != '+')
409 410
    return FALSE;

Emmanuele Bassi's avatar
Emmanuele Bassi committed
411 412 413 414 415 416 417 418 419
  val = strtoul (iso_date, (char **)&iso_date, 10);
  if (*iso_date == '-')
    {
      /* YYYY-MM-DD */
      tm.tm_year = val - 1900;
      iso_date++;
      tm.tm_mon = strtoul (iso_date, (char **)&iso_date, 10) - 1;
      
      if (*iso_date++ != '-')
420
        return FALSE;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
      
      tm.tm_mday = strtoul (iso_date, (char **)&iso_date, 10);
    }
  else
    {
      /* YYYYMMDD */
      tm.tm_mday = val % 100;
      tm.tm_mon = (val % 10000) / 100 - 1;
      tm.tm_year = val / 10000 - 1900;
    }

  if (*iso_date++ != 'T')
    return FALSE;
  
  val = strtoul (iso_date, (char **)&iso_date, 10);
  if (*iso_date == ':')
    {
      /* hh:mm:ss */
      tm.tm_hour = val;
      iso_date++;
      tm.tm_min = strtoul (iso_date, (char **)&iso_date, 10);
      
      if (*iso_date++ != ':')
        return FALSE;
      
      tm.tm_sec = strtoul (iso_date, (char **)&iso_date, 10);
    }
  else
    {
      /* hhmmss */
      tm.tm_sec = val % 100;
      tm.tm_min = (val % 10000) / 100;
      tm.tm_hour = val / 10000;
    }

456
  time_->tv_usec = 0;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
457
  
458
  if (*iso_date == ',' || *iso_date == '.')
459 460 461 462 463 464 465 466 467
    {
      glong mul = 100000;

      while (g_ascii_isdigit (*++iso_date))
        {
          time_->tv_usec += (*iso_date - '0') * mul;
          mul /= 10;
        }
    }
Emmanuele Bassi's avatar
Emmanuele Bassi committed
468
    
469 470 471 472 473 474 475
  /* Now parse the offset and convert tm to a time_t */
  if (*iso_date == 'Z')
    {
      iso_date++;
      time_->tv_sec = mktime_utc (&tm);
    }
  else if (*iso_date == '+' || *iso_date == '-')
Emmanuele Bassi's avatar
Emmanuele Bassi committed
476 477 478
    {
      gint sign = (*iso_date == '+') ? -1 : 1;
      
479
      val = strtoul (iso_date + 1, (char **)&iso_date, 10);
Emmanuele Bassi's avatar
Emmanuele Bassi committed
480 481
      
      if (*iso_date == ':')
482
        val = 60 * val + strtoul (iso_date + 1, (char **)&iso_date, 10);
Emmanuele Bassi's avatar
Emmanuele Bassi committed
483 484 485
      else
        val = 60 * (val / 100) + (val % 100);

486 487 488 489 490
      time_->tv_sec = mktime_utc (&tm) + (time_t) (60 * val * sign);
    }
  else
    {
      /* No "Z" or offset, so local time */
491
      tm.tm_isdst = -1; /* locale selects DST */
492
      time_->tv_sec = mktime (&tm);
Emmanuele Bassi's avatar
Emmanuele Bassi committed
493 494
    }

495 496 497 498
  while (g_ascii_isspace (*iso_date))
    iso_date++;

  return *iso_date == '\0';
Emmanuele Bassi's avatar
Emmanuele Bassi committed
499 500 501 502 503 504
}

/**
 * g_time_val_to_iso8601:
 * @time_: a #GTimeVal
 * 
505
 * Converts @time_ into an ISO 8601 encoded string, relative to the
Emmanuele Bassi's avatar
Emmanuele Bassi committed
506 507
 * Coordinated Universal Time (UTC).
 *
508
 * Return value: a newly allocated string containing an ISO 8601 date
Emmanuele Bassi's avatar
Emmanuele Bassi committed
509
 *
Matthias Clasen's avatar
Matthias Clasen committed
510
 * Since: 2.12
Emmanuele Bassi's avatar
Emmanuele Bassi committed
511 512 513 514 515
 */
gchar *
g_time_val_to_iso8601 (GTimeVal *time_)
{
  gchar *retval;
516
  struct tm *tm;
517 518 519
#ifdef HAVE_GMTIME_R
  struct tm tm_;
#endif
520
  time_t secs;
521
  
Emmanuele Bassi's avatar
Emmanuele Bassi committed
522 523
  g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);

524
 secs = time_->tv_sec;
525
#ifdef _WIN32
526
 tm = gmtime (&secs);
527
#else
528
#ifdef HAVE_GMTIME_R
529
  tm = gmtime_r (&secs, &tm_);
530
#else
531
  tm = gmtime (&secs);
532
#endif
533
#endif
534 535 536

  if (time_->tv_usec != 0)
    {
537 538 539
      /* ISO 8601 date and time format, with fractionary seconds:
       *   YYYY-MM-DDTHH:MM:SS.MMMMMMZ
       */
540
      retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ",
541 542 543 544 545 546 547
                                tm->tm_year + 1900,
                                tm->tm_mon + 1,
                                tm->tm_mday,
                                tm->tm_hour,
                                tm->tm_min,
                                tm->tm_sec,
                                time_->tv_usec);
548 549 550
    }
  else
    {
551 552 553
      /* ISO 8601 date and time format:
       *   YYYY-MM-DDTHH:MM:SSZ
       */
554
      retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02dZ",
555 556 557 558 559 560
                                tm->tm_year + 1900,
                                tm->tm_mon + 1,
                                tm->tm_mday,
                                tm->tm_hour,
                                tm->tm_min,
                                tm->tm_sec);
561
    }
Emmanuele Bassi's avatar
Emmanuele Bassi committed
562 563 564
  
  return retval;
}