/*
   Copyright 2010 Sun Microsystems, Inc.
    All rights reserved. Use is subject to license terms.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/

/*
 * hrt_utils.c
 *
 */

#include <hrt_utils.h>
#include <assert.h>

/*
 * High-Resolution Time Utilities -- Implementation
 */

int
hrt_rtnow(hrt_rtstamp* x)
{
    int ret;

#if (HRT_REALTIME_METHOD==HRT_USE_CLOCK_GETTIME)
#  if (defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK+0 >= 0))
#    define REAL_CLOCK_TYPE CLOCK_MONOTONIC
#  else
#    define REAL_CLOCK_TYPE CLOCK_REALTIME
#  endif
    ret = clock_gettime(REAL_CLOCK_TYPE, &x->time);
#elif (HRT_REALTIME_METHOD==HRT_USE_GETTIMEOFDAY)
    ret = gettimeofday(&x->time, NULL);
#elif (HRT_REALTIME_METHOD==HRT_USE_TIMES)
    {
        clock_t r = times(NULL);
        if (r == -1) {
            ret = r;
        } else {
            ret = 0;
            x->time = r;
        }
    }
#elif (HRT_REALTIME_METHOD==HRT_USE_ANSI_TIME)
    {
        time_t r = time(NULL);
        if (r == -1) {
            ret = r;
        } else {
            ret = 0;
            x->time = r;
        }
    }
#endif

    return ret;
}

int
hrt_ctnow(hrt_ctstamp* x)
{
    int ret;

#if (HRT_CPUTIME_METHOD==HRT_USE_CLOCK_GETTIME)
    ret = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &x->time);
#elif (HRT_CPUTIME_METHOD==HRT_USE_GETRUSAGE)
    ret = getrusage(RUSAGE_SELF, &x->time);
#elif (HRT_CPUTIME_METHOD==HRT_USE_TIMES)
    {
        clock_t r = times(&x->time);
        ret = (r == -1 ? r : 0);
    }
#elif (HRT_CPUTIME_METHOD==HRT_USE_ANSI_CLOCK)
    {
        clock_t r = clock();
        if (r == -1) {
            ret = r;
        } else {
            ret = 0;
            x->time = r;
        }
    }
#endif

    return ret;
}

int
hrt_tnow(hrt_tstamp* x)
{
    int ret;
    ret = hrt_rtnow(&x->rtstamp);
    if (ret != 0)
        return ret;
    ret = hrt_ctnow(&x->ctstamp);
    return ret;
}

#if (HRT_REALTIME_METHOD==HRT_USE_CLOCK_GETTIME      \
     || HRT_CPUTIME_METHOD==HRT_USE_CLOCK_GETTIME)
static inline double
timespec_diff(const struct timespec *y, const struct timespec *x)
{
    return (((y->tv_sec - x->tv_sec) * 1000000.0)
            + ((y->tv_nsec - x->tv_nsec) / 1000.0));
}
#endif

#if (HRT_REALTIME_METHOD==HRT_USE_GETTIMEOFDAY       \
     || HRT_CPUTIME_METHOD==HRT_USE_GETRUSAGE)
static inline double
timeval_diff(const struct timeval *y, const struct timeval *x)
{
    return (((y->tv_sec - x->tv_sec) * 1000000.0)
            + (y->tv_usec - x->tv_usec));
}
#endif

#if (HRT_REALTIME_METHOD==HRT_USE_TIMES              \
     || HRT_CPUTIME_METHOD==HRT_USE_TIMES            \
     || HRT_CPUTIME_METHOD==HRT_USE_ANSI_CLOCK)
static inline double
clock_t_diff(clock_t y, clock_t x)
{
    /* ignored: loosing precision if clock_t is specified as long double */
    return (double)(y - x);
}
#endif

double
hrt_rtmicros(const hrt_rtstamp* y, const hrt_rtstamp* x)
{
#if (HRT_REALTIME_METHOD==HRT_USE_TIMES)
    static long clock_ticks = 0;
    if (clock_ticks == 0) {
        clock_ticks = sysconf(_SC_CLK_TCK);
        assert (clock_ticks > 0);
    }
#endif

#if (HRT_REALTIME_METHOD==HRT_USE_CLOCK_GETTIME)
    return timespec_diff(&y->time, &x->time);
#elif (HRT_REALTIME_METHOD==HRT_USE_GETTIMEOFDAY)
    return timeval_diff(&y->time, &x->time);
#elif (HRT_REALTIME_METHOD==HRT_USE_TIMES)
    return ((clock_t_diff(y->time, x->time) * 1000000.0) / clock_ticks);
#elif (HRT_REALTIME_METHOD==HRT_USE_ANSI_TIME)
    return (difftime(y->time, x->time) * 1000000.0);
#endif
}

double
hrt_ctmicros(const hrt_ctstamp* y, const hrt_ctstamp* x)
{
#if (HRT_CPUTIME_METHOD==HRT_USE_TIMES)
    static long clock_ticks = 0;
    if (clock_ticks == 0) {
        clock_ticks = sysconf(_SC_CLK_TCK);
        assert (clock_ticks > 0);
    }
#endif

#if (HRT_CPUTIME_METHOD==HRT_USE_CLOCK_GETTIME)
    return timespec_diff(&y->time, &x->time);
#elif (HRT_CPUTIME_METHOD==HRT_USE_GETRUSAGE)
    return (timeval_diff(&y->time.ru_utime, &x->time.ru_utime)
            + timeval_diff(&y->time.ru_stime, &x->time.ru_stime));
#elif (HRT_CPUTIME_METHOD==HRT_USE_TIMES)
    return (((clock_t_diff(y->time.tms_utime, x->time.tms_utime)
              + clock_t_diff(y->time.tms_stime, x->time.tms_stime))
             * 1000000.0) / clock_ticks);
#elif (HRT_CPUTIME_METHOD==HRT_USE_ANSI_CLOCK)
    return ((clock_t_diff(y->time, x->time) * 1000000.0) / CLOCKS_PER_SEC);
#endif
}

void
hrt_rtnull(hrt_rtstamp* x)
{
#if (HRT_REALTIME_METHOD==HRT_USE_CLOCK_GETTIME)
    x->time.tv_sec = 0;
    x->time.tv_nsec = 0;
#elif (HRT_REALTIME_METHOD==HRT_USE_GETTIMEOFDAY)
    x->time.tv_sec = 0;
    x->time.tv_usec = 0;
#elif (HRT_REALTIME_METHOD==HRT_USE_TIMES)
    x->time = 0;
#elif (HRT_REALTIME_METHOD==HRT_USE_ANSI_TIME)
    x->time = 0;
#endif
}

void
hrt_ctnull(hrt_ctstamp* x)
{
#if (HRT_CPUTIME_METHOD==HRT_USE_CLOCK_GETTIME)
    x->time.tv_sec = 0;
    x->time.tv_nsec = 0;
#elif (HRT_CPUTIME_METHOD==HRT_USE_GETRUSAGE)
    x->time.ru_utime.tv_sec = 0;
    x->time.ru_utime.tv_usec = 0;
    x->time.ru_stime.tv_sec = 0;
    x->time.ru_stime.tv_usec = 0;
#elif (HRT_CPUTIME_METHOD==HRT_USE_TIMES)
    x->time.tms_utime = 0;
    x->time.tms_stime = 0;
#elif (HRT_CPUTIME_METHOD==HRT_USE_ANSI_CLOCK)
    x->time = 0;
#endif
}

void
hrt_tnull(hrt_tstamp* x)
{
    hrt_rtnull(&x->rtstamp);
    hrt_ctnull(&x->ctstamp);
}
