libCom/osi: fetch monotonic time

This commit is contained in:
Michael Davidsaver
2015-08-25 18:09:35 -04:00
parent cffa2e8f46
commit 6aec8d9bcb
12 changed files with 261 additions and 1 deletions

View File

@@ -119,6 +119,7 @@ Com_SRCS += osdMutex.c
Com_SRCS += osdSpin.c
Com_SRCS += osdEvent.c
Com_SRCS += osdTime.cpp
Com_SRCS += osdMonotonic.c
Com_SRCS += osdProcess.c
Com_SRCS += osdNetIntf.c
Com_SRCS += osdMessageQueue.c

View File

@@ -258,6 +258,17 @@ epicsShareFunc void epicsShareAPI epicsTimeShow (
epicsShareFunc int epicsShareAPI epicsTime_localtime ( const time_t * clock, struct tm * result );
epicsShareFunc int epicsShareAPI epicsTime_gmtime ( const time_t * clock, struct tm * result );
/* Advertised monotonic counter resolution (may not be accurate).
* Minimum non-zero difference between two calls to epicsMonotonicGet()
*/
epicsShareFunc epicsUInt64 epicsMonotonicResolution(void);
/* Fetch monotonic counter, return is nano-seconds since an unspecified time */
epicsShareFunc epicsUInt64 epicsMonotonicGet(void);
#ifdef EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
epicsShareFunc void osdMonotonicInit(void);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@@ -0,0 +1,32 @@
/*************************************************************************\
* Copyright (c) 2015 Michael Davidsaver
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <kern/clock.h>
#include <mach/mach_time.h>
#define epicsExportSharedSymbols
#include "dbDefs.h"
#include "errlog.h"
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "generalTimeSup.h"
void osdMonotonicInit(void)
{
/* no-op */
}
epicsUInt64 epicsMonotonicResolution(void)
{
return 1; /* TODO, how to find ? */
}
epicsUInt64 epicsMonotonicGet(void)
{
uint64_t val = mach_absolute_time(), ret;
absolutetime_to_nanoseconds(val, &ret);
return ret;
}

View File

@@ -19,6 +19,7 @@
#define epicsExportSharedSymbols
#include "cantProceed.h"
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "generalTimeSup.h"
@@ -45,6 +46,8 @@ static int timeRegister(void)
generalTimeCurrentTpRegister("MachTime", \
LAST_RESORT_PRIORITY, osdTimeGetCurrent);
osdMonotonicInit();
return 1;
}
static int done = timeRegister();

View File

@@ -20,6 +20,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <rtems/rtems_bsdnet_internal.h>
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "osdTime.h"
#include "osiNTPTime.h"
@@ -37,6 +38,8 @@ void osdTimeRegister(void)
/* Init NTP first so it can be used to sync ClockTime */
NTPTime_Init(100);
ClockTime_Init(CLOCKTIME_SYNC);
osdMonotonicInit();
}
int osdNTPGet(struct timespec *ts)

View File

@@ -0,0 +1,53 @@
/*************************************************************************\
* Copyright (c) 2015 Michael Davidsaver
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <windows.h>
#define epicsExportSharedSymbols
#include "dbDefs.h"
#include "errlog.h"
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "generalTimeSup.h"
static unsigned char osdUsePrefCounter;
static epicsUInt64 osdMonotonicResolution;
void osdMonotonicInit(void)
{
LARGE_INTEGER freq, val;
if(!QueryPerformanceFrequency(&freq) ||
!QueryPerformanceCounter(&val))
{
double period = 1.0/freq.QuadPart;
osdMonotonicResolution = period*1e9;
osdUsePrefCounter = 1;
} else {
osdMonotonicResolution = 1e6; /* 1 ms TODO place holder */
}
}
epicsUInt64 epicsMonotonicResolution(void)
{
return osdMonotonicResolution;
}
epicsUInt64 epicsMonotonicGet(void)
{
LARGE_INTEGER val;
if(osdUsePrefCounter) {
if(!QueryPerformanceCounter(&val)) {
errMessage(errlogMinor, "Warning: failed to fetch performance counter\n");
return 0;
} else
return val.QuadPart;
} else {
epicsUInt64 ret = GetTickCount();
ret *= 1000000;
return ret;
}
}

View File

@@ -31,6 +31,7 @@
// EPICS
//
#define epicsExportSharedSymbols
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "generalTimeSup.h"
#include "epicsTimer.h"
@@ -106,6 +107,8 @@ static int timeRegister(void)
generalTimeCurrentTpRegister("PerfCounter", 150, osdTimeGetCurrent);
pCurrentTime->startPLL ();
osdMonotonicInit();
return 1;
}
static int done = timeRegister();

View File

@@ -0,0 +1,31 @@
/*************************************************************************\
* Copyright (c) 2015 Michael Davidsaver
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#import <mach/mach_time.h>
#define epicsExportSharedSymbols
#include "dbDefs.h"
#include "errlog.h"
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "generalTimeSup.h"
void osdMonotonicInit(void)
{
/* no-op */
}
epicsUInt64 epicsMonotonicResolution(void)
{
return 1; /* TODO, how to find ? */
}
epicsUInt64 epicsMonotonicGet(void)
{
uint64_t val = mach_absolute_time(), ret;
absolutetime_to_nanoseconds(val, &ret);
return ret;
}

