orca/src/platform/linux_clock.c

194 lines
5.2 KiB
C
Raw Normal View History

2022-08-14 16:19:40 +00:00
/************************************************************//**
*
* @file: linux_clock.c
* @author: Martin Fouilleul
* @date: 20/04/2020
* @revision:
*
*****************************************************************/
#include<math.h> //fabs()
#include<time.h>
#include<sys/time.h> // gettimeofday()
#include<sys/types.h>
#include<sys/sysctl.h>
#include<unistd.h> // nanosleep()
#include"platform_rng.h"
#include"platform_clock.h"
extern "C" {
//TODO(martin): measure the actual values of these constants
const f64 SYSTEM_FUZZ = 25e-9, // minimum time to read the clock (s)
SYSTEM_TICK = 25e-9; // minimum step between two clock readings (s)
static u64 __initialTimestamp__ = 0;
static u64 __initialMonotonicNanoseconds__ = 0;
static inline u64 LinuxGetMonotonicNanoseconds()
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
//WARN(martin): do not multiply ts.tv_sec directly (implicit conversion will overflow)
u64 r = ts.tv_sec;
r *= 1000000000;
r += ts.tv_nsec;
return(r);
}
static inline u64 LinuxGetUptimeNanoseconds()
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
//WARN(martin): do not multiply ts.tv_sec directly (implicit conversion will overflow)
u64 r = ts.tv_sec;
r *= 1000000000;
r += ts.tv_nsec;
return(r);
}
void ClockSystemInit()
{
//NOTE(martin): we don't know of a widely supported way of getting the boot time on linux, so
// we fallback to our second best choice, which is taking an initial timestamp and
// the initial monotonic time now
timeval tv;
gettimeofday(&tv, 0);
__initialMonotonicNanoseconds__ = LinuxGetMonotonicNanoseconds();
//NOTE(martin): convert boot date to timestamp
__initialTimestamp__ = (((u64)tv.tv_sec + JAN_1970) << 32)
+ (u64)(tv.tv_usec * 1e-6 * TIMESTAMPS_PER_SECOND);
//TODO(martin): maybe get a state vector for exclusive clock usage ?
RandomSeedFromDevice();
}
fx_timestamp ClockGetTimestamp(clock_kind clock)
{
fx_timestamp ts = {0};
switch(clock)
{
case SYS_CLOCK_MONOTONIC:
{
//NOTE(martin): compute monotonic offset and add it to bootup timestamp
u64 noff = LinuxGetMonotonicNanoseconds() - __initialMonotonicNanoseconds__;
u64 foff = (u64)(noff * 1e-9 * TIMESTAMPS_PER_SECOND);
ts.ts = __initialTimestamp__ + foff;
} break;
case SYS_CLOCK_UPTIME:
{
//TODO(martin): maybe we should warn that this date is inconsistent after a sleep ?
//NOTE(martin): compute uptime offset and add it to bootup timestamp
u64 noff = LinuxGetUptimeNanoseconds() - __initialMonotonicNanoseconds__ ;
u64 foff = (u64)(noff * 1e-9 * TIMESTAMPS_PER_SECOND);
ts.ts = __initialTimestamp__ + foff;
} break;
case SYS_CLOCK_DATE:
{
//NOTE(martin): get system date and convert it to a fixed-point timestamp
timeval tv;
gettimeofday(&tv, 0);
ts.ts = (((u64)tv.tv_sec + JAN_1970) << 32)
+ (u64)(tv.tv_usec * 1e-6 * TIMESTAMPS_PER_SECOND);
} break;
}
//NOTE(martin): add a random fuzz between 0 and 1 times the system fuzz
f64 fuzz = RandomU32()/(f64)(~(0UL)) * SYSTEM_FUZZ;
fx_timediff tdfuzz = TimediffFromSeconds(fuzz);
ts = TimestampAdd(ts, tdfuzz);
//TODO(martin): ensure that we always return a value greater than the last value
return(ts);
}
f64 ClockGetTime(clock_kind clock)
{
switch(clock)
{
case SYS_CLOCK_MONOTONIC:
{
//NOTE(martin): compute monotonic offset and add it to bootup timestamp
u64 noff = LinuxGetMonotonicNanoseconds();
return((f64)noff * 1e-9);
} break;
case SYS_CLOCK_UPTIME:
{
//TODO(martin): maybe we should warn that this date is inconsistent after a sleep ?
//NOTE(martin): compute uptime offset and add it to bootup timestamp
u64 noff = LinuxGetUptimeNanoseconds();
return((f64)noff * 1e-9);
} break;
case SYS_CLOCK_DATE:
{
//TODO(martin): maybe warn about precision loss ?
// could also change the epoch since we only promise to return a relative time
//NOTE(martin): get system date and convert it to seconds
timeval tv;
gettimeofday(&tv, 0);
return(((f64)tv.tv_sec + JAN_1970) + ((f64)tv.tv_usec * 1e-6));
} break;
}
return(0);
}
void ClockSleepNanoseconds(u64 nanoseconds)
{
timespec rqtp;
rqtp.tv_sec = nanoseconds / 1000000000;
rqtp.tv_nsec = nanoseconds - rqtp.tv_sec * 1000000000;
nanosleep(&rqtp, 0);
}
////////////////////////////////////////////////////////////////////
//TODO: update these functions for various clocks besides monotonic
////////////////////////////////////////////////////////////////////
f64 ClockGetGranularity(clock_kind clock)
{
u64 minDiff = ~(0ULL);
const int GRANULARITY_NUM_ITERATION = 100000;
for(int i=0; i<GRANULARITY_NUM_ITERATION; i++)
{
u64 a = LinuxGetMonotonicNanoseconds();
u64 b = LinuxGetMonotonicNanoseconds();
u64 diff = b - a;
if(diff != 0 && diff < minDiff)
{
minDiff = diff;
}
}
return(minDiff * 1e-9);
}
f64 ClockGetMeanReadTime(clock_kind clock)
{
u64 start = LinuxGetMonotonicNanoseconds();
fx_timestamp time;
const int READ_NUM_ITERATION = 1000000;
for(int i=0; i<READ_NUM_ITERATION; i++)
{
time = ClockGetTimestamp(SYS_CLOCK_MONOTONIC);
}
volatile u64 ts = time.ts;
u64 end = LinuxGetMonotonicNanoseconds();
f64 mean = (end - start)/(f64)READ_NUM_ITERATION;
return(mean * 1e-9);
}
} // extern "C"