Files
epics-base/modules/database/test/ioc/db/callbackTest.c
Michael Davidsaver 0f8876de67 Merge branch '3.15' into 7.0
* 3.15: (28 commits)
  update RELEASE_NOTES
  add option EPICS_NO_CALLBACK
  replace CALLBACK -> epicsCallback
  Update dbTest.c
  Remove links to wiki-ext
  Add POD annotations from Wiki to subArrayRecord and menuAlarmStat
  Rename subArrayRecord.dbd and menuAlarmStat.dbd to .pod
  Add POD annotations to longoutRecord from Wiki
  Rename longoutRecord.dbd longoutRecord.dbd.pod
  Add POD annotations to longinRecord from Wiki
  Rename longinRecord.dbd longinRecord.dbd.pod
  Add POD annotations to subRecord from Wiki
  Rename subRecord.dbd subRecord.dbd.pod
  Add POD annotations to selRecord from Wiki
  Rename selRecord.dbd selRecord.dbd.pod
  Add POD annotations to seqRecord from Wiki
  Rename seqRecord.dbd seqRecord.dbd.pod
  Fix menu declaration test too
  Add redefinition guard to menu-generated typedefs
  Updates to existing .dbd.pod texts, add event and fanout from wiki
  ...

# Conflicts:
#	documentation/README.1st
#	documentation/README.html
#	modules/database/src/ioc/db/callback.h
#	modules/database/src/ioc/db/dbNotify.c
#	modules/database/src/ioc/db/menuAlarmStat.dbd
#	modules/database/src/ioc/db/menuFtype.dbd
#	modules/database/src/std/rec/compressRecord.dbd.pod
#	modules/database/src/std/rec/eventRecord.dbd
#	modules/database/src/std/rec/fanoutRecord.dbd
#	modules/database/src/std/rec/longinRecord.dbd
#	modules/database/src/std/rec/longoutRecord.dbd
#	modules/database/src/std/rec/selRecord.dbd
#	modules/database/src/std/rec/seqRecord.dbd
#	modules/database/src/std/rec/subArrayRecord.dbd
#	modules/database/src/std/rec/subRecord.dbd
#	modules/libcom/src/iocsh/menuAlarmStat.dbd.pod
#	modules/libcom/src/iocsh/menuFtype.dbd.pod
#	src/ioc/db/menuAlarmStat.dbd
#	src/ioc/db/menuFtype.dbd

Manually fix some move+rename
Make additional CALLBACK -> epicsCallback
preserve INT64 in menuFtype
preserve OLDSIM et al
2019-09-09 19:29:58 -07:00

207 lines
6.1 KiB
C

