diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index fc15ddd2c..d74aae675 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -12,6 +12,12 @@

Changes between 3.14.10 and 3.14.11

+

PINI Processing Phases

+ +

The PHAS field now controls the order in which records with PINI set are +processed at initialization time, lower values of PHAS are scanned before +higher ones.

+

Channel Access command line tool changes

The caget/caput/camonitor programs in src/catools now use '\' escape @@ -22,17 +28,19 @@ to be used instead of the default space character.

New functions for escaping non-printables in epicsString.h

-

The existing routines used to escape non-printable characters have been replaced -by a new set of functions that are prototyped in the epicsString.h header file:

+

The existing routines used to escape non-printable characters have been +replaced by a new set of functions that are prototyped in the epicsString.h +header file:

int epicsStrnRawFromEscaped(char *outbuf, size_t outsize, const char *inbuf, size_t inlen);
 epicsShareFunc int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, const char *inbuf, size_t inlen);
 epicsShareFunc size_t epicsStrnEscapedFromRawSize(const char *inbuf, size_t inlen);
-

Both conversion functions take the output buffer (and its size), and the input -buffer (and its size) as argument. They will convert non-printable characters -from/to their '\'-escaped versions. The third function scans a raw input string -and returns the number of characters needed for the escaped version.

+

Both conversion functions take the output buffer (and its size), and the +input buffer (and its size) as argument. They will convert non-printable +characters from/to their '\'-escaped versions. The third function scans a raw +input string and returns the number of characters needed for the escaped +version.

The existing function interfaces will be kept for compatibility, but their further use is deprecated.

@@ -58,7 +66,7 @@ string length limit imposed by the DBF_STRING type is replaced by the amount of storage allocated for the string on the IOC (for link fields the limit is related to the maximum length of a record name).

-

The caget/caput/camonitor programs in src/catools can use long strings by +

