diff --git a/.tools/make-tar.sh b/.tools/make-tar.sh index ff78a23cd..4c9c33a7b 100755 --- a/.tools/make-tar.sh +++ b/.tools/make-tar.sh @@ -36,7 +36,10 @@ case "$PREFIX" in esac # Check for both and R -if ! [ `git tag -l $TOPREV` ] +if [ "$TOPREV" = "HEAD" ] +then + true +elif ! [ `git tag -l $TOPREV` ] then if [ `git tag -l R$TOPREV` ] then diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index 715fc168c..e55ee5d33 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -52,9 +52,11 @@ EPICS_MODIFICATION = 2 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included if zero -EPICS_PATCH_LEVEL = 0 +EPICS_PATCH_LEVEL = 2 -# This will end in -DEV between official releases +# Between official releases, the EPICS_PATCH_LEVEL gets incremented +# and a -DEV suffix is added (similar to the Maven -SNAPSHOT versions) +#EPICS_DEV_SNAPSHOT= EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-pre1 #EPICS_DEV_SNAPSHOT=-pre1-DEV @@ -64,7 +66,6 @@ EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-rc1-DEV #EPICS_DEV_SNAPSHOT=-rc2 #EPICS_DEV_SNAPSHOT=-rc2-DEV -#EPICS_DEV_SNAPSHOT= # No changes should be needed below here diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV index 3bdb94b15..5bf4960f7 100644 --- a/configure/CONFIG_SITE_ENV +++ b/configure/CONFIG_SITE_ENV @@ -34,14 +34,9 @@ # The future dates below assume the rules don't get changed; # see http://www.timeanddate.com/time/dst/2018.html to check. # -# DST for 2018 US: Mar 11 - Nov 04 -# EU: Mar 25 - Oct 28 -EPICS_TIMEZONE = CUS::360:031102:110402 -#EPICS_TIMEZONE = MET::-60:032502:102803 -# # DST for 2019 US: Mar 10 - Nov 03 # EU: Mar 31 - Oct 27 -#EPICS_TIMEZONE = CUS::360:031002:110302 +EPICS_TIMEZONE = CUS::360:031002:110302 #EPICS_TIMEZONE = MET::-60:033102:102703 # # DST for 2020 US: Mar 08 - Nov 01 @@ -58,6 +53,11 @@ EPICS_TIMEZONE = CUS::360:031102:110402 # EU: Mar 27 - Oct 30 #EPICS_TIMEZONE = CUS::360:031302:110602 #EPICS_TIMEZONE = MET::-60:032702:103003 +# +# DST for 2023 US: Mar 13 - Nov 06 +# EU: Mar 27 - Oct 30 +#EPICS_TIMEZONE = CUS::360:031202:110502 +#EPICS_TIMEZONE = MET::-60:032602:102903 # EPICS_TS_NTP_INET # NTP time server ip address for VxWorks and RTEMS. diff --git a/configure/RULES_TOP b/configure/RULES_TOP index b18fbf026..bd844e76f 100644 --- a/configure/RULES_TOP +++ b/configure/RULES_TOP @@ -26,8 +26,10 @@ realuninstall: uninstallDirs UNINSTALL_DIRS += $(INSTALL_DBD) $(INSTALL_INCLUDE) $(INSTALL_DOC) \ $(INSTALL_HTML) $(INSTALL_TEMPLATES) $(INSTALL_DB) $(DIRECTORY_TARGETS) ifneq ($(INSTALL_LOCATION),$(TOP)) +ifneq ($(INSTALL_LOCATION),$(EPICS_BASE)) UNINSTALL_DIRS += $(INSTALL_CONFIG) endif +endif uninstallDirs: $(RMDIR) $(UNINSTALL_DIRS) diff --git a/configure/os/CONFIG.darwinCommon.darwinCommon b/configure/os/CONFIG.darwinCommon.darwinCommon index 4a8f3ef8e..bfd61830d 100644 --- a/configure/os/CONFIG.darwinCommon.darwinCommon +++ b/configure/os/CONFIG.darwinCommon.darwinCommon @@ -65,7 +65,7 @@ GNU = NO # # Darwin shared libraries # -SHRLIB_LDFLAGS = -dynamiclib -undefined dynamic_lookup \ +SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \ -install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \ $(addprefix -compatibility_version , $(SHRLIB_VERSION)) \ $(addprefix -current_version , $(SHRLIB_VERSION)) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 5cc286495..27670a17e 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,8 +20,7 @@ release.

which should also be read to understand what has changed since an earlier release.