/*************************************************************************\
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* Copyright (c) 2013 ITER Organization.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* Author: Marty Kraimer Date: 26JAN2000 */
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "callback.h"
#include "cantProceed.h"
#include "epicsThread.h"
#include "epicsEvent.h"
#include "epicsTime.h"
#include "epicsUnitTest.h"
#include "testMain.h"
/*
* This test checks both immediate and delayed callbacks in two steps.
* In the first step (pass1) NCALLBACKS immediate callbacks are queued.
* As each is run it starts a second delayed callback (pass2).
* The last delayed callback which runs signals an epicsEvent
* to the main thread.
*
* Two time intervals are measured. The time to queue and run each of
* the immediate callbacks, and the actual delay of the delayed callback.
*
* Slow callbacks no longer fail the test, they just emit a diagnostic.
*/
#define NCALLBACKS 169
#define DELAY_QUANTUM 0.25
#define TEST_DELAY(i) ((i / NUM_CALLBACK_PRIORITIES) * DELAY_QUANTUM)
typedef struct myPvt {
epicsCallback cb1;
epicsCallback cb2;
epicsTimeStamp pass1Time;
epicsTimeStamp pass2Time;
double delay;
int pass;
int resultFail;
} myPvt;
epicsEventId finished;
static void myCallback(epicsCallback *pCallback)
{
myPvt *pmyPvt;
callbackGetUser(pmyPvt, pCallback);
pmyPvt->pass++;
if (pmyPvt->pass == 1) {
epicsTimeGetCurrent(&pmyPvt->pass1Time);
callbackRequestDelayed(&pmyPvt->cb2, pmyPvt->delay);
} else if (pmyPvt->pass == 2) {
epicsTimeGetCurrent(&pmyPvt->pass2Time);
} else {
pmyPvt->resultFail = 1;
return;
}
}
static void finalCallback(epicsCallback *pCallback)
{
myCallback(pCallback);
epicsEventSignal(finished);
}
static void updateStats(double *stats, double val)
{
if (stats[0] > val) stats[0] = val;
if (stats[1] < val) stats[1] = val;
stats[2] += val;
stats[3] += pow(val, 2.0);
stats[4] += 1.;
}
static void printStats(double *stats, const char* tag) {
testDiag("Priority %4s min/avg/max/sigma = %f / %f / %f / %f",
tag, stats[0], stats[2]/stats[4], stats[1],
sqrt(stats[4]*stats[3]-pow(stats[2], 2.0))/stats[4]);
}
MAIN(callbackTest)
{
myPvt *pcbt[NCALLBACKS];
epicsTimeStamp start;
int i, j, slowups, faults;
/* Statistics: min/max/sum/sum^2/n for each priority */
double setupError[NUM_CALLBACK_PRIORITIES][5];
double timeError[NUM_CALLBACK_PRIORITIES][5];
double defaultError[5] = {1,-1,0,0,0};
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++)
for (j = 0; j < 5; j++)
setupError[i][j] = timeError[i][j] = defaultError[j];
testPlan(2);
callbackInit();
epicsThreadSleep(1.0);
finished = epicsEventMustCreate(epicsEventEmpty);
for (i = 0; i < NCALLBACKS ; i++) {
pcbt[i] = callocMustSucceed(1, sizeof(myPvt), "pcbt");
callbackSetCallback(myCallback, &pcbt[i]->cb1);
callbackSetCallback(myCallback, &pcbt[i]->cb2);
callbackSetUser(pcbt[i], &pcbt[i]->cb1);
callbackSetUser(pcbt[i], &pcbt[i]->cb2);
callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb1);
callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb2);
pcbt[i]->delay = TEST_DELAY(i);
pcbt[i]->pass = 0;
}
/* Last callback is special */
callbackSetCallback(finalCallback, &pcbt[NCALLBACKS-1]->cb2);
callbackSetPriority(0, &pcbt[NCALLBACKS-1]->cb1);
callbackSetPriority(0, &pcbt[NCALLBACKS-1]->cb2);
pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0;
pcbt[NCALLBACKS-1]->pass = 0;
testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok");
for (i = 0; i < NCALLBACKS ; i++) {
callbackRequest(&pcbt[i]->cb1);
}
testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay);
epicsEventWait(finished);
slowups = 0;
faults = 0;
for (i = 0; i < NCALLBACKS ; i++) {
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
testDiag("callback setup fault #%d: pass = %d for delay = %.02f",
++faults, pcbt[i]->pass, pcbt[i]->delay);
else {
double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start);
if (fabs(delta) >= 0.05) {
slowups++;
testDiag("callback %.02f setup time |%f| >= 0.05 seconds",
pcbt[i]->delay, delta);
}
updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta);
}
}
testOk(faults == 0, "%d faults during callback setup", faults);
if (slowups)
testDiag("%d slowups during callback setup", slowups);
slowups = 0;
for (i = 0; i < NCALLBACKS ; i++) {
double delta, error;
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
continue;
delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time);
error = delta - pcbt[i]->delay;
if (fabs(error) >= 0.05) {
slowups++;
testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05",
pcbt[i]->delay, error);
}
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
}
if (slowups)
testDiag("%d slowups during callback setup", slowups);
testDiag("Setup time statistics");
printStats(setupError[0], "LOW");
printStats(setupError[1], "MID");
printStats(setupError[2], "HIGH");
testDiag("Delay time statistics");
printStats(timeError[0], "LOW");
printStats(timeError[1], "MID");
printStats(timeError[2], "HIGH");
for (i = 0; i < NCALLBACKS ; i++) {
free(pcbt[i]);
}
callbackStop();
callbackCleanup();
return testDone();
}