View File

@@ -0,0 +1,75 @@
/*************************************************************************\
* Copyright (c) 2015 Michael Davidsaver
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#define epicsExportSharedSymbols
#include "dbDefs.h"
#include "errlog.h"
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "generalTimeSup.h"
static clockid_t osdMonotonicID;
static epicsUInt64 osdMonotonicResolution;
void osdMonotonicInit(void)
{
unsigned i;
clockid_t ids[] = {
#ifdef CLOCK_MONOTONIC_RAW
CLOCK_MONOTONIC_RAW, /* Linux specific */
#endif
#ifdef CLOCK_HIGHRES
CLOCK_HIGHRES, /* solaris specific */
#endif
#ifdef CLOCK_MONOTONIC
CLOCK_MONOTONIC, /* Linux, RTEMS, and probably others */
#endif
/* fallback and vxWorks, not actually monotonic, but always available */
CLOCK_REALTIME
};
for(i=0; i<NELEMENTS(ids); i++) {
epicsUInt64 res;
struct timespec ts;
int ret = clock_getres(ids[i], &ts);
if(ret) continue;
res = ts.tv_sec;
res *= 1000000000;
res += ts.tv_nsec;
/* fetch the time once to see that it actually succeeds */
ret = clock_gettime(ids[i], &ts);
if(ret) continue;
osdMonotonicID = ids[i];
osdMonotonicResolution = res;
return;
}
errMessage(errlogMinor, "Warning: failed to setup monotonic time source\n");
}
epicsUInt64 epicsMonotonicResolution(void)
{
return osdMonotonicResolution;
}
epicsUInt64 epicsMonotonicGet(void)
{
struct timespec ts;
int ret = clock_gettime(osdMonotonicID, &ts);
if(ret) {
errlogPrintf("Warning: failed to fetch monotonic time %d %d\n",
(int)osdMonotonicID, ret);
return 0;
} else {
epicsUInt64 ret = ts.tv_sec;
ret *= 1000000000;
ret += ts.tv_nsec;
return ret;
}
}

View File

@@ -17,6 +17,7 @@
#define epicsExportSharedSymbols
#include "cantProceed.h"
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "generalTimeSup.h"
@@ -56,6 +57,8 @@ int clock_settime(clockid_t clock, const timespec *tp)
static int timeRegister(void)
{
TIME_INIT;
osdMonotonicInit();
return 1;
}
static int done = timeRegister();

View File

@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <taskLib.h>
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTime.h"
#include "osiNTPTime.h"
#include "osiClockTime.h"
@@ -63,6 +64,7 @@ static int timeRegister(void)
} else {
ClockTime_Init(CLOCKTIME_NOSYNC);
}
osdMonotonicInit();
return 1;
}
static int done = timeRegister();

View File

@@ -38,12 +38,53 @@ static const unsigned uSecPerSec = 1000u * mSecPerSec;
static const unsigned nSecPerSec = 1000u * uSecPerSec;
static const double precisionEPICS = 1.0 / nSecPerSec;
static void crossCheck(double delay)
{
double mindelta = 2*epicsMonotonicResolution()*1e-9,
tres = epicsThreadSleepQuantum();
epicsUInt64 A = epicsMonotonicGet();
epicsThreadSleep(delay);
epicsUInt64 B = epicsMonotonicGet();
double actual = (B-A)*1e-9, percent;
if(mindelta<tres*2)
mindelta = tres*2;
if(delay<mindelta)
delay = mindelta;
percent = (delay-actual)/delay*100.0;
testOk(fabs(percent)<1000.0, "crossCheck(%f) actual %f (%f %%)",
delay, actual, percent);
}
static void testMonotonic()
{
crossCheck(2.1); /* greater than 2 so that seconds value is different */
crossCheck(0.1);
crossCheck(0.01);
crossCheck(0.001);
crossCheck(epicsThreadSleepQuantum());
testDiag("Resolution %u ns", (unsigned)epicsMonotonicResolution());
epicsUInt64 A = epicsMonotonicGet();
epicsThreadSleep(0.0);
epicsUInt64 B = epicsMonotonicGet();
testDiag("epicsThreadSleep(0.0) Delta %u ns", (unsigned)(B-A));
A = epicsMonotonicGet();
B = epicsMonotonicGet();
testDiag("Small Delta %u ns", (unsigned)(B-A));
}
MAIN(epicsTimeTest)
{
const int wasteTime = 100000;
const int nTimes = 10;
testPlan(17 + nTimes * 19);
testPlan(22 + nTimes * 19);
try {
const epicsTimeStamp epochTS = {0, 0};
@@ -241,5 +282,7 @@ MAIN(epicsTimeTest)
testFail("OS time_t conversion exception for value 10 years hence");
}
testMonotonic();
return testDone();
}