Introduced error status values for epicsTime routines. The epicsTimeERROR identifier no longer exists, so code that uses it will no longer compile (deliberately). Replace all uses of it with a specific error status value.
502 lines
13 KiB
C
502 lines
13 KiB
C
/*************************************************************************\
|
|
* Copyright (c) 2009 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>
|
|
|
|
#define epicsExportSharedSymbols
|
|
#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"
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
/* Implementation */
|
|
|
|
static void generalTime_InitOnce(void *dummy)
|
|
{
|
|
ellInit(>Pvt.timeProviders);
|
|
gtPvt.timeListLock = epicsMutexMustCreate();
|
|
|
|
ellInit(>Pvt.eventProviders);
|
|
gtPvt.eventListLock = epicsMutexMustCreate();
|
|
}
|
|
|
|
void generalTime_Init(void)
|
|
{
|
|
epicsThreadOnce(&onceId, generalTime_InitOnce, NULL);
|
|
}
|
|
|
|
|
|
int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore)
|
|
{
|
|
gtProvider *ptp;
|
|
int status = S_time_noProvider;
|
|
|
|
generalTime_Init();
|
|
|
|
epicsMutexMustLock(gtPvt.timeListLock);
|
|
for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders);
|
|
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
|
|
if (ptp->priority == ignore)
|
|
continue;
|
|
|
|
status = ptp->get.Time(pDest);
|
|
if (status == epicsTimeOK) {
|
|
/* check time is monotonic */
|
|
if (epicsTimeGreaterThanEqual(pDest, >Pvt.lastProvidedTime)) {
|
|
gtPvt.lastProvidedTime = *pDest;
|
|
if (ignore == 0)
|
|
gtPvt.lastTimeProvider = ptp;
|
|
if (pPrio)
|
|
*pPrio = ptp->priority;
|
|
} else {
|
|
int key;
|
|
|
|
*pDest = gtPvt.lastProvidedTime;
|
|
if (pPrio)
|
|
*pPrio = gtPvt.lastTimeProvider->priority;
|
|
key = epicsInterruptLock();
|
|
gtPvt.ErrorCounts++;
|
|
epicsInterruptUnlock(key);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (status && ignore == 0)
|
|
gtPvt.lastTimeProvider = NULL;
|
|
epicsMutexUnlock(gtPvt.timeListLock);
|
|
|
|
return status;
|
|
}
|
|
|
|
int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest)
|
|
{
|
|
return generalTimeGetExceptPriority(pDest, NULL, 0);
|
|
}
|
|
|
|
int epicsTimeGetCurrentInt(epicsTimeStamp *pDest)
|
|
{
|
|
gtProvider *ptp = gtPvt.lastTimeProvider;
|
|
|
|
if (ptp == NULL ||
|
|
ptp->getInt.Time == NULL)
|
|
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;
|
|
|
|
generalTime_Init();
|
|
|
|
if ((eventNumber < 0 || eventNumber >= NUM_TIME_EVENTS) &&
|
|
(eventNumber != epicsTimeEventBestTime))
|
|
return S_time_badEvent;
|
|
|
|
epicsMutexMustLock(gtPvt.eventListLock);
|
|
for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders);
|
|
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
|
|
|
|
status = ptp->get.Event(pDest, eventNumber);
|
|
if (status == epicsTimeOK) {
|
|
gtPvt.lastEventProvider = ptp;
|
|
if (pPrio)
|
|
*pPrio = ptp->priority;
|
|
|
|
if (eventNumber == epicsTimeEventBestTime) {
|
|
if (epicsTimeGreaterThanEqual(pDest,
|
|
>Pvt.lastProvidedBestTime)) {
|
|
gtPvt.lastProvidedBestTime = *pDest;
|
|
} else {
|
|
int key;
|
|
|
|
*pDest = gtPvt.lastProvidedBestTime;
|
|
key = epicsInterruptLock();
|
|
gtPvt.ErrorCounts++;
|
|
epicsInterruptUnlock(key);
|
|
}
|
|
} else {
|
|
if (epicsTimeGreaterThanEqual(pDest,
|
|
>Pvt.eventTime[eventNumber])) {
|
|
gtPvt.eventTime[eventNumber] = *pDest;
|
|
} else {
|
|
int key;
|
|
|
|
*pDest = gtPvt.eventTime[eventNumber];
|
|
key = epicsInterruptLock();
|
|
gtPvt.ErrorCounts++;
|
|
epicsInterruptUnlock(key);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (status)
|
|
gtPvt.lastEventProvider = NULL;
|
|
epicsMutexUnlock(gtPvt.eventListLock);
|
|
|
|
return status;
|
|
}
|
|
|
|
int epicsShareAPI 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)
|
|
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);
|
|
}
|
|
|
|
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 == ptp->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, >Pvt.eventProviders, gtPvt.eventListLock);
|
|
|
|
return epicsTimeOK;
|
|
}
|
|
|
|
int generalTimeAddIntEventProvider(const char *name, int priority,
|
|
TIMEEVENTFUN getEvent)
|
|
{
|
|
gtProvider *ptp = findProvider(>Pvt.eventProviders, gtPvt.eventListLock,
|
|
name, priority);
|
|
if (ptp == NULL)
|
|
return S_time_noProvider;
|
|
|
|
ptp->getInt.Event = getEvent;
|
|
|
|
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, >Pvt.timeProviders, gtPvt.timeListLock);
|
|
|
|
return epicsTimeOK;
|
|
}
|
|
|
|
int generalTimeAddIntCurrentProvider(const char *name, int priority,
|
|
TIMECURRENTFUN getTime)
|
|
{
|
|
gtProvider *ptp = findProvider(>Pvt.timeProviders, gtPvt.timeListLock,
|
|
name, priority);
|
|
if (ptp == NULL)
|
|
return S_time_noProvider;
|
|
|
|
ptp->getInt.Time = getTime;
|
|
|
|
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(>Pvt.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(>Pvt.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(>Pvt.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(>Pvt.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(>Pvt.timeProviders);
|
|
epicsMutexUnlock(gtPvt.timeListLock);
|
|
return ptp ? ptp->name : NULL;
|
|
}
|