diff --git a/src/env/envDefs.h b/src/env/envDefs.h index 650f8ee58..13df94733 100644 --- a/src/env/envDefs.h +++ b/src/env/envDefs.h @@ -65,6 +65,7 @@ epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS; epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH; epicsShareExtern const ENV_PARAM EPICS_TIMEZONE; epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; +epicsShareExtern const ENV_PARAM EPICS_IOC_IGNORE_SERVERS; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_LIMIT; diff --git a/src/osi/Makefile b/src/osi/Makefile index c06a862ed..ecbf4c23b 100644 --- a/src/osi/Makefile +++ b/src/osi/Makefile @@ -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 diff --git a/src/osi/epicsTime.h b/src/osi/epicsTime.h index 149f6f2d3..862bc22d2 100644 --- a/src/osi/epicsTime.h +++ b/src/osi/epicsTime.h @@ -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 */ diff --git a/src/osi/os/Darwin/osdMonotonic.c b/src/osi/os/Darwin/osdMonotonic.c new file mode 100644 index 000000000..2f81d002b --- /dev/null +++ b/src/osi/os/Darwin/osdMonotonic.c @@ -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 +#include + +#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; +} diff --git a/src/osi/os/Darwin/osdTime.cpp b/src/osi/os/Darwin/osdTime.cpp index 72b3dc874..315caeb9b 100644 --- a/src/osi/os/Darwin/osdTime.cpp +++ b/src/osi/os/Darwin/osdTime.cpp @@ -15,6 +15,7 @@ #include #include +#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(); diff --git a/src/osi/os/RTEMS/osdTime.cpp b/src/osi/os/RTEMS/osdTime.cpp index 4947c568e..1350c1cf2 100644 --- a/src/osi/os/RTEMS/osdTime.cpp +++ b/src/osi/os/RTEMS/osdTime.cpp @@ -12,6 +12,7 @@ #include #include +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include #include #include @@ -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) diff --git a/src/osi/os/WIN32/osdMonotonic.c b/src/osi/os/WIN32/osdMonotonic.c new file mode 100644 index 000000000..e7da25da4 --- /dev/null +++ b/src/osi/os/WIN32/osdMonotonic.c @@ -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 + +#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; + } +} diff --git a/src/osi/os/WIN32/osdTime.cpp b/src/osi/os/WIN32/osdTime.cpp index 2ee0d3066..348fe7c2f 100644 --- a/src/osi/os/WIN32/osdTime.cpp +++ b/src/osi/os/WIN32/osdTime.cpp @@ -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(); diff --git a/src/osi/os/iOS/osdMonotonic.c b/src/osi/os/iOS/osdMonotonic.c new file mode 100644 index 000000000..0eab29c91 --- /dev/null +++ b/src/osi/os/iOS/osdMonotonic.c @@ -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 + +#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; +} diff --git a/src/osi/os/posix/osdMonotonic.c b/src/osi/os/posix/osdMonotonic.c new file mode 100644 index 000000000..00c9c2871 --- /dev/null +++ b/src/osi/os/posix/osdMonotonic.c @@ -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 #include +#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(); diff --git a/src/osi/os/vxWorks/osdMonotonic.c b/src/osi/os/vxWorks/osdMonotonic.c new file mode 100644 index 000000000..e0c6b2278 --- /dev/null +++ b/src/osi/os/vxWorks/osdMonotonic.c @@ -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 +#include + +#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 +#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 +#include + +/* 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; +} diff --git a/src/osi/os/vxWorks/osdTime.cpp b/src/osi/os/vxWorks/osdTime.cpp index 4db375fbb..fdfefb704 100644 --- a/src/osi/os/vxWorks/osdTime.cpp +++ b/src/osi/os/vxWorks/osdTime.cpp @@ -16,6 +16,7 @@ #include #include +#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(); diff --git a/test/epicsTimeTest.cpp b/test/epicsTimeTest.cpp index 55ad43cdd..8eb8de6d4 100644 --- a/test/epicsTimeTest.cpp +++ b/test/epicsTimeTest.cpp @@ -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