(root)/
glib-2.79.0/
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   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General Public
      17   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      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 G_OS_UNIX
      37  #include <unistd.h>
      38  #endif /* G_OS_UNIX */
      39  
      40  #ifdef HAVE_SYS_TIME_H
      41  #include <sys/time.h>
      42  #endif
      43  #include <time.h>
      44  #ifndef G_OS_WIN32
      45  #include <errno.h>
      46  #endif /* G_OS_WIN32 */
      47  
      48  #ifdef G_OS_WIN32
      49  #include <windows.h>
      50  #endif /* G_OS_WIN32 */
      51  
      52  #include "gtimer.h"
      53  
      54  #include "gmem.h"
      55  #include "gstrfuncs.h"
      56  #include "gtestutils.h"
      57  #include "gmain.h"
      58  
      59  /**
      60   * GTimer:
      61   *
      62   * `GTimer` records a start time, and counts microseconds elapsed since
      63   * that time.
      64   *
      65   * This is done somewhat differently on different platforms, and can be
      66   * tricky to get exactly right, so `GTimer` provides a portable/convenient interface.
      67   */
      68  struct _GTimer
      69  {
      70    guint64 start;
      71    guint64 end;
      72  
      73    guint active : 1;
      74  };
      75  
      76  /**
      77   * g_timer_new: (constructor)
      78   *
      79   * Creates a new timer, and starts timing (i.e. g_timer_start() is
      80   * implicitly called for you).
      81   *
      82   * Returns: (transfer full): a new #GTimer.
      83   **/
      84  GTimer*
      85  g_timer_new (void)
      86  {
      87    GTimer *timer;
      88  
      89    timer = g_new (GTimer, 1);
      90    timer->active = TRUE;
      91  
      92    timer->start = g_get_monotonic_time ();
      93  
      94    return timer;
      95  }
      96  
      97  /**
      98   * g_timer_destroy:
      99   * @timer: a #GTimer to destroy.
     100   *
     101   * Destroys a timer, freeing associated resources.
     102   **/
     103  void
     104  g_timer_destroy (GTimer *timer)
     105  {
     106    g_return_if_fail (timer != NULL);
     107  
     108    g_free (timer);
     109  }
     110  
     111  /**
     112   * g_timer_start:
     113   * @timer: a #GTimer.
     114   *
     115   * Marks a start time, so that future calls to g_timer_elapsed() will
     116   * report the time since g_timer_start() was called. g_timer_new()
     117   * automatically marks the start time, so no need to call
     118   * g_timer_start() immediately after creating the timer.
     119   **/
     120  void
     121  g_timer_start (GTimer *timer)
     122  {
     123    g_return_if_fail (timer != NULL);
     124  
     125    timer->active = TRUE;
     126  
     127    timer->start = g_get_monotonic_time ();
     128  }
     129  
     130  /**
     131   * g_timer_stop:
     132   * @timer: a #GTimer.
     133   *
     134   * Marks an end time, so calls to g_timer_elapsed() will return the
     135   * difference between this end time and the start time.
     136   **/
     137  void
     138  g_timer_stop (GTimer *timer)
     139  {
     140    g_return_if_fail (timer != NULL);
     141  
     142    timer->active = FALSE;
     143  
     144    timer->end = g_get_monotonic_time ();
     145  }
     146  
     147  /**
     148   * g_timer_reset:
     149   * @timer: a #GTimer.
     150   *
     151   * This function is useless; it's fine to call g_timer_start() on an
     152   * already-started timer to reset the start time, so g_timer_reset()
     153   * serves no purpose.
     154   **/
     155  void
     156  g_timer_reset (GTimer *timer)
     157  {
     158    g_return_if_fail (timer != NULL);
     159  
     160    timer->start = g_get_monotonic_time ();
     161  }
     162  
     163  /**
     164   * g_timer_continue:
     165   * @timer: a #GTimer.
     166   *
     167   * Resumes a timer that has previously been stopped with
     168   * g_timer_stop(). g_timer_stop() must be called before using this
     169   * function.
     170   *
     171   * Since: 2.4
     172   **/
     173  void
     174  g_timer_continue (GTimer *timer)
     175  {
     176    guint64 elapsed;
     177  
     178    g_return_if_fail (timer != NULL);
     179    g_return_if_fail (timer->active == FALSE);
     180  
     181    /* Get elapsed time and reset timer start time
     182     *  to the current time minus the previously
     183     *  elapsed interval.
     184     */
     185  
     186    elapsed = timer->end - timer->start;
     187  
     188    timer->start = g_get_monotonic_time ();
     189  
     190    timer->start -= elapsed;
     191  
     192    timer->active = TRUE;
     193  }
     194  
     195  /**
     196   * g_timer_elapsed:
     197   * @timer: a #GTimer.
     198   * @microseconds: return location for the fractional part of seconds
     199   *                elapsed, in microseconds (that is, the total number
     200   *                of microseconds elapsed, modulo 1000000), or %NULL
     201   *
     202   * If @timer has been started but not stopped, obtains the time since
     203   * the timer was started. If @timer has been stopped, obtains the
     204   * elapsed time between the time it was started and the time it was
     205   * stopped. The return value is the number of seconds elapsed,
     206   * including any fractional part. The @microseconds out parameter is
     207   * essentially useless.
     208   *
     209   * Returns: seconds elapsed as a floating point value, including any
     210   *          fractional part.
     211   **/
     212  gdouble
     213  g_timer_elapsed (GTimer *timer,
     214  		 gulong *microseconds)
     215  {
     216    gdouble total;
     217    gint64 elapsed;
     218  
     219    g_return_val_if_fail (timer != NULL, 0);
     220  
     221    if (timer->active)
     222      timer->end = g_get_monotonic_time ();
     223  
     224    elapsed = timer->end - timer->start;
     225  
     226    total = elapsed / 1e6;
     227  
     228    if (microseconds)
     229      *microseconds = elapsed % 1000000;
     230  
     231    return total;
     232  }
     233  
     234  /**
     235   * g_timer_is_active:
     236   * @timer: a #GTimer.
     237   * 
     238   * Exposes whether the timer is currently active.
     239   *
     240   * Returns: %TRUE if the timer is running, %FALSE otherwise
     241   * Since: 2.62
     242   **/
     243  gboolean
     244  g_timer_is_active (GTimer *timer)
     245  {
     246    g_return_val_if_fail (timer != NULL, FALSE);
     247  
     248    return timer->active;
     249  }
     250  
     251  /**
     252   * g_usleep:
     253   * @microseconds: number of microseconds to pause
     254   *
     255   * Pauses the current thread for the given number of microseconds.
     256   *
     257   * There are 1 million microseconds per second (represented by the
     258   * %G_USEC_PER_SEC macro). g_usleep() may have limited precision,
     259   * depending on hardware and operating system; don't rely on the exact
     260   * length of the sleep.
     261   */
     262  void
     263  g_usleep (gulong microseconds)
     264  {
     265    if G_UNLIKELY (microseconds == 0)
     266      return;
     267  
     268  #ifdef G_OS_WIN32
     269    /* Round up to the next millisecond */
     270    Sleep (microseconds ? (1 + (microseconds - 1) / 1000) : 0);
     271  #else
     272    struct timespec request, remaining;
     273    request.tv_sec = microseconds / G_USEC_PER_SEC;
     274    request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
     275    while (nanosleep (&request, &remaining) == -1 && errno == EINTR)
     276      request = remaining;
     277  #endif
     278  }
     279  
     280  /**
     281   * g_time_val_add:
     282   * @time_: a #GTimeVal
     283   * @microseconds: number of microseconds to add to @time
     284   *
     285   * Adds the given number of microseconds to @time_. @microseconds can
     286   * also be negative to decrease the value of @time_.
     287   *
     288   * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use `guint64` for
     289   *    representing microseconds since the epoch, or use #GDateTime.
     290   **/
     291  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     292  void 
     293  g_time_val_add (GTimeVal *time_, glong microseconds)
     294  {
     295    g_return_if_fail (time_ != NULL &&
     296                      time_->tv_usec >= 0 &&
     297                      time_->tv_usec < G_USEC_PER_SEC);
     298  
     299    if (microseconds >= 0)
     300      {
     301        time_->tv_usec += microseconds % G_USEC_PER_SEC;
     302        time_->tv_sec += microseconds / G_USEC_PER_SEC;
     303        if (time_->tv_usec >= G_USEC_PER_SEC)
     304         {
     305           time_->tv_usec -= G_USEC_PER_SEC;
     306           time_->tv_sec++;
     307         }
     308      }
     309    else
     310      {
     311        microseconds *= -1;
     312        time_->tv_usec -= microseconds % G_USEC_PER_SEC;
     313        time_->tv_sec -= microseconds / G_USEC_PER_SEC;
     314        if (time_->tv_usec < 0)
     315         {
     316           time_->tv_usec += G_USEC_PER_SEC;
     317           time_->tv_sec--;
     318         }      
     319      }
     320  }
     321  G_GNUC_END_IGNORE_DEPRECATIONS
     322  
     323  /* converts a broken down date representation, relative to UTC,
     324   * to a timestamp; it uses timegm() if it's available.
     325   */
     326  static time_t
     327  mktime_utc (struct tm *tm)
     328  {
     329    time_t retval;
     330    
     331  #ifndef HAVE_TIMEGM
     332    static const gint days_before[] =
     333    {
     334      0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
     335    };
     336  #endif
     337  
     338  #ifndef HAVE_TIMEGM
     339    if (tm->tm_mon < 0 || tm->tm_mon > 11)
     340      return (time_t) -1;
     341  
     342    retval = (tm->tm_year - 70) * 365;
     343    retval += (tm->tm_year - 68) / 4;
     344    retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
     345    
     346    if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
     347      retval -= 1;
     348    
     349    retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
     350  #else
     351    retval = timegm (tm);
     352  #endif /* !HAVE_TIMEGM */
     353    
     354    return retval;
     355  }
     356  
     357  /**
     358   * g_time_val_from_iso8601:
     359   * @iso_date: an ISO 8601 encoded date string
     360   * @time_: (out): a #GTimeVal
     361   *
     362   * Converts a string containing an ISO 8601 encoded date and time
     363   * to a #GTimeVal and puts it into @time_.
     364   *
     365   * @iso_date must include year, month, day, hours, minutes, and
     366   * seconds. It can optionally include fractions of a second and a time
     367   * zone indicator. (In the absence of any time zone indication, the
     368   * timestamp is assumed to be in local time.)
     369   *
     370   * Any leading or trailing space in @iso_date is ignored.
     371   *
     372   * This function was deprecated, along with #GTimeVal itself, in GLib 2.62.
     373   * Equivalent functionality is available using code like:
     374   * |[
     375   * GDateTime *dt = g_date_time_new_from_iso8601 (iso8601_string, NULL);
     376   * gint64 time_val = g_date_time_to_unix (dt);
     377   * g_date_time_unref (dt);
     378   * ]|
     379   *
     380   * Returns: %TRUE if the conversion was successful.
     381   *
     382   * Since: 2.12
     383   * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use
     384   *    g_date_time_new_from_iso8601() instead.
     385   */
     386  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     387  gboolean
     388  g_time_val_from_iso8601 (const gchar *iso_date,
     389  			 GTimeVal    *time_)
     390  {
     391    struct tm tm = {0};
     392    long val;
     393    long mday, mon, year;
     394    long hour, min, sec;
     395  
     396    g_return_val_if_fail (iso_date != NULL, FALSE);
     397    g_return_val_if_fail (time_ != NULL, FALSE);
     398  
     399    /* Ensure that the first character is a digit, the first digit
     400     * of the date, otherwise we don't have an ISO 8601 date
     401     */
     402    while (g_ascii_isspace (*iso_date))
     403      iso_date++;
     404  
     405    if (*iso_date == '\0')
     406      return FALSE;
     407  
     408    if (!g_ascii_isdigit (*iso_date) && *iso_date != '+')
     409      return FALSE;
     410  
     411    val = strtoul (iso_date, (char **)&iso_date, 10);
     412    if (*iso_date == '-')
     413      {
     414        /* YYYY-MM-DD */
     415        year = val;
     416        iso_date++;
     417  
     418        mon = strtoul (iso_date, (char **)&iso_date, 10);
     419        if (*iso_date++ != '-')
     420          return FALSE;
     421        
     422        mday = strtoul (iso_date, (char **)&iso_date, 10);
     423      }
     424    else
     425      {
     426        /* YYYYMMDD */
     427        mday = val % 100;
     428        mon = (val % 10000) / 100;
     429        year = val / 10000;
     430      }
     431  
     432    /* Validation. */
     433    if (year < 1900 || year > G_MAXINT)
     434      return FALSE;
     435    if (mon < 1 || mon > 12)
     436      return FALSE;
     437    if (mday < 1 || mday > 31)
     438      return FALSE;
     439  
     440    tm.tm_mday = mday;
     441    tm.tm_mon = mon - 1;
     442    tm.tm_year = year - 1900;
     443  
     444    if (*iso_date != 'T')
     445      return FALSE;
     446  
     447    iso_date++;
     448  
     449    /* If there is a 'T' then there has to be a time */
     450    if (!g_ascii_isdigit (*iso_date))
     451      return FALSE;
     452  
     453    val = strtoul (iso_date, (char **)&iso_date, 10);
     454    if (*iso_date == ':')
     455      {
     456        /* hh:mm:ss */
     457        hour = val;
     458        iso_date++;
     459        min = strtoul (iso_date, (char **)&iso_date, 10);
     460        
     461        if (*iso_date++ != ':')
     462          return FALSE;
     463        
     464        sec = strtoul (iso_date, (char **)&iso_date, 10);
     465      }
     466    else
     467      {
     468        /* hhmmss */
     469        sec = val % 100;
     470        min = (val % 10000) / 100;
     471        hour = val / 10000;
     472      }
     473  
     474    /* Validation. Allow up to 2 leap seconds when validating @sec. */
     475    if (hour > 23)
     476      return FALSE;
     477    if (min > 59)
     478      return FALSE;
     479    if (sec > 61)
     480      return FALSE;
     481  
     482    tm.tm_hour = hour;
     483    tm.tm_min = min;
     484    tm.tm_sec = sec;
     485  
     486    time_->tv_usec = 0;
     487    
     488    if (*iso_date == ',' || *iso_date == '.')
     489      {
     490        glong mul = 100000;
     491  
     492        while (mul >= 1 && g_ascii_isdigit (*++iso_date))
     493          {
     494            time_->tv_usec += (*iso_date - '0') * mul;
     495            mul /= 10;
     496          }
     497  
     498        /* Skip any remaining digits after we’ve reached our limit of precision. */
     499        while (g_ascii_isdigit (*iso_date))
     500          iso_date++;
     501      }
     502      
     503    /* Now parse the offset and convert tm to a time_t */
     504    if (*iso_date == 'Z')
     505      {
     506        iso_date++;
     507        time_->tv_sec = mktime_utc (&tm);
     508      }
     509    else if (*iso_date == '+' || *iso_date == '-')
     510      {
     511        gint sign = (*iso_date == '+') ? -1 : 1;
     512        
     513        val = strtoul (iso_date + 1, (char **)&iso_date, 10);
     514        
     515        if (*iso_date == ':')
     516          {
     517            /* hh:mm */
     518            hour = val;
     519            min = strtoul (iso_date + 1, (char **)&iso_date, 10);
     520          }
     521        else
     522          {
     523            /* hhmm */
     524            hour = val / 100;
     525            min = val % 100;
     526          }
     527  
     528        if (hour > 99)
     529          return FALSE;
     530        if (min > 59)
     531          return FALSE;
     532  
     533        time_->tv_sec = mktime_utc (&tm) + (time_t) (60 * (gint64) (60 * hour + min) * sign);
     534      }
     535    else
     536      {
     537        /* No "Z" or offset, so local time */
     538        tm.tm_isdst = -1; /* locale selects DST */
     539        time_->tv_sec = mktime (&tm);
     540      }
     541  
     542    while (g_ascii_isspace (*iso_date))
     543      iso_date++;
     544  
     545    return *iso_date == '\0';
     546  }
     547  G_GNUC_END_IGNORE_DEPRECATIONS
     548  
     549  /**
     550   * g_time_val_to_iso8601:
     551   * @time_: a #GTimeVal
     552   * 
     553   * Converts @time_ into an RFC 3339 encoded string, relative to the
     554   * Coordinated Universal Time (UTC). This is one of the many formats
     555   * allowed by ISO 8601.
     556   *
     557   * ISO 8601 allows a large number of date/time formats, with or without
     558   * punctuation and optional elements. The format returned by this function
     559   * is a complete date and time, with optional punctuation included, the
     560   * UTC time zone represented as "Z", and the @tv_usec part included if
     561   * and only if it is nonzero, i.e. either
     562   * "YYYY-MM-DDTHH:MM:SSZ" or "YYYY-MM-DDTHH:MM:SS.fffffZ".
     563   *
     564   * This corresponds to the Internet date/time format defined by
     565   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt),
     566   * and to either of the two most-precise formats defined by
     567   * the W3C Note
     568   * [Date and Time Formats](http://www.w3.org/TR/NOTE-datetime-19980827).
     569   * Both of these documents are profiles of ISO 8601.
     570   *
     571   * Use g_date_time_format() or g_strdup_printf() if a different
     572   * variation of ISO 8601 format is required.
     573   *
     574   * If @time_ represents a date which is too large to fit into a `struct tm`,
     575   * %NULL will be returned. This is platform dependent. Note also that since
     576   * `GTimeVal` stores the number of seconds as a `glong`, on 32-bit systems it
     577   * is subject to the year 2038 problem. Accordingly, since GLib 2.62, this
     578   * function has been deprecated. Equivalent functionality is available using:
     579   * |[
     580   * GDateTime *dt = g_date_time_new_from_unix_utc (time_val);
     581   * iso8601_string = g_date_time_format_iso8601 (dt);
     582   * g_date_time_unref (dt);
     583   * ]|
     584   *
     585   * The return value of g_time_val_to_iso8601() has been nullable since GLib
     586   * 2.54; before then, GLib would crash under the same conditions.
     587   *
     588   * Returns: (nullable): a newly allocated string containing an ISO 8601 date,
     589   *    or %NULL if @time_ was too large
     590   *
     591   * Since: 2.12
     592   * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use
     593   *    g_date_time_format_iso8601(dt) instead.
     594   */
     595  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     596  gchar *
     597  g_time_val_to_iso8601 (GTimeVal *time_)
     598  {
     599    gchar *retval;
     600    struct tm *tm;
     601  #ifdef HAVE_GMTIME_R
     602    struct tm tm_;
     603  #endif
     604    time_t secs;
     605  
     606    g_return_val_if_fail (time_ != NULL &&
     607                          time_->tv_usec >= 0 &&
     608                          time_->tv_usec < G_USEC_PER_SEC, NULL);
     609  
     610    secs = time_->tv_sec;
     611  #ifdef _WIN32
     612    tm = gmtime (&secs);
     613  #else
     614  #ifdef HAVE_GMTIME_R
     615    tm = gmtime_r (&secs, &tm_);
     616  #else
     617    tm = gmtime (&secs);
     618  #endif
     619  #endif
     620  
     621    /* If the gmtime() call has failed, time_->tv_sec is too big. */
     622    if (tm == NULL)
     623      return NULL;
     624  
     625    if (time_->tv_usec != 0)
     626      {
     627        /* ISO 8601 date and time format, with fractionary seconds:
     628         *   YYYY-MM-DDTHH:MM:SS.MMMMMMZ
     629         */
     630        retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ",
     631                                  tm->tm_year + 1900,
     632                                  tm->tm_mon + 1,
     633                                  tm->tm_mday,
     634                                  tm->tm_hour,
     635                                  tm->tm_min,
     636                                  tm->tm_sec,
     637                                  time_->tv_usec);
     638      }
     639    else
     640      {
     641        /* ISO 8601 date and time format:
     642         *   YYYY-MM-DDTHH:MM:SSZ
     643         */
     644        retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02dZ",
     645                                  tm->tm_year + 1900,
     646                                  tm->tm_mon + 1,
     647                                  tm->tm_mday,
     648                                  tm->tm_hour,
     649                                  tm->tm_min,
     650                                  tm->tm_sec);
     651      }
     652    
     653    return retval;
     654  }
     655  G_GNUC_END_IGNORE_DEPRECATIONS