Files
epics-base/modules/libcom/src/osi/epicsGeneralTime.c
Dirk Zimoch 36a8b51d8e CleanupWhitespace
removed spaces at end of line

replaced tabs with spaces
2020-05-20 14:48:09 -07:00

661 lines
18 KiB
C

/*************************************************************************\
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2008 Diamond Light Source Ltd
* Copyright (c) 2004 Oak Ridge National Laboratory
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* Original Authors: David H. Thompson & Sheng Peng (ORNL) */
#include <string.h>
#include <stdlib.h>
#include "epicsTypes.h"
#include "epicsEvent.h"
#include "epicsMutex.h"
#include "epicsMessageQueue.h"
#include "epicsString.h"
#include "epicsStdioRedirect.h"
#include "epicsThread.h"
#include "epicsTime.h"
#include "epicsTimer.h"
#include "epicsInterrupt.h"
#include "osiSock.h"
#include "ellLib.h"
#include "errlog.h"
#include "cantProceed.h"
#include "envDefs.h"
#include "generalTimeSup.h"
#include "epicsGeneralTime.h"
/* Change 'undef' to 'define' to turn on debug statements: */
#undef DEBUG_GENERAL_TIME
#ifdef DEBUG_GENERAL_TIME
int generalTimeDebug = 10;
# define IFDEBUG(n) \
if (generalTimeDebug >= n) /* block or statement */
#else
# define IFDEBUG(n) \
if(0) /* Compiler will elide the block or statement */
#endif
/* Declarations */
typedef struct {
ELLNODE node;
char *name;
int priority;
union {
TIMECURRENTFUN Time;
TIMEEVENTFUN Event;
} get;
union {
TIMECURRENTFUN Time;
TIMEEVENTFUN Event;
} getInt;
} gtProvider;
static struct {
epicsMutexId timeListLock;
ELLLIST timeProviders;
gtProvider *lastTimeProvider;
epicsTimeStamp lastProvidedTime;
epicsMutexId eventListLock;
ELLLIST eventProviders;
gtProvider *lastEventProvider;
epicsTimeStamp eventTime[NUM_TIME_EVENTS];
epicsTimeStamp lastProvidedBestTime;
int ErrorCounts;
} gtPvt;
static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
static const char * const tsfmt = "%Y-%m-%d %H:%M:%S.%09f";
/* defined in osiClockTime.c or osdTime.cpp */
int osdTimeGetCurrent ( epicsTimeStamp *pDest );
/* cleared if/when gtPvt.timeProviders contains more than the default osdTimeGetCurrent() */
static int useOsdGetCurrent = 1;
/* Implementation */
static void generalTime_InitOnce(void *dummy)
{
ellInit(&gtPvt.timeProviders);
gtPvt.timeListLock = epicsMutexMustCreate();
ellInit(&gtPvt.eventProviders);
gtPvt.eventListLock = epicsMutexMustCreate();
IFDEBUG(1)
printf("General Time Initialized\n");
}
void generalTime_Init(void)
{
epicsThreadOnce(&onceId, generalTime_InitOnce, NULL);
}
int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore)
{
gtProvider *ptp;
int status = S_time_noProvider;
if(useOsdGetCurrent)
return osdTimeGetCurrent(pDest);
generalTime_Init();
IFDEBUG(2)
printf("generalTimeGetExceptPriority(ignore=%d)\n", ignore);
epicsMutexMustLock(gtPvt.timeListLock);
for (ptp = (gtProvider *)ellFirst(&gtPvt.timeProviders);
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
if ((ignore > 0 && ptp->priority == ignore) ||
(ignore < 0 && ptp->priority != -ignore))
continue;
status = ptp->get.Time(pDest);
if (status == epicsTimeOK) {
/* No ratchet, time from this routine may go backwards */
if (pPrio)
*pPrio = ptp->priority;
break;
}
else IFDEBUG(2)
printf("gTGExP provider '%s' returned error\n", ptp->name);
}
epicsMutexUnlock(gtPvt.timeListLock);
IFDEBUG(2) {
if (ptp && status == epicsTimeOK) {
char buff[40];
epicsTimeToStrftime(buff, sizeof(buff), tsfmt, pDest);
printf("gTGExP returning %s from provider '%s'\n",
buff, ptp->name);
}
else
printf("gTGExP returning error\n");
}
return status;
}
int epicsStdCall epicsTimeGetCurrent(epicsTimeStamp *pDest)
{
gtProvider *ptp;
int status = S_time_noProvider;
epicsTimeStamp ts;
if(useOsdGetCurrent)
return osdTimeGetCurrent(pDest);
generalTime_Init();
IFDEBUG(20)
printf("epicsTimeGetCurrent()\n");
epicsMutexMustLock(gtPvt.timeListLock);
for (ptp = (gtProvider *)ellFirst(&gtPvt.timeProviders);
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
status = ptp->get.Time(&ts);
if (status == epicsTimeOK) {
/* check time is monotonic */
if (epicsTimeGreaterThanEqual(&ts, &gtPvt.lastProvidedTime)) {
*pDest = ts;
gtPvt.lastProvidedTime = ts;
gtPvt.lastTimeProvider = ptp;
} else {
int key;
*pDest = gtPvt.lastProvidedTime;
key = epicsInterruptLock();
gtPvt.ErrorCounts++;
epicsInterruptUnlock(key);
IFDEBUG(10) {
char last[40], buff[40];
epicsTimeToStrftime(last, sizeof(last), tsfmt,
&gtPvt.lastProvidedTime);
epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts);
printf("eTGC provider '%s' returned older time\n"
" %s, using %s instead\n", ptp->name, buff, last);
}
}
break;
}
}
if (status)
gtPvt.lastTimeProvider = NULL;
epicsMutexUnlock(gtPvt.timeListLock);
IFDEBUG(20) {
if (ptp && status == epicsTimeOK) {
char buff[40];
epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts);
printf("eTGC returning %s from provider '%s'\n",
buff, ptp->name);
}
else
printf("eTGC returning error\n");
}
return status;
}
int epicsTimeGetMonotonic ( epicsTimeStamp * pDest )
{
epicsUInt64 now = epicsMonotonicGet();
pDest->nsec = now%1000000000ul;
pDest->secPastEpoch = now/1000000000ul;
return 0;
}
int epicsTimeGetCurrentInt(epicsTimeStamp *pDest)
{
gtProvider *ptp = gtPvt.lastTimeProvider;
if (ptp == NULL ||
ptp->getInt.Time == NULL) {
IFDEBUG(20)
epicsInterruptContextMessage("eTGCInt: No support\n");
return S_time_noProvider;
}
return ptp->getInt.Time(pDest);
}
static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber,
int *pPrio)
{
gtProvider *ptp;
int status = S_time_noProvider;
epicsTimeStamp ts;
STATIC_ASSERT ( epicsTimeEventBestTime == -1 );
generalTime_Init();
IFDEBUG(2)
printf("generalTimeGetEventPriority(eventNum=%d)\n", eventNumber);
if (eventNumber < epicsTimeEventBestTime)
return S_time_badEvent;
epicsMutexMustLock(gtPvt.eventListLock);
for (ptp = (gtProvider *)ellFirst(&gtPvt.eventProviders);
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
status = ptp->get.Event(&ts, eventNumber);
if (status == epicsTimeOK) {
gtPvt.lastEventProvider = ptp;
if (pPrio)
*pPrio = ptp->priority;
if (eventNumber >= NUM_TIME_EVENTS) {
*pDest = ts;
} else if (eventNumber == epicsTimeEventBestTime) {
if (epicsTimeGreaterThanEqual(&ts,
&gtPvt.lastProvidedBestTime)) {
*pDest = ts;
gtPvt.lastProvidedBestTime = ts;
} else {
int key;
*pDest = gtPvt.lastProvidedBestTime;
key = epicsInterruptLock();
gtPvt.ErrorCounts++;
epicsInterruptUnlock(key);
IFDEBUG(10) {
char last[40], buff[40];
epicsTimeToStrftime(last, sizeof(last), tsfmt,
&gtPvt.lastProvidedBestTime);
epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts);
printf("gTGEvP provider '%s' returned older time\n"
" %s, using %s instead\n",
ptp->name, buff, last);
}
}
} else {
if (epicsTimeGreaterThanEqual(&ts,
&gtPvt.eventTime[eventNumber])) {
*pDest = ts;
gtPvt.eventTime[eventNumber] = ts;
} else {
int key;
*pDest = gtPvt.eventTime[eventNumber];
key = epicsInterruptLock();
gtPvt.ErrorCounts++;
epicsInterruptUnlock(key);
IFDEBUG(10) {
char last[40], buff[40];
epicsTimeToStrftime(last, sizeof(last), tsfmt,
&gtPvt.lastProvidedBestTime);
epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts);
printf("gTGEvP provider '%s' returned older time\n"
" %s, using %s instead\n",
ptp->name, buff, last);
}
}
}
break;
}
else IFDEBUG(2)
printf("gTGEvP provider '%s' returned error\n", ptp->name);
}
if (status)
gtPvt.lastEventProvider = NULL;
epicsMutexUnlock(gtPvt.eventListLock);
IFDEBUG(10) {
if (ptp && status == epicsTimeOK) {
char buff[40];
epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts);
printf("gTGEvP returning %s from provider '%s'\n",
buff, ptp->name);
}
else
printf("gTGEvP returning error\n");
}
return status;
}
int epicsStdCall epicsTimeGetEvent(epicsTimeStamp *pDest, int eventNumber)
{
if (eventNumber == epicsTimeEventCurrentTime) {
return epicsTimeGetCurrent(pDest);
} else {
return generalTimeGetEventPriority(pDest, eventNumber, NULL);
}
}
int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber)
{
if (eventNumber == epicsTimeEventCurrentTime) {
return epicsTimeGetCurrentInt(pDest);
} else {
gtProvider *ptp = gtPvt.lastEventProvider;
if (ptp == NULL ||
ptp->getInt.Event == NULL) {
IFDEBUG(20)
epicsInterruptContextMessage("eTGEvInt: No support\n");
return S_time_noProvider;
}
return ptp->getInt.Event(pDest, eventNumber);
}
}
/* Provider Registration */
static void insertProvider(gtProvider *ptp, ELLLIST *plist, epicsMutexId lock)
{
gtProvider *ptpref;
epicsMutexMustLock(lock);
for (ptpref = (gtProvider *)ellFirst(plist);
ptpref; ptpref = (gtProvider *)ellNext(&ptpref->node)) {
if (ptpref->priority > ptp->priority)
break;
}
if (ptpref) {
/* Found a provider below the new one */
ptpref = (gtProvider *)ellPrevious(&ptpref->node);
ellInsert(plist, &ptpref->node, &ptp->node);
} else {
ellAdd(plist, &ptp->node);
}
/* Check to see if we have more than just the OS default time source */
if(plist==&gtPvt.timeProviders && (ellCount(plist)!=1 || ptp->get.Time!=&osdTimeGetCurrent)) {
useOsdGetCurrent = 0;
}
epicsMutexUnlock(lock);
}
static gtProvider * findProvider(ELLLIST *plist, epicsMutexId lock,
const char *name, int priority)
{
gtProvider *ptp;
epicsMutexMustLock(lock);
for (ptp = (gtProvider *)ellFirst(plist);
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
if (ptp->priority == priority &&
!strcmp(ptp->name, name))
break;
}
epicsMutexUnlock(lock);
return ptp;
}
int generalTimeRegisterEventProvider(const char *name, int priority,
TIMEEVENTFUN getEvent)
{
gtProvider *ptp;
generalTime_Init();
if (name == NULL || getEvent == NULL)
return S_time_badArgs;
ptp = (gtProvider *)malloc(sizeof(gtProvider));
if (ptp == NULL)
return S_time_noMemory;
ptp->name = epicsStrDup(name);
ptp->priority = priority;
ptp->get.Event = getEvent;
ptp->getInt.Event = NULL;
insertProvider(ptp, &gtPvt.eventProviders, gtPvt.eventListLock);
IFDEBUG(1)
printf("Registered event provider '%s' at %d\n", name, priority);
return epicsTimeOK;
}
int generalTimeAddIntEventProvider(const char *name, int priority,
TIMEEVENTFUN getEvent)
{
gtProvider *ptp = findProvider(&gtPvt.eventProviders, gtPvt.eventListLock,
name, priority);
if (ptp == NULL)
return S_time_noProvider;
ptp->getInt.Event = getEvent;
IFDEBUG(1)
printf("Event provider '%s' is interrupt-callable\n", name);
return epicsTimeOK;
}
int generalTimeRegisterCurrentProvider(const char *name, int priority,
TIMECURRENTFUN getTime)
{
gtProvider *ptp;
generalTime_Init();
if (name == NULL || getTime == NULL)
return S_time_badArgs;
ptp = (gtProvider *)malloc(sizeof(gtProvider));
if (ptp == NULL)
return S_time_noMemory;
ptp->name = epicsStrDup(name);
ptp->priority = priority;
ptp->get.Time = getTime;
ptp->getInt.Time = NULL;
insertProvider(ptp, &gtPvt.timeProviders, gtPvt.timeListLock);
IFDEBUG(1)
printf("Registered time provider '%s' at %d\n", name, priority);
return epicsTimeOK;
}
int generalTimeAddIntCurrentProvider(const char *name, int priority,
TIMECURRENTFUN getTime)
{
gtProvider *ptp = findProvider(&gtPvt.timeProviders, gtPvt.timeListLock,
name, priority);
if (ptp == NULL)
return S_time_noProvider;
ptp->getInt.Time = getTime;
IFDEBUG(1)
printf("Time provider '%s' is interrupt-callable\n", name);
return epicsTimeOK;
}
/*
* Provide an optional "last resort" provider for Event Time.
*
* This is deliberately optional, as it represents site policy.
* It is intended to be installed as an EventTime provider at the lowest
* priority, to return the current time for an event if there is no
* better time provider for event times.
*
* Typically, this will only be used during startup, or a time-provider
* resynchronisation, where events are being generated by the event system
* but the time provided by the system is not yet valid.
*/
static int lastResortGetEvent(epicsTimeStamp *timeStamp, int eventNumber)
{
return epicsTimeGetCurrent(timeStamp);
}
int installLastResortEventProvider(void)
{
return generalTimeEventTpRegister("Last Resort Event",
LAST_RESORT_PRIORITY, lastResortGetEvent);
}
/* Status Report */
long generalTimeReport(int level)
{
int items;
if (onceId == EPICS_THREAD_ONCE_INIT) {
printf("General time framework not yet initialized.\n");
return epicsTimeOK;
}
printf("Backwards time errors prevented %u times.\n\n",
generalTimeGetErrorCounts());
/* Use an output buffer to avoid holding mutexes during printing */
printf("Current Time Providers:\n");
epicsMutexMustLock(gtPvt.timeListLock);
if ((items = ellCount(&gtPvt.timeProviders))) {
gtProvider *ptp;
char *message; /* Temporary output buffer */
char *pout;
message = calloc(items, 80 * 2); /* Each provider needs 2 lines */
if (!message) {
epicsMutexUnlock(gtPvt.timeListLock);
printf("Out of memory\n");
return S_time_noMemory;
}
pout = message;
for (ptp = (gtProvider *)ellFirst(&gtPvt.timeProviders);
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
pout += sprintf(pout, " \"%s\", priority = %d\n",
ptp->name, ptp->priority);
if (level) {
epicsTimeStamp tempTS;
if (ptp->get.Time(&tempTS) == epicsTimeOK) {
char tempTSText[40];
epicsTimeToStrftime(tempTSText, sizeof(tempTSText),
"%Y-%m-%d %H:%M:%S.%06f", &tempTS);
pout += sprintf(pout, "\tCurrent Time is %s.\n",
tempTSText);
} else {
pout += sprintf(pout, "\tCurrent Time not available\n");
}
}
}
epicsMutexUnlock(gtPvt.timeListLock);
puts(message);
free(message);
} else {
epicsMutexUnlock(gtPvt.timeListLock);
printf("\tNo Providers registered.\n");
}
printf("Event Time Providers:\n");
epicsMutexMustLock(gtPvt.eventListLock);
if ((items = ellCount(&gtPvt.eventProviders)))
{
gtProvider *ptp;
char *message; /* Temporary output buffer */
char *pout;
message = calloc(items, 80); /* Each provider needs 1 line, */
if (!message) {
epicsMutexUnlock(gtPvt.eventListLock);
printf("Out of memory\n");
return S_time_noMemory;
}
pout = message;
for (ptp = (gtProvider *)ellFirst(&gtPvt.eventProviders);
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
pout += sprintf(pout, " \"%s\", priority = %d\n",
ptp->name, ptp->priority);
}
epicsMutexUnlock(gtPvt.eventListLock);
puts(message);
free(message);
}
else
{
epicsMutexUnlock(gtPvt.eventListLock);
printf("\tNo Providers registered.\n");
}
return epicsTimeOK;
}
/*
* Access to internal status values.
*/
void generalTimeResetErrorCounts(void)
{
int key = epicsInterruptLock();
gtPvt.ErrorCounts = 0;
epicsInterruptUnlock(key);
}
int generalTimeGetErrorCounts(void)
{
int key = epicsInterruptLock();
int errors = gtPvt.ErrorCounts;
epicsInterruptUnlock(key);
return errors;
}
const char * generalTimeCurrentProviderName(void)
{
if (gtPvt.lastTimeProvider)
return gtPvt.lastTimeProvider->name;
return NULL;
}
const char * generalTimeEventProviderName(void)
{
if (gtPvt.lastEventProvider)
return gtPvt.lastEventProvider->name;
return NULL;
}
const char * generalTimeHighestCurrentName(void)
{
gtProvider *ptp;
epicsMutexMustLock(gtPvt.timeListLock);
ptp = (gtProvider *)ellFirst(&gtPvt.timeProviders);
epicsMutexUnlock(gtPvt.timeListLock);
return ptp ? ptp->name : NULL;
}