Merge branch 'monotonictime' into 3.17

This commit is contained in:
Andrew Johnson
2017-11-02 17:32:46 -05:00
14 changed files with 404 additions and 32 deletions
+28 -31
View File
@@ -27,7 +27,10 @@
#include "epicsSpin.h"
#include "epicsThread.h"
#include "epicsMutex.h"
#include "epicsEvent.h"
#include "epicsMath.h"
#include "dbCommon.h"
#include "epicsTime.h"
#include "dbLockPvt.h"
#include "dbStaticLib.h"
@@ -40,10 +43,6 @@
#include "xRecord.h"
#if defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && !defined(_WIN32)
# define TIME_STATS
#endif
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
@@ -64,10 +63,9 @@ static dbCommon **precords;
typedef struct {
int id;
unsigned long N[3];
#ifdef TIME_STATS
double X[3];
double X2[3];
#endif
double min[3], max[3];
unsigned int done;
epicsEventId donevent;
@@ -158,25 +156,19 @@ void doreTarget(workerPriv *p)
static
void worker(void *raw)
{
#ifdef TIME_STATS
struct timespec before;
#endif
unsigned init = 1;
workerPriv *priv = raw;
testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf());
#ifdef TIME_STATS
clock_gettime(CLOCK_MONOTONIC, &before);
#endif
while(!priv->done) {
epicsUInt64 after, before;
double sel = getRand();
#ifdef TIME_STATS
struct timespec after;
double duration;
#endif
int act;
before = epicsMonotonicGet();
if(sel<0.33) {
doSingle(priv);
act = 0;
@@ -188,19 +180,18 @@ void worker(void *raw)
act = 2;
}
#ifdef TIME_STATS
clock_gettime(CLOCK_MONOTONIC, &after);
duration = (double)((long)after.tv_nsec - (long)before.tv_nsec);
duration *= 1e-9;
duration += (double)(after.tv_sec - before.tv_sec);
#endif
after = epicsMonotonicGet();
duration = (after-before)*1e-9;
priv->N[act]++;
#ifdef TIME_STATS
priv->X[act] += duration;
priv->X2[act] += duration*duration;
#endif
if(duration<priv->min[act] || init) {
priv->min[act] = duration;
init = 0;
}
if(duration>priv->max[act])
priv->max[act] = duration;
}
epicsEventMustTrigger(priv->donevent);
@@ -333,12 +324,18 @@ MAIN(dbStressTest)
testDiag("Statistics");
for(i=0; i<nworkers; i++) {
double avg[3], std[3];
unsigned j;
testDiag("Worker %u", i);
testDiag("N = %lu %lu %lu", priv[i].N[0], priv[i].N[1], priv[i].N[2]);
#ifdef TIME_STATS
testDiag("X = %g %g %g", priv[i].X[0], priv[i].X[1], priv[i].X[2]);
testDiag("X2= %g %g %g", priv[i].X2[0], priv[i].X2[1], priv[i].X2[2]);
#endif
for(j=0; j<3; j++) {
avg[j] = priv[i].X[j]/priv[i].N[j];
std[j] = sqrt( (priv[i].X2[j]/priv[i].N[j]) - avg[j]*avg[j] );
}
testDiag("N = %lu\t%lu\t%lu", priv[i].N[0], priv[i].N[1], priv[i].N[2]);
testDiag("AVG = %g us\t%g us\t%g us", avg[0]*1e6, avg[1]*1e6, avg[2]*1e6);
testDiag("STD = %g us\t%g us\t%g us", std[0]*1e6, std[1]*1e6, std[2]*1e6);
testDiag("MIN = %g us\t%g us\t%g us", priv[i].min[0]*1e6, priv[i].min[1]*1e6, priv[i].min[2]*1e6);
testDiag("MAX = %g us\t%g us\t%g us", priv[i].max[0]*1e6, priv[i].max[1]*1e6, priv[i].max[2]*1e6);
testOk1(priv[i].N[0]>0);
testOk1(priv[i].N[1]>0);
+1
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
+11
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 */
+34
View File
@@ -0,0 +1,34 @@
/*************************************************************************\
* Copyright (c) 2017 Michael Davidsaver
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <mach/mach.h>
#include <mach/mach_time.h>
#define epicsExportSharedSymbols
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "dbDefs.h"
#include "errlog.h"
#include "epicsTime.h"
#include "generalTimeSup.h"
/* see https://developer.apple.com/library/content/qa/qa1398/_index.html */
static mach_timebase_info_data_t tbinfo;
void osdMonotonicInit(void)
{
(void)mach_timebase_info(&tbinfo);
}
epicsUInt64 epicsMonotonicResolution(void)
{
return 1e-9 * tbinfo.numer / tbinfo.denom;
}
epicsUInt64 epicsMonotonicGet(void)
{
uint64_t val = mach_absolute_time();
return val * tbinfo.numer / tbinfo.denom;
}
+3
View File
@@ -15,6 +15,7 @@
#include <mach/mach.h>
#include <mach/clock.h>
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "osiSock.h"
#define epicsExportSharedSymbols
@@ -45,6 +46,8 @@ static int timeRegister(void)
generalTimeCurrentTpRegister("MachTime", \
LAST_RESORT_PRIORITY, osdTimeGetCurrent);
osdMonotonicInit();
return 1;
}
static int done = timeRegister();
+3
View File
@@ -12,6 +12,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include <epicsStdio.h>
#include <rtems.h>
#include <errno.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)
+53
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
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "dbDefs.h"
#include "errlog.h"
#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;
}
}
+3
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();
+31
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
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "dbDefs.h"
#include "errlog.h"
#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;
}
+75
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
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "dbDefs.h"
#include "errlog.h"
#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;
}
}
+3
View File
@@ -13,6 +13,7 @@
#include <string.h>
#include <errno.h>
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "osiSock.h"
#define epicsExportSharedSymbols
@@ -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();
+113
View File
@@ -0,0 +1,113 @@
/*************************************************************************\
* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h>
#include <vxWorks.h>
#include <stdio.h>
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTypes.h"
#include "epicsTime.h"
#define NS_PER_SEC 1000000000
union timebase {
UINT32 u32[2]; /* vxTimeBaseGet() */
INT64 i64; /* pentiumTscGet64() */
UINT64 u64; /* epicsMonotonicGet() */
};
#if CPU_FAMILY == PPC
#include <arch/ppc/vxPpcLib.h>
#include "epicsFindSymbol.h"
/* On PowerPC the timebase counter runs at a rate related to the
* bus clock and its frequency should always fit into a UINT32.
*/
static epicsUInt32 ticksPerSec;
#define TIMEBASEGET(TB) \
vxTimeBaseGet(&TB.u32[0], &TB.u32[1])
void osdMonotonicInit(void)
{
typedef epicsUInt32 (*sysTimeBaseFreq_t)(void);
sysTimeBaseFreq_t sysTimeBaseFreq =
(sysTimeBaseFreq_t) epicsFindSymbol("_sysTimeBaseFreq");
if (sysTimeBaseFreq) {
ticksPerSec = sysTimeBaseFreq();
if (!ticksPerSec)
printf("Warning: Failed to set up monotonic time source.\n");
/* Warn here only if the BSP routine exists but returned 0 */
}
else
ticksPerSec = 0; /* Warn on first use */
}
#elif CPU_FAMILY == I80X86
#include <arch/i86/pentiumLib.h>
#include <hwif/cpu/arch/i86/vxCpuIdLib.h>
/* On Intel the timebase counter frequency is returned by the OS as a
* UINT64. Some CPUs may count at multi-GHz rates so we need 64 bits.
*/
static epicsUInt64 ticksPerSec;
#define TIMEBASEGET(TB) \
pentiumTscGet64(&TB.i64)
void osdMonotonicInit(void)
{
ticksPerSec = vxCpuIdGetFreq();
if (!ticksPerSec)
printf("Warning: Failed to set up monotonic time source.\n");
}
#else
#error This CPU family not supported yet!
#endif
epicsUInt64 epicsMonotonicResolution(void)
{
if (!ticksPerSec)
return 0;
return NS_PER_SEC / ticksPerSec;
}
epicsUInt64 epicsMonotonicGet(void)
{
union timebase tbNow;
if (!ticksPerSec) {
static int warned = 0;
if (!warned) {
printf("Warning: Monotonic time source is not available.\n");
warned = 1;
}
return 0;
}
TIMEBASEGET(tbNow);
/* Using a long double for the calculation below to preserve
* as many bits in the mantissa as possible.
*/
return ((long double) tbNow.u64) * NS_PER_SEC / ticksPerSec;
}
+2
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();
+44 -1
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();
}