summaryrefslogblamecommitdiffstats
path: root/inc/dnbd3/shared/timing.h
blob: f23bfebb6f0040dc78d444266a48a1072d137fb6 (plain) (tree)























                                               
                                                                                                                          




































                                                                               
                                                                 




                                     





                                                                                    




































































                                                                                               
















                                                                            

      
#ifndef _D_TIMING_H
#define _D_TIMING_H

#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif

#include <time.h>
#include <stdint.h>
#include <stdbool.h>

#ifdef CLOCK_MONOTONIC_RAW
#define BEST_CLOCK_SOURCE CLOCK_MONOTONIC_RAW
#else
#define BEST_CLOCK_SOURCE CLOCK_MONOTONIC
#endif

typedef struct timespec ticks;

extern struct timespec basetime;

/**
 * Assign src to dst while adding secs seconds.
 */
#define timing_set(dst,src,secs) do { (dst)->tv_sec = (src)->tv_sec + (secs); (dst)->tv_nsec = (src)->tv_nsec; } while (0)

/**
 * Define variable now, initialize to timing_get.
 */
#define declare_now ticks now; timing_get( &now )

/**
 * Call this once to calibrate on startup.
 * Although overflows of CLOCK_MONOTONIC(_RAW) should
 * by definition never happen, we still have a fixed size
 * int that could at some point. By forcing the counter
 * to start at 0 on startup the point of overflow
 * will be very far in the future (decades for 32bit time_t,
 * end of universe for 64bit).
 */
void timing_setBase();

/**
 * Internal, do not use. Moved to another function
 * to prevent inlining of error handling code, which
 * should be very unlikely to ever trigger.
 */
_Noreturn void timing_abort();

/**
 * Get current time. Shortcut for clock_gettime with error check.
 */
static inline void timing_get(ticks* retval)
{
	if ( clock_gettime( BEST_CLOCK_SOURCE, retval ) == -1 ) timing_abort();
	retval->tv_sec -= basetime.tv_sec;
}

/**
 * Get a ticks instance somewhere in the future.
 * Useful for timeouts.
 */
static inline void timing_gets(ticks* retval, int32_t addSeconds)
{
	timing_get( retval );
	retval->tv_sec += addSeconds;
}

static inline void timing_addSeconds(ticks* retval, ticks* base, int32_t addSeconds)
{
	retval->tv_sec = base->tv_sec + addSeconds;
	retval->tv_nsec = base->tv_nsec;
}

/**
 * Check whether given timeout is reached.
 * Might trigger up to one second early.
 */
static inline bool timing_reached(const ticks* timeout, const ticks* now)
{
	return now->tv_sec >= timeout->tv_sec;
}
#define timing_1le2(one,two) timing_reached(one,two)

/**
 * Precise check whether given timeout has been reached.
 */
static inline bool timing_reachedPrecise(const ticks* timeout, const ticks* now)
{
	return now->tv_sec > timeout->tv_sec
			|| (now->tv_sec == timeout->tv_sec && now->tv_nsec > timeout->tv_nsec);
}

/**
 * Shortcut for above. Useful if not used in loop.
 * Might trigger up to one second early.
 */
static inline bool timing_isReached(const ticks* timeout)
{
	ticks now;
	timing_get( &now );
	return timing_reached( timeout, &now );
}
/**
 * Shortcut for above. Useful if not used in loop.
 */
static inline bool timing_isReachedPrecise(const ticks* timeout)
{
	ticks now;
	timing_get( &now );
	return timing_reachedPrecise( timeout, &now );
}


/**
 * Get difference between two ticks, rounded down to seconds.
 * Make sure you pass the arguments in the proper order. If
 * end is before start, 0 will always be returned.
 */
static inline uint32_t timing_diff(const ticks *start, const ticks *end)
{
	if ( end->tv_sec <= start->tv_sec ) return 0;
	return (uint32_t)( ( end->tv_sec - start->tv_sec )
			+ ( start->tv_nsec > end->tv_nsec ? -1 : 0 ) );
}

/**
 * Get difference between two ticks, rounded down to milliseconds.
 * Same as above; passing arguments in reverse will always return 0.
 */
static inline uint64_t timing_diffMs(const ticks *start, const ticks *end)
{
	if ( end->tv_sec < start->tv_sec ) return 0;
	uint64_t diff = (uint64_t)( end->tv_sec - start->tv_sec ) * 1000;
	if ( start->tv_nsec >= end->tv_nsec ) {
		if ( diff == 0 ) return 0;
		diff -= (start->tv_nsec - end->tv_nsec) / 1000000;
	} else {
		diff += (end->tv_nsec - start->tv_nsec) / 1000000;
	}
	return diff;
}

/**
 * Get difference between two ticks, rounded down to microseconds.
 * Same as above; passing arguments in reverse will always return 0.
 */
static inline uint64_t timing_diffUs(const ticks *start, const ticks *end)
{
	if ( end->tv_sec < start->tv_sec ) return 0;
	uint64_t diff = (uint64_t)( end->tv_sec - start->tv_sec ) * 1000000;
	if ( start->tv_nsec >= end->tv_nsec ) {
		if ( diff == 0 ) return 0;
		diff -= ( start->tv_nsec - end->tv_nsec ) / 1000;
	} else {
		diff += ( end->tv_nsec - start->tv_nsec ) / 1000;
	}
	return diff;
}


#endif