m3core/src/time/POSIX/TimePosixC.c


/* Copyright (C) 1989, Digital Equipment Corporation        */
/* All rights reserved.                                     */
/* See the file COPYRIGHT for a full description.           */

/*
Modula-3 Time.T is LONGREAL aka double counting seconds.
We use gettimeofday() which returns seconds and microseconds.
*/

#include "m3core.h"

#if __GNUC__ >= 4
#pragma GCC visibility push(hidden)
#endif

#ifdef __cplusplus
extern "C" {
#endif

#define THOUSAND ((UINT64)1000)
#define MILLION (THOUSAND * THOUSAND)
#define BILLION (THOUSAND * MILLION)

#ifdef _TIME64_T
/* OSF/1 5.1 in particular */
typedef struct timeval64 m3_timeval_t;
#define m3_gettimeofday gettimeofday64
#else
typedef struct timeval m3_timeval_t;
#define m3_gettimeofday gettimeofday
#endif

static
LONGREAL/*Time.T*/
__cdecl
TimePosix__FromMicrotime(const m3_timeval_t* tv)
/* see also TimePosix__ToUtime */
{
    return ((LONGREAL)tv->tv_sec) + ((LONGREAL)tv->tv_usec) / (LONGREAL)MILLION;
}

static
LONGREAL/*Time.T*/
__cdecl
TimePosix__FromNanotime(const struct timespec* tv)
{
    return ((LONGREAL)tv->tv_sec) + ((LONGREAL)tv->tv_nsec) / (LONGREAL)BILLION;
}

LONGREAL/*Time.T*/
__cdecl
TimePosix__Now(void)
{
    m3_timeval_t tv;
    int i;

    ZERO_MEMORY(tv);
    i = m3_gettimeofday(&tv, NULL);
    assert(i == 0);
    return TimePosix__FromMicrotime(&tv);
}

static
LONGREAL/*Time.T*/
ComputeGrainViaSamplingOnce(void)
{
    /* Determine value of "Grain" experimentally.  Note that
     * this will fail if this thread is descheduled for a tick during the
     * loop below. Omitting volatile leads to the result is 0 on Cygwin if optimized.
     */
    volatile LONGREAL t0 = TimePosix__Now();
    while (1)
    {
        volatile LONGREAL t1 = TimePosix__Now();
        if (t1 != t0)
            return (t1 - t0);
    }
}

static
LONGREAL/*Time.T*/
__cdecl
ComputeGrainViaSampling(void)
{
    /* Compute a few and then takes the smallest value. */
    LONGREAL a = ComputeGrainViaSamplingOnce();
    LONGREAL b = ComputeGrainViaSamplingOnce();
    LONGREAL c = ComputeGrainViaSamplingOnce();
    a = (b < a ? b : a);
    a = (c < a ? c : a);
    return a;
}

static
LONGREAL/*Time.T*/
__cdecl
ComputeGrainViaClockGetRes(void)
{
    struct timespec res = { 0 };
#if defined(__osf__) /* defined(CLOCK_HIGHRES) || defined(CLOCK_REALTIME) */
    int i = -1;
#if defined(CLOCK_HIGHRES)
    i = clock_getres(CLOCK_HIGHRES, &res);
#elif defined(CLOCK_REALTIME)
    i = clock_getres(CLOCK_REALTIME, &res);
#else
#error no CLOCK_HIGHRES or CLOCK_REALTIME
#endif
    assert(i == 0);
#endif
    return TimePosix__FromNanotime(&res);
}

LONGREAL/*Time.T*/
__cdecl
TimePosix__ComputeGrain(void)
{
/* ComputeGrainViaSampling has a strong propensity to hang on OSF/1.
   I don't know why. On other platforms, ComputeGrainViaClockGetRes and
   ComputeGrainViaSampling vary by a lot.
*/
#if defined(__osf__) /* defined(CLOCK_HIGHRES) || defined(CLOCK_REALTIME) */
  return ComputeGrainViaClockGetRes();
#else
  return ComputeGrainViaSampling();
#endif
}

#ifdef __cplusplus
} /* extern C */
#endif