The caget/caput/camonitor programs in src/catools can now use long strings by adding the command-line option -S which causes them to handle an array of DBF_CHAR as a string. Both MEDM and EDM can already present such DBF_CHAR arrays as strings in their text widgets (although you do have to configure the @@ -190,8 +198,8 @@ epicsTime class has also gone.

RTEMS Release

-

RTEMS release 4.9.2 or newer is required. EPICS now configures RTEMS to use unified executive/malloc memory pools if available (RTEMS 4.10 and up).

- +

RTEMS release 4.9.2 or newer is required. EPICS now configures RTEMS to use +unified executive/malloc memory pools if available (RTEMS 4.10 and up).

RTEMS

RTEMS stack sizes have been reduced. This allows more clients in machines diff --git a/src/db/dbScan.h b/src/db/dbScan.h index 7f50d3e8d..717bae8ba 100644 --- a/src/db/dbScan.h +++ b/src/db/dbScan.h @@ -15,6 +15,8 @@ #ifndef INCdbScanH #define INCdbScanH +#include + #include "menuScan.h" #include "shareLib.h" @@ -27,6 +29,9 @@ extern "C" { #define SCAN_IO_EVENT menuScanI_O_Intr #define SCAN_1ST_PERIODIC (menuScanI_O_Intr + 1) +#define MAX_PHASE SHRT_MAX +#define MIN_PHASE SHRT_MIN + /*definitions for I/O Interrupt Scanning */ struct io_scan_list; diff --git a/src/misc/iocInit.c b/src/misc/iocInit.c index d7b88c11a..5db3d6de6 100644 --- a/src/misc/iocInit.c +++ b/src/misc/iocInit.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "dbDefs.h" #include "epicsThread.h" @@ -204,13 +205,9 @@ static void initDrvSup(void) /* Locate all driver support entry tables */ continue; } pdrvSup->pdrvet = pdrvet; - /* - * If an initialization routine is defined (not NULL), - * for the driver support call it. - */ - if (pdrvet->init) { + + if (pdrvet->init) pdrvet->init(); - } } } @@ -295,221 +292,210 @@ static void finishDevSup(void) pthisDevSup = (devSup *)ellNext(&pthisDevSup->node)) { struct dset *pdset = pthisDevSup->pdset; - if (pdset && pdset->init) { + if (pdset && pdset->init) pdset->init(1); - } } - } } -static void initDatabase(void) +/* + * Iterate through all record instances (but not aliases), + * calling a function for each one. + */ +typedef void (*recIterFunc)(dbRecordType *pdbRecordType, dbCommon *precord); + +static void iterateRecords(recIterFunc func) { dbRecordType *pdbRecordType; for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - struct rset *prset = pdbRecordType->prset; dbRecordNode *pdbRecordNode; - if (!prset) continue; - for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { dbCommon *precord = pdbRecordNode->precord; - devSup *pdevSup; - struct dset *pdset; if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) continue; - precord->rset = prset; - precord->rdes = pdbRecordType; - precord->mlok = epicsMutexMustCreate(); - ellInit(&precord->mlis); - - /* Reset the process active field */ - precord->pact = FALSE; - - /* Init DSET NOTE that result may be NULL */ - pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); - pdset = pdevSup ? pdevSup->pdset : NULL; - precord->dset = pdset; - if (prset->init_record) { - prset->init_record(precord, 0); - } + func(pdbRecordType, precord); } } + return; +} + +static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord) +{ + struct rset *prset = pdbRecordType->prset; + devSup *pdevSup; - /* Second pass to resolve links */ - for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - dbRecordNode *pdbRecordNode; + if (!prset) return; /* unlikely */ - for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { - dbCommon *precord = pdbRecordNode->precord; - int j; - devSup *pdevSup; + precord->rset = prset; + precord->rdes = pdbRecordType; + precord->mlok = epicsMutexMustCreate(); + ellInit(&precord->mlis); - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) - continue; + /* Reset the process active field */ + precord->pact = FALSE; - /* Convert all PV_LINKs to DB_LINKs or CA_LINKs */ - /* For all the links in the record type... */ - for (j = 0; j < pdbRecordType->no_links; j++) { - dbFldDes *pdbFldDes = - pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; - DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + /* Init DSET NOTE that result may be NULL */ + pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); + precord->dset = pdevSup ? pdevSup->pdset : NULL; - if (plink->type == PV_LINK) { - DBADDR dbaddr; + if (prset->init_record) + prset->init_record(precord, 0); +} - if (plink == &precord->tsel) recGblTSELwasModified(plink); - if (!(plink->value.pv_link.pvlMask&(pvlOptCA|pvlOptCP|pvlOptCPP)) - && (dbNameToAddr(plink->value.pv_link.pvname,&dbaddr)==0)) { - DBADDR *pdbAddr; +static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord) +{ + devSup *pdevSup; + int j; - plink->type = DB_LINK; - pdbAddr = dbCalloc(1,sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /*structure copy*/; - plink->value.pv_link.pvt = pdbAddr; - } else {/*It is a CA link*/ + /* Convert all PV_LINKs to DB_LINKs or CA_LINKs */ + /* For all the links in the record type... */ + for (j = 0; j < pdbRecordType->no_links; j++) { + dbFldDes *pdbFldDes = + pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - if (pdbFldDes->field_type == DBF_INLINK) { - plink->value.pv_link.pvlMask |= pvlOptInpNative; - } - dbCaAddLink(plink); - if (pdbFldDes->field_type == DBF_FWDLINK) { - char *pperiod = - strrchr(plink->value.pv_link.pvname,'.'); + if (plink->type == PV_LINK) { + DBADDR dbaddr; - if (pperiod && strstr(pperiod,"PROC")) { - plink->value.pv_link.pvlMask |= pvlOptFWD; - } else { - errlogPrintf("%s.FLNK is a Channel Access Link " - " but does not link to a PROC field\n", - precord->name); - } - } + if (plink == &precord->tsel) recGblTSELwasModified(plink); + if (!(plink->value.pv_link.pvlMask&(pvlOptCA|pvlOptCP|pvlOptCPP)) + && (dbNameToAddr(plink->value.pv_link.pvname,&dbaddr)==0)) { + DBADDR *pdbAddr; + + plink->type = DB_LINK; + pdbAddr = dbCalloc(1,sizeof(struct dbAddr)); + *pdbAddr = dbaddr; /*structure copy*/; + plink->value.pv_link.pvt = pdbAddr; + } else {/*It is a CA link*/ + + if (pdbFldDes->field_type == DBF_INLINK) { + plink->value.pv_link.pvlMask |= pvlOptInpNative; + } + dbCaAddLink(plink); + if (pdbFldDes->field_type == DBF_FWDLINK) { + char *pperiod = + strrchr(plink->value.pv_link.pvname,'.'); + + if (pperiod && strstr(pperiod,"PROC")) { + plink->value.pv_link.pvlMask |= pvlOptFWD; + } else { + errlogPrintf("%s.FLNK is a Channel Access Link " + " but does not link to a PROC field\n", + precord->name); } } } - pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); - if (pdevSup) { - struct dsxt *pdsxt = pdevSup->pdsxt; - if (pdsxt && pdsxt->add_record) { - pdsxt->add_record(precord); - } - } } } - - /* Call record support init_record routine - Second pass */ - for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - struct rset *prset = pdbRecordType->prset; - dbRecordNode *pdbRecordNode; - - if (!prset) continue; - - for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { - dbCommon *precord = pdbRecordNode->precord; - - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) - continue; - - precord->rset = prset; - if (prset->init_record) { - prset->init_record(precord, 1); - } + pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); + if (pdevSup) { + struct dsxt *pdsxt = pdevSup->pdsxt; + if (pdsxt && pdsxt->add_record) { + pdsxt->add_record(precord); } } +} + +/* Find range of PHAS for records where PINI is true */ +static short minPhase, maxPhase; + +static void doInitRecord1(dbRecordType *pdbRecordType, dbCommon *precord) +{ + struct rset *prset = pdbRecordType->prset; + short phase = precord->phas; + + if (!prset) return; /* unlikely */ + + if (prset->init_record) + prset->init_record(precord, 1); + + if (precord->pini) { + if (phase > maxPhase) maxPhase = phase; + if (phase < minPhase) minPhase = phase; + } +} + +static void initDatabase(void) +{ + minPhase = MAX_PHASE; + maxPhase = MIN_PHASE; + + iterateRecords(doInitRecord0); + iterateRecords(doResolveLinks); + iterateRecords(doInitRecord1); + epicsAtExit(exitDatabase, NULL); return; } /* - * Process database records at initialization if - * their pini (process at init) field is set. + * Process database records at initialization ordered by phase + * if their pini (process at init) field is set. */ +static short piniPhase, nextPhase; + +static void doRecordPini(dbRecordType *pdbRecordType, dbCommon *precord) +{ + int phase; + + if (!precord->pini) return; + + phase = precord->phas; + if (phase == piniPhase) + dbProcess(precord); + else if (phase > piniPhase && phase < nextPhase) + nextPhase = phase; +} + static void initialProcess(void) { - dbRecordType *pdbRecordType; - - for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - dbRecordNode *pdbRecordNode; - - for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { - dbCommon *precord = pdbRecordNode->precord; - - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) - continue; - - if (precord->pini) { - dbProcess(precord); - } - } - } - return; + nextPhase = minPhase; + do { + piniPhase = nextPhase; + nextPhase = maxPhase; + iterateRecords(doRecordPini); + } while (piniPhase != maxPhase); } -static void exitDatabase(void *dummy) +/* + * Shutdown processing. + */ +static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord) { - dbRecordType *pdbRecordType; + devSup *pdevSup; + struct dsxt *pdsxt; + int j; - for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - dbRecordNode *pdbRecordNode; + for (j = 0; j < pdbRecordType->no_links; j++) { + dbFldDes *pdbFldDes = + pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { - dbCommon *precord = pdbRecordNode->precord; - int j; - devSup *pdevSup; - struct dsxt *pdsxt; - - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) - continue; - - /* For all the links in the record type... */ - for (j = 0; j < pdbRecordType->no_links; j++) { - dbFldDes *pdbFldDes = - pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; - DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - - if (plink->type == CA_LINK) { - dbCaRemoveLink(plink); - } - } - - if (precord->dset && - (pdevSup = dbDSETtoDevSup(pdbRecordType, precord->dset)) && - (pdsxt = pdevSup->pdsxt) && - pdsxt->del_record) { - pdsxt->del_record(precord); - } + if (plink->type == CA_LINK) { + dbCaRemoveLink(plink); } } + if (precord->dset && + (pdevSup = dbDSETtoDevSup(pdbRecordType, precord->dset)) && + (pdsxt = pdevSup->pdsxt) && + pdsxt->del_record) { + pdsxt->del_record(precord); + } +} + +static void exitDatabase(void *dummy) +{ + iterateRecords(doCloseLinks); iocState = iocStopped; }