diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index dc0fdcdb3..98913581e 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -3,16 +3,28 @@
- EPICS Base R3.14.12.6 Release Notes
+ EPICS Base R3.14.12.7 Release Notes
-EPICS Base Release 3.14.12.6
+EPICS Base Release 3.14.12.7
-Changes between 3.14.12.5 and 3.14.12.6
+Changes between 3.14.12.6 and 3.14.12.7
+Checking Periodic Scan Rates
+
+Code has been added to the IOC startup to better protect it against bad
+periodic scan rates, including against locales where .
is not
+accepted as a decimal separator character. If the scan period in a menuScan
+choice string cannot be parsed, the associated periodic scan thread will no
+longer be started by the IOC and a warning message will be displayed at iocInit
+time. The scanppl command will also flag the faulty menuScan value.
+
+
+Changes between 3.14.12.5 and 3.14.12.6
+
Launchpad Bug-fixes
In addition to the more detailed change descriptions below, the following
@@ -277,8 +289,6 @@ it up. This release patches the readline support code to clean up automatically
by registering an epicsAtExit() routine.
-EPICS Base Release 3.14.12.5
-
Changes between 3.14.12.4 and 3.14.12.5
aoRecord raw conversion overflows
diff --git a/src/db/dbScan.c b/src/db/dbScan.c
index f764a1760..fb13cb0c3 100644
--- a/src/db/dbScan.c
+++ b/src/db/dbScan.c
@@ -148,8 +148,11 @@ static void scanShutdown(void *arg)
interruptAccept = FALSE;
for (i = 0; i < nPeriodic; i++) {
- papPeriodic[i]->scanCtl = ctlExit;
- epicsEventSignal(papPeriodic[i]->loopEvent);
+ periodic_scan_list *ppsl = papPeriodic[i];
+
+ if (!ppsl) continue;
+ ppsl->scanCtl = ctlExit;
+ epicsEventSignal(ppsl->loopEvent);
epicsEventWait(startStopEvent);
}
@@ -182,16 +185,24 @@ void scanRun(void)
interruptAccept = TRUE;
scanCtl = ctlRun;
- for (i = 0; i < nPeriodic; i++)
- papPeriodic[i]->scanCtl = ctlRun;
+ for (i = 0; i < nPeriodic; i++) {
+ periodic_scan_list *ppsl = papPeriodic[i];
+
+ if (!ppsl) continue;
+ ppsl->scanCtl = ctlRun;
+ }
}
void scanPause(void)
{
int i;
- for (i = nPeriodic - 1; i >= 0; --i)
- papPeriodic[i]->scanCtl = ctlPause;
+ for (i = nPeriodic - 1; i >= 0; --i) {
+ periodic_scan_list *ppsl = papPeriodic[i];
+
+ if (!ppsl) continue;
+ ppsl->scanCtl = ctlPause;
+ }
scanCtl = ctlPause;
interruptAccept = FALSE;
@@ -275,9 +286,11 @@ void scanAdd(struct dbCommon *precord)
piosl += prio; /* get piosl for correct priority*/
addToList(precord, &piosl->scan_list);
} else if (scan >= SCAN_1ST_PERIODIC) {
- addToList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list);
+ periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC];
+
+ if (ppsl)
+ addToList(precord, &ppsl->scan_list);
}
- return;
}
void scanDelete(struct dbCommon *precord)
@@ -348,28 +361,48 @@ void scanDelete(struct dbCommon *precord)
piosl += prio; /*get piosl for correct priority*/
deleteFromList(precord, &piosl->scan_list);
} else if (scan >= SCAN_1ST_PERIODIC) {
- deleteFromList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list);
+ periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC];
+
+ if (ppsl)
+ deleteFromList(precord, &ppsl->scan_list);
}
- return;
}
double scanPeriod(int scan) {
+ periodic_scan_list *ppsl;
+
scan -= SCAN_1ST_PERIODIC;
if (scan < 0 || scan >= nPeriodic)
return 0.0;
- return papPeriodic[scan]->period;
+ ppsl = papPeriodic[scan];
+ return ppsl ? ppsl->period : 0.0;
}
-
-int scanppl(double period) /* print periodic list */
+
+int scanppl(double period) /* print periodic scan list(s) */
{
- periodic_scan_list *ppsl;
+ dbMenu *pmenu = dbFindMenu(pdbbase, "menuScan");
char message[80];
int i;
+ if (!pmenu || !papPeriodic) {
+ printf("scanppl: dbScan subsystem not initialized\n");
+ return -1;
+ }
+
for (i = 0; i < nPeriodic; i++) {
- ppsl = papPeriodic[i];
- if (ppsl == NULL) continue;
- if (period > 0.0 && (fabs(period - ppsl->period) >.05)) continue;
+ periodic_scan_list *ppsl = papPeriodic[i];
+
+ if (!ppsl) {
+ const char *choice = pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC];
+
+ printf("Periodic scan list for SCAN = '%s' not initialized\n",
+ choice);
+ continue;
+ }
+ if (period > 0.0 &&
+ (fabs(period - ppsl->period) > 0.05))
+ continue;
+
sprintf(message, "Records with SCAN = '%s' (%lu over-runs):",
ppsl->name, ppsl->overruns);
printList(&ppsl->scan_list, message);
@@ -588,7 +621,7 @@ static void periodicTask(void *arg)
if (++overruns >= 10 &&
epicsTimeDiffInSeconds(&now, &reported) > report_delay) {
errlogPrintf("\ndbScan warning from '%s' scan thread:\n"
- "\tScan processing averages %.2f seconds (%.2f .. %.2f).\n"
+ "\tScan processing averages %.3f seconds (%.3f .. %.3f).\n"
"\tOver-runs have now happened %u times in a row.\n"
"\tTo fix this, move some records to a slower scan rate.\n",
ppsl->name, ppsl->period + overtime / overruns,
@@ -617,25 +650,30 @@ static void periodicTask(void *arg)
static void initPeriodic(void)
{
- dbMenu *pmenu;
- periodic_scan_list *ppsl;
+ dbMenu *pmenu = dbFindMenu(pdbbase, "menuScan");
int i;
- pmenu = dbFindMenu(pdbbase, "menuScan");
if (!pmenu) {
- epicsPrintf("initPeriodic: menuScan not present\n");
+ errlogPrintf("initPeriodic: menuScan not present\n");
return;
}
nPeriodic = pmenu->nChoice - SCAN_1ST_PERIODIC;
papPeriodic = dbCalloc(nPeriodic, sizeof(periodic_scan_list*));
periodicTaskId = dbCalloc(nPeriodic, sizeof(void *));
for (i = 0; i < nPeriodic; i++) {
- ppsl = dbCalloc(1, sizeof(periodic_scan_list));
+ periodic_scan_list *ppsl = dbCalloc(1, sizeof(periodic_scan_list));
+ const char *choice = pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC];
+
+ if (!epicsScanDouble(choice, &ppsl->period) ||
+ ppsl->period <= 0) {
+ errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice);
+ free(ppsl);
+ continue;
+ }
ppsl->scan_list.lock = epicsMutexMustCreate();
ellInit(&ppsl->scan_list.list);
- ppsl->name = pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC];
- epicsScanDouble(ppsl->name, &ppsl->period);
+ ppsl->name = choice;
ppsl->scanCtl = ctlPause;
ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty);
@@ -645,10 +683,11 @@ static void initPeriodic(void)
static void spawnPeriodic(int ind)
{
- periodic_scan_list *ppsl;
+ periodic_scan_list *ppsl = papPeriodic[ind];
char taskName[20];
- ppsl = papPeriodic[ind];
+ if (!ppsl) return;
+
sprintf(taskName, "scan%g", ppsl->period);
periodicTaskId[ind] = epicsThreadCreate(
taskName, epicsThreadPriorityScanLow + ind,