- -

EPICS Release 7.0.3

+

EPICS Release 7.0.2.2

-

Skipping build of modules and re-enabling vxWorks 5

-

Any module can be skipped during the build for a given architecture by setting -the Makfile variable SKIP_BUILDS to a list of patterns matching the -module directories to skip. For example to skip building any pvAccess related -modules set SKIP_BUILDS=pv% normativeTypes in the -CONFIG.Common.<arch> file for that architecture. -The builds for vxWorks 5 and up to 6.2 do that. -

+

EPICS Release 7.0.2.1

+ +

Linking shared libraries on macOS

+ +

The linker flag -flat_namespace has been restored for creating +shared libraries, although not for loadable libraries (bundles). This was +required for building using the latest versions of Apple XCode.

+ +

Fix DB_LINK loop breaking

+ +

A regression was introduced in 7.0.2 which caused record chains with loops to +be incorrectly broken. Processing should be skipped when a DB_LINK with Process +Passive (PP) closes a loop to a synchronous record.

+ +

Instead in 7.0.2 the targeted record would be processed if processing began +with a remote action (or some other caller of dbPutField()). This would +result in the loop running a second time. The loop would be broken on the second +iteration.

+ +

See lp: +#1809570

Old dbStaticLib APIs removed

@@ -82,6 +94,7 @@ than it used to be.

Launchpad Milestone page for EPICS Base 7.0.2.

+

Git Branches Recombined

The four separate Git branches core/master, libcom/master, @@ -92,6 +105,7 @@ more than one of these modules. The layout of the source files has not changed at all however, so the source code for libcom, ca and the database are still found separately under the module subdirectory.

+

EPICS Release 7.0.1.1

Changed SIML failure behavior

@@ -963,6 +977,15 @@ of its CALLBACK objects.

+

Perl CA bindings fixed for macOS Mojave

+ +

Apple removed some Perl header files from macOS Mojave that were available +in their SDK, requiring a change to the include paths used when compiling the +CA bindings. The new version should build on new and older macOS versions, and +these changes may also help other targets that have an incomplete installation +of Perl (the build will continue after printing a warning that the Perl CA +bindings could not be built).

+

Routine epicsTempName() removed from libCom

This routine was a simple wrapper around the C89 function tmpnam() diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html index 18533f59d..cb95f7f60 100644 --- a/documentation/ReleaseChecklist.html +++ b/documentation/ReleaseChecklist.html @@ -37,6 +37,8 @@ that should be performed when creating production releases of EPICS Base.

The Release Process

+

Full Process

+

The version released on the Feature Freeze date is designated the first pre-release, -pre1. The first release candidate -rc1 is the first version that has undergone testing by the developers and has shown no @@ -49,6 +51,12 @@ release candidate has been available for 2 weeks without any new problems being reported or major changes having to be committed, the final release can be made.

+

Short Process for Patch Releases

+ +

The Patch Release date and its scope are agreed upon about four weeks +ahead of time. If no blocking issues are raised, the release is made by the +release manager.

+

Roles

The following roles are used below:

@@ -322,6 +330,21 @@ made.

+ +   + Release Manager + Edit and commit changes to the EPICS Base version number file and + the embedded module version files: + + Version numbers should be set to the next expected patch release, + with a "-DEV" tag added (where applicable). + +   Release Manager diff --git a/modules/ca/src/perl/Makefile b/modules/ca/src/perl/Makefile index 5b2bd5920..7890f34e4 100644 --- a/modules/ca/src/perl/Makefile +++ b/modules/ca/src/perl/Makefile @@ -8,6 +8,18 @@ TOP = ../.. include $(TOP)/configure/CONFIG +ifdef T_A + PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version) + PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname) + PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME) + + PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib) + PERL_h = $(PERL_ARCHLIB)/CORE/perl.h + + EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils + PERLBIN := $(shell $(PERL) ../perlConfig.pl bin) + XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp)) + # Special settings for Darwin: ifeq ($(OS_CLASS),Darwin) # Use hdepends command (not GNU compiler flags) @@ -18,22 +30,23 @@ ifeq ($(OS_CLASS),Darwin) # Perl loadable libraries on Darwin have funny names LOADABLE_SHRLIB_PREFIX = LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext) + +ifeq ($(wildcard $(PERL_h)),) + # Perl's headers moved in Mojave + SDK_PATH := $(shell xcodebuild -version -sdk macosx Path) + PERL_ARCHLIB := $(SDK_PATH)/$(PERL_ARCHLIB) +endif endif -ifdef T_A - PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version) - PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname) - PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME) - - EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils - PERLBIN := $(shell $(PERL) ../perlConfig.pl bin) - XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp)) - +ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!) ifeq ($(strip $(XSUBPP)),) $(warning Perl's xsubpp program was not found.) $(warning The Perl CA module will not be built.) else -ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!) +ifeq ($(wildcard $(PERL_h)),) + $(warning Perl's C header files were not found.) + $(warning The Perl CA module will not be built.) +else ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32 LOADABLE_LIBRARY_HOST = Cap5 @@ -50,10 +63,11 @@ endif endif endif endif +endif Cap5_SRCS = Cap5.xs Cap5_LIBS = ca Com -Cap5_INCLUDES = -I$(shell $(PERL) ../perlConfig.pl archlib)/CORE +Cap5_INCLUDES = -I$(PERL_ARCHLIB)/CORE Cap5_CFLAGS = $(shell $(PERL) ../perlConfig.pl ccflags) CLEANS += Cap5.c diff --git a/modules/database/configure/CONFIG_DATABASE_VERSION b/modules/database/configure/CONFIG_DATABASE_VERSION index 63ad9cd4c..4b6ef8a6b 100644 --- a/modules/database/configure/CONFIG_DATABASE_VERSION +++ b/modules/database/configure/CONFIG_DATABASE_VERSION @@ -1,4 +1,4 @@ EPICS_DATABASE_MAJOR_VERSION = 3 EPICS_DATABASE_MINOR_VERSION = 17 -EPICS_DATABASE_MAINTENANCE_VERSION = 2 +EPICS_DATABASE_MAINTENANCE_VERSION = 3 EPICS_DATABASE_DEVELOPMENT_FLAG = 0 diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index af396c74e..be81d22ed 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -684,7 +684,7 @@ finish: void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry) { struct dbCommon *prec = paddr->precord; - dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common); + dbCommonPvt *ppvt = dbRec2Pvt(prec); memset((char *)pdbentry,'\0',sizeof(DBENTRY)); @@ -698,7 +698,7 @@ void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry) void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry) { - dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common); + dbCommonPvt *ppvt = dbRec2Pvt(prec); memset((char *)pdbentry,'\0',sizeof(DBENTRY)); diff --git a/modules/database/src/ioc/db/dbCommonPvt.h b/modules/database/src/ioc/db/dbCommonPvt.h index 3dfce8b27..eda8b18a7 100644 --- a/modules/database/src/ioc/db/dbCommonPvt.h +++ b/modules/database/src/ioc/db/dbCommonPvt.h @@ -1,14 +1,27 @@ #ifndef DBCOMMONPVT_H #define DBCOMMONPVT_H +#include +#include #include "dbCommon.h" +struct epicsThreadOSD; + /** Base internal additional information for every record */ typedef struct dbCommonPvt { struct dbRecordNode *recnode; + /* Thread which is currently processing this record */ + struct epicsThreadOSD* procThread; + struct dbCommon common; } dbCommonPvt; +static EPICS_ALWAYS_INLINE +dbCommonPvt* dbRec2Pvt(struct dbCommon *prec) +{ + return CONTAINER(prec, dbCommonPvt, common); +} + #endif // DBCOMMONPVT_H diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index ce0110a64..0da927139 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -56,7 +56,7 @@ #include "dbAddr.h" #include "dbBase.h" #include "dbBkpt.h" -#include "dbCommon.h" +#include "dbCommonPvt.h" #include "dbConvertFast.h" #include "dbConvert.h" #include "db_field_log.h" @@ -386,8 +386,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst) { char context[40] = ""; int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc); + int claim_src = dbRec2Pvt(psrc)->procThread==NULL; + int claim_dst = psrc!=pdst && dbRec2Pvt(pdst)->procThread==NULL; long status; epicsUInt8 pact = psrc->pact; + epicsThreadId self = epicsThreadGetIdSelf(); psrc->pact = TRUE; @@ -406,14 +409,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst) printf("%s: '%s' -> '%s' with PUTF=%u\n", context, psrc->name, pdst->name, psrc->putf); - if (pdst->putf) - errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n", - pdst->name); - pdst->putf = psrc->putf; } - else if (psrc->putf) { - /* The dst record is busy (awaiting async reprocessing) and + else if (psrc->putf && claim_dst) { + /* The dst record is busy (awaiting async reprocessing), + * not being processed recursively by us, and * we were originally triggered by a call to dbPutField(), * so we mark the dst record for reprocessing once the async * completion is over. @@ -426,17 +426,43 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst) pdst->rpro = TRUE; } else { - /* The dst record is busy, but we weren't triggered by a call - * to dbPutField(). Do nothing. + /* The dst record is busy, but either is being processed recursively, + * or wasn't triggered by a call to dbPutField(). Do nothing. */ if (trace) printf("%s: '%s' -> Active '%s', done\n", context, psrc->name, pdst->name); } + if(claim_src) { + dbRec2Pvt(psrc)->procThread = self; + } + if(claim_dst) { + dbRec2Pvt(pdst)->procThread = self; + } + + if(dbRec2Pvt(psrc)->procThread!=self || + dbRec2Pvt(pdst)->procThread!=self) { + errlogPrintf("Logic Error: processTarget 1 from %p, %s(%p) -> %s(%p)\n", + self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst)); + } + status = dbProcess(pdst); psrc->pact = pact; + if(dbRec2Pvt(psrc)->procThread!=self || + dbRec2Pvt(pdst)->procThread!=self) { + errlogPrintf("Logic Error: processTarget 2 from %p, %s(%p) -> %s(%p)\n", + self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst)); + } + + if(claim_src) { + dbRec2Pvt(psrc)->procThread = NULL; + } + if(claim_dst) { + dbRec2Pvt(pdst)->procThread = NULL; + } + return status; } diff --git a/modules/database/src/ioc/db/dbNotify.c b/modules/database/src/ioc/db/dbNotify.c index c2420affc..794672a55 100644 --- a/modules/database/src/ioc/db/dbNotify.c +++ b/modules/database/src/ioc/db/dbNotify.c @@ -86,12 +86,6 @@ typedef struct notifyGlobal { static notifyGlobal *pnotifyGlobal = 0; -/*Local routines*/ -static void notifyInit(processNotify *ppn); -static void notifyCleanup(processNotify *ppn); -static void restartCheck(processNotifyRecord *ppnr); -static void callDone(dbCommon *precord,processNotify *ppn); -static void processNotifyCommon(processNotify *ppn,dbCommon *precord); static void notifyCallback(CALLBACK *pcallback); #define ellSafeAdd(list,listnode) \ @@ -210,7 +204,7 @@ static void callDone(dbCommon *precord, processNotify *ppn) return; } -static void processNotifyCommon(processNotify *ppn,dbCommon *precord) +static void processNotifyCommon(processNotify *ppn, dbCommon *precord, int first) { notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; int didPut = 0; @@ -256,6 +250,9 @@ static void processNotifyCommon(processNotify *ppn,dbCommon *precord) doProcess = 1; if (doProcess) { + if (first) { + precord->putf = TRUE; + } ppn->wasProcessed = 1; precord->ppn = ppn; ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode); @@ -298,7 +295,7 @@ static void notifyCallback(CALLBACK *pcallback) return; } if(pnotifyPvt->state == notifyRestartCallbackRequested) { - processNotifyCommon(ppn, precord); + processNotifyCommon(ppn, precord, 0); return; } /* All done. Clean up and call userCallback */ @@ -382,7 +379,7 @@ void dbProcessNotify(processNotify *ppn) precord->ppnr->precord = precord; ellInit(&precord->ppnr->restartList); } - processNotifyCommon(ppn, precord); + processNotifyCommon(ppn, precord, 1); } void dbNotifyCancel(processNotify *ppn) @@ -582,7 +579,7 @@ static void tpnThread(void *pvt) processNotify *ppn = (processNotify *) ptpnInfo->ppn; dbProcessNotify(ppn); - epicsEventWait(ptpnInfo->callbackDone); + epicsEventMustWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); dbChannelDelete(ppn->chan); diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 59eb345f3..9281246d6 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -434,6 +434,10 @@ static void dbMenuHead(char *name) dbMenu *pdbMenu; GPHENTRY *pgphentry; + if (!*name) { + yyerrorAbort("dbMenuHead: Menu name can't be empty"); + return; + } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList); if(pgphentry) { duplicate = TRUE; @@ -447,6 +451,10 @@ static void dbMenuHead(char *name) static void dbMenuChoice(char *name,char *value) { + if (!*name) { + yyerror("dbMenuChoice: Menu choice name can't be empty"); + return; + } if(duplicate) return; allocTemp(epicsStrDup(name)); allocTemp(epicsStrDup(value)); @@ -494,6 +502,10 @@ static void dbRecordtypeHead(char *name) dbRecordType *pdbRecordType; GPHENTRY *pgphentry; + if (!*name) { + yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty"); + return; + } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList); if(pgphentry) { duplicate = TRUE; @@ -512,6 +524,10 @@ static void dbRecordtypeFieldHead(char *name,char *type) dbFldDes *pdbFldDes; int i; + if (!*name) { + yyerrorAbort("dbRecordtypeFieldHead: Field name can't be empty"); + return; + } if(duplicate) return; pdbFldDes = dbCalloc(1,sizeof(dbFldDes)); allocTemp(pdbFldDes); @@ -580,7 +596,7 @@ static void dbRecordtypeFieldItem(char *name,char *value) if(sscanf(value,"%hd",&pdbFldDes->special)==1) { return; } - yyerror("Illegal special value."); + yyerror("Illegal 'special' value."); return; } if(strcmp(name,"pp")==0) { @@ -589,13 +605,13 @@ static void dbRecordtypeFieldItem(char *name,char *value) } else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) { pdbFldDes->process_passive = FALSE; } else { - yyerror("Illegal value. Must be NO or YES"); + yyerror("Illegal 'pp' value, must be YES/NO/TRUE/FALSE"); } return; } if(strcmp(name,"interest")==0) { if(sscanf(value,"%hd",&pdbFldDes->interest)!=1) - yyerror("Illegal value. Must be integer"); + yyerror("Illegal 'interest' value, must be integer"); return; } if(strcmp(name,"base")==0) { @@ -604,13 +620,13 @@ static void dbRecordtypeFieldItem(char *name,char *value) } else if(strcmp(value,"HEX")==0) { pdbFldDes->base = CT_HEX; } else { - yyerror("Illegal value. Must be CT_DECIMAL or CT_HEX"); + yyerror("Illegal 'base' value, must be DECIMAL/HEX"); } return; } if(strcmp(name,"size")==0) { if(sscanf(value,"%hd",&pdbFldDes->size)!=1) - yyerror("Illegal value. Must be integer"); + yyerror("Illegal 'size' value, must be integer"); return; } if(strcmp(name,"extra")==0) { @@ -802,6 +818,10 @@ static void dbDriver(char *name) drvSup *pdrvSup; GPHENTRY *pgphentry; + if (!*name) { + yyerrorAbort("dbDriver: Driver name can't be empty"); + return; + } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList); if(pgphentry) { return; @@ -841,6 +861,10 @@ static void dbRegistrar(char *name) dbText *ptext; GPHENTRY *pgphentry; + if (!*name) { + yyerrorAbort("dbRegistrar: Registrar name can't be empty"); + return; + } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList); if(pgphentry) { return; @@ -860,6 +884,10 @@ static void dbFunction(char *name) dbText *ptext; GPHENTRY *pgphentry; + if (!*name) { + yyerrorAbort("dbFunction: Function name can't be empty"); + return; + } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList); if(pgphentry) { return; @@ -879,6 +907,10 @@ static void dbVariable(char *name, char *type) dbVariableDef *pvar; GPHENTRY *pgphentry; + if (!*name) { + yyerrorAbort("dbVariable: Variable name can't be empty"); + return; + } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList); if(pgphentry) { return; @@ -899,6 +931,10 @@ static void dbBreakHead(char *name) brkTable *pbrkTable; GPHENTRY *pgphentry; + if (!*name) { + yyerrorAbort("dbBreakHead: Breaktable name can't be empty"); + return; + } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList); if(pgphentry) { duplicate = TRUE; @@ -1001,6 +1037,10 @@ static void dbRecordHead(char *recordType, char *name, int visible) DBENTRY *pdbentry; long status; + if (!*name) { + yyerrorAbort("dbRecordHead: Record name can't be empty"); + return; + } badch = strpbrk(name, " \"'.$"); if (badch) { epicsPrintf("Bad character '%c' in record name \"%s\"\n", @@ -1108,6 +1148,10 @@ static void dbRecordInfo(char *name, char *value) tempListNode *ptempListNode; long status; + if (!*name) { + yyerrorAbort("dbRecordInfo: Info item name can't be empty"); + return; + } if (duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbentry = ptempListNode->item; @@ -1132,6 +1176,10 @@ static void dbRecordAlias(char *name) tempListNode *ptempListNode; long status; + if (!*name) { + yyerrorAbort("dbRecordAlias: Alias name can't be empty"); + return; + } if (duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbentry = ptempListNode->item; @@ -1149,6 +1197,10 @@ static void dbAlias(char *name, char *alias) DBENTRY dbEntry; DBENTRY *pdbEntry = &dbEntry; + if (!*alias) { + yyerrorAbort("dbAlias: Alias name can't be empty"); + return; + } dbInitEntry(pdbbase, pdbEntry); if (dbFindRecord(pdbEntry, name)) { epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n", diff --git a/modules/database/src/ioc/dbStatic/dbStaticRun.c b/modules/database/src/ioc/dbStatic/dbStaticRun.c index d3817ffc5..46cbf982a 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticRun.c +++ b/modules/database/src/ioc/dbStatic/dbStaticRun.c @@ -179,7 +179,7 @@ long dbFreeRecord(DBENTRY *pdbentry) if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); if(!precnode) return(S_dbLib_recNotFound); if(!precnode->precord) return(S_dbLib_recNotFound); - free(CONTAINER(precnode->precord, dbCommonPvt, common)); + free(dbRec2Pvt(precnode->precord)); precnode->precord = NULL; return(0); } diff --git a/modules/database/test/std/rec/asyncproctest.c b/modules/database/test/std/rec/asyncproctest.c index a8b09a928..7b8fca782 100644 --- a/modules/database/test/std/rec/asyncproctest.c +++ b/modules/database/test/std/rec/asyncproctest.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include "registryFunction.h" #include +#include epicsEventId done; static int waitFor; @@ -28,11 +30,17 @@ long doneSubr(subRecord *prec) return 0; } +static +void dummydone(void *usr, struct dbCommon* prec) +{ + epicsEventMustTrigger(done); +} + void asyncproctest_registerRecordDeviceDriver(struct dbBase *); MAIN(asyncproctest) { - testPlan(21); + testPlan(27); done = epicsEventMustCreate(epicsEventEmpty); @@ -99,6 +107,40 @@ MAIN(asyncproctest) testdbGetFieldEqual("chain3", DBF_LONG, 7); testdbGetFieldEqual("chain3.A", DBF_LONG, 2); + testDiag("===== Chain 4 ======"); + + { + dbCommon *dummy=testdbRecordPtr("chain4_dummy"); + + testdbPutFieldOk("chain4_pos.PROC", DBF_LONG, 0); + + /* sync once queue to wait for any queued RPRO */ + scanOnceCallback(dummy, dummydone, NULL); + + if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) + testAbort("Processing timed out"); + + testdbGetFieldEqual("chain4_pos", DBF_SHORT, 1); + testdbGetFieldEqual("chain4_rel", DBF_SHORT, 1); + testdbGetFieldEqual("chain4_lim", DBF_SHORT, 1); + } + + testDiag("===== Chain 5 ======"); + + { + dbCommon *dummy=testdbRecordPtr("chain4_dummy"); + + testdbPutFieldOk("chain5_cnt.PROC", DBF_LONG, 0); + + /* sync once queue to wait for any queued RPRO */ + scanOnceCallback(dummy, dummydone, NULL); + + if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) + testAbort("Processing timed out"); + + testdbGetFieldEqual("chain5_cnt", DBF_SHORT, 1); + } + testIocShutdownOk(); testdbCleanup(); diff --git a/modules/database/test/std/rec/asyncproctest.db b/modules/database/test/std/rec/asyncproctest.db index 352b6ca56..87fe5d63d 100644 --- a/modules/database/test/std/rec/asyncproctest.db +++ b/modules/database/test/std/rec/asyncproctest.db @@ -53,3 +53,41 @@ record(sub, "done3") { field(FLNK, "chain3") field(TPRO, "$(TPRO=)") } + + +# loop breaking regression +# should _not_ RPRO +record(calcout,"chain4_pos") { + field(CALC, "E:=E+1;E") + field(OUT,"chain4_rel.A PP") + field(TPRO, "$(TPRO=)") +} + +record(calc,"chain4_rel") { + field(CALC, "E:=E+1;E") + field(FLNK,"chain4_lim") + field(TPRO, "$(TPRO=)") +} + +record(calc,"chain4_lim") { + field(CALC, "E:=E+1;E") + field(INPA,"chain4_pos PP") + field(INPB,"chain4_pos.HIGH PP") + field(INPC,"chain4_pos.LOW PP") + field(FLNK,"chain4_pos") + field(TPRO, "$(TPRO=)") +} + +record(bo, "chain4_dummy") { +field(TPRO, "$(TPRO=)") +} + +# loop breaking regression part 2 +# selft link should _not_ RPRO +record(calcout,"chain5_cnt") { + field(CALC, "E:=E+1;E") + field(INPA,"chain5_cnt.A PP") + field(OUT,"chain5_cnt.A PP") + field(FLNK,"chain5_cnt") + field(TPRO, "$(TPRO=)") +} diff --git a/modules/libcom/configure/CONFIG_LIBCOM_VERSION b/modules/libcom/configure/CONFIG_LIBCOM_VERSION index 668c4fe1b..5a21198ff 100644 --- a/modules/libcom/configure/CONFIG_LIBCOM_VERSION +++ b/modules/libcom/configure/CONFIG_LIBCOM_VERSION @@ -1,4 +1,4 @@ EPICS_LIBCOM_MAJOR_VERSION = 3 EPICS_LIBCOM_MINOR_VERSION = 17 -EPICS_LIBCOM_MAINTENANCE_VERSION = 2 +EPICS_LIBCOM_MAINTENANCE_VERSION = 3 EPICS_LIBCOM_DEVELOPMENT_FLAG = 0 diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index 737d7e9c2..b105ea12f 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -319,7 +319,7 @@ static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt}; static const iocshArg * const epicsMutexShowAllArgs[2] = {&epicsMutexShowAllArg0,&epicsMutexShowAllArg1}; static const iocshFuncDef epicsMutexShowAllFuncDef = - {"epicsMutexShowAll",1,epicsMutexShowAllArgs}; + {"epicsMutexShowAll",2,epicsMutexShowAllArgs}; static void epicsMutexShowAllCallFunc(const iocshArgBuf *args) { epicsMutexShowAll(args[0].ival,args[1].ival); diff --git a/modules/libcom/src/osi/os/posix/osdMonotonic.c b/modules/libcom/src/osi/os/posix/osdMonotonic.c index 00c9c2871..65585797c 100644 --- a/modules/libcom/src/osi/os/posix/osdMonotonic.c +++ b/modules/libcom/src/osi/os/posix/osdMonotonic.c @@ -18,9 +18,6 @@ void osdMonotonicInit(void) { unsigned i; clockid_t ids[] = { -#ifdef CLOCK_MONOTONIC_RAW - CLOCK_MONOTONIC_RAW, /* Linux specific */ -#endif #ifdef CLOCK_HIGHRES CLOCK_HIGHRES, /* solaris specific */ #endif diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index c08dea40e..854441898 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -567,14 +567,13 @@ static epicsThreadOSD *createImplicit(void) pthreadInfo->osiPriority = 0; #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 - { - struct sched_param param; - int policy; - if(pthread_getschedparam(tid,&policy,¶m) == 0) - pthreadInfo->osiPriority = - (param.sched_priority - pcommonAttr->minPriority) * 100.0 / + if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) { + if ( pcommonAttr->usePolicy && pthreadInfo->schedPolicy == pcommonAttr->schedPolicy ) { + pthreadInfo->osiPriority = + (pthreadInfo->schedParam.sched_priority - pcommonAttr->minPriority) * 100.0 / (pcommonAttr->maxPriority - pcommonAttr->minPriority + 1); - } + } + } #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo); diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile index 1c26c44b7..f2e495a90 100755 --- a/modules/libcom/test/Makefile +++ b/modules/libcom/test/Makefile @@ -288,4 +288,15 @@ TESTPROD_HOST += cvtFastPerform cvtFastPerform_SRCS += cvtFastPerform.cpp testHarness_SRCS += cvtFastPerform.cpp +ifeq ($(OS_CLASS),Linux) +ifeq ($(USE_POSIX_THREAD_PRIORITY_SCHEDULING),YES) +TESTPROD_HOST += nonEpicsThreadPriorityTest +nonEpicsThreadPriorityTest_SRCS += nonEpicsThreadPriorityTest.cpp +nonEpicsThreadPriorityTest_SYS_LIBS += $(POSIX_LDLIBS:-l%=%) +testHarness_SRCS += nonEpicsThreadPriorityTest.cpp +epicsRunLibComTests_CFLAGS += -DHAVE_PTHREAD_PRIORITY_SCHEDULING +TESTS += nonEpicsThreadPriorityTest +endif +endif + include $(TOP)/configure/RULES diff --git a/modules/libcom/test/nonEpicsThreadPriorityTest.cpp b/modules/libcom/test/nonEpicsThreadPriorityTest.cpp new file mode 100644 index 000000000..088f0fca7 --- /dev/null +++ b/modules/libcom/test/nonEpicsThreadPriorityTest.cpp @@ -0,0 +1,100 @@ +#include +#include "epicsUnitTest.h" +#include "testMain.h" +#include +#include + +#ifdef __rtems__ + +/* RTEMS is posix but currently does not use the pthread API */ +MAIN(nonEpicsThreadPriorityTest) +{ + testPlan(1); + testSkip(1, "Platform does not use pthread API"); + return testDone(); +} + +#else + +static void *nonEpicsTestFunc(void *arg) +{ + unsigned int pri; + // epicsThreadGetIdSelf() creates an EPICS context + // verify that the priority computed by epics context + // is OK + pri = epicsThreadGetPriority( epicsThreadGetIdSelf() ); + if ( ! testOk( 0 == pri, "'createImplicit' assigned correct priority (%d) to non-EPICS thread", pri) ) { + return 0; + } + + return (void*)1; +} + + +static void testFunc(void *arg) +{ + epicsEventId ev = (epicsEventId)arg; + int policy; + struct sched_param param; + int status; + pthread_t tid; + void *rval; + pthread_attr_t attr; + + status = pthread_getschedparam(pthread_self(), &policy,¶m); + if ( status ) { + testSkip(1, "pthread_getschedparam failed"); + goto done; + } + + if ( SCHED_FIFO != policy ) { + testSkip(1, "nonEpicsThreadPriorityTest must be executed with privileges to use SCHED_FIFO"); + goto done; + } + + if ( pthread_attr_init( &attr ) ) { + testSkip(1, "pthread_attr_init failed"); + goto done; + } + if ( pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ) ) { + testSkip(1, "pthread_attr_setinheritsched failed"); + goto done; + } + if ( pthread_attr_setschedpolicy ( &attr, SCHED_OTHER ) ) { + testSkip(1, "pthread_attr_setschedpolicy failed"); + goto done; + } + param.sched_priority = 0; + if ( pthread_attr_setschedparam ( &attr, ¶m ) ) { + testSkip(1, "pthread_attr_setschedparam failed"); + goto done; + } + + if ( pthread_create( &tid, &attr, nonEpicsTestFunc, 0 ) ) { + testSkip(1, "pthread_create failed"); + goto done; + } + + if ( pthread_join( tid, &rval ) ) { + testSkip(1, "pthread_join failed"); + goto done; + } + +done: + + epicsEventSignal( ev ); +} + +MAIN(nonEpicsThreadPriorityTest) +{ + testPlan(2); + epicsEventId testComplete = epicsEventMustCreate(epicsEventEmpty); + epicsThreadMustCreate("nonEpicsThreadPriorityTest", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackMedium), + testFunc, testComplete); + epicsEventWaitStatus status = epicsEventWait(testComplete); + testOk(status == epicsEventWaitOK, + "epicsEventWait returned %d", status); + return testDone(); +} +#endif diff --git a/modules/normativeTypes b/modules/normativeTypes index 41d56fdf8..e803240fb 160000 --- a/modules/normativeTypes +++ b/modules/normativeTypes @@ -1 +1 @@ -Subproject commit 41d56fdf89a09617f2d5fe726fa0ecb579ca551f +Subproject commit e803240fbfbb06d96d94fc70e91b4cf832811b15 diff --git a/modules/pvAccess b/modules/pvAccess index 5e7934221..78410499f 160000 --- a/modules/pvAccess +++ b/modules/pvAccess @@ -1 +1 @@ -Subproject commit 5e793422198d4223a03164787cacbfb6cf9575fc +Subproject commit 78410499f0236f5830a0937567bbb794de7b2347 diff --git a/modules/pvData b/modules/pvData index d776f6eaf..8c275cbc1 160000 --- a/modules/pvData +++ b/modules/pvData @@ -1 +1 @@ -Subproject commit d776f6eaf03c058597c05b3308636d95223b6c6e +Subproject commit 8c275cbc1caca1f6175b75b6f0ffc2d4abc234db diff --git a/modules/pvDatabase b/modules/pvDatabase index 07a951c3a..b3f96f730 160000 --- a/modules/pvDatabase +++ b/modules/pvDatabase @@ -1 +1 @@ -Subproject commit 07a951c3a2bd3e95b1de24e3224e310225e3e45a +Subproject commit b3f96f730fcf1b07a24125fd06b8c413a1e93b5d diff --git a/modules/pva2pva b/modules/pva2pva index a245adb5c..521154fd5 160000 --- a/modules/pva2pva +++ b/modules/pva2pva @@ -1 +1 @@ -Subproject commit a245adb5cfc9b4a0b0d04b630032963ecc3410d0 +Subproject commit 521154fd52d149ea578a73cabc1f7e4c784cf14f