From 80343363a46d366055225d45a382976d6e72fd42 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 1 Jul 2015 11:30:02 -0500 Subject: [PATCH 01/12] configure: Include RULES_FILE_TYPE earlier From Benjamin Franksen: There is one remaining problem with the order of includes in the build rules for 3.14.12.5: configure/RULES_BUILD includes RULES_FILE_TYPE (which in turn includes the cfg/RULES* from modules in the RELEASE file) *after* including RULES.Db and RULES_JAVA. This makes it impossible to override (pattern) rules in one of these files. Instead, $(CONFIG)/RULES_FILE_TYPE should be included first. --- configure/RULES_BUILD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index c0e1ffbe7..1528c48d5 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -69,6 +69,8 @@ endif all: install +-include $(CONFIG)/RULES_FILE_TYPE + -include $(CONFIG)/RULES.Db -include $(CONFIG)/RULES_JAVA @@ -110,8 +112,6 @@ LIBTARGETS += $(LIBNAME) $(INSTALL_LIBS) \ $(INSTALL_LIBS): $(INSTALL_SHRLIBS) $(INSTALL_DLL_LINK_LIBS) $(INSTALL_LOADABLE_SHRLIBS) --include $(CONFIG)/RULES_FILE_TYPE - # Main targets install: buildInstall From 625172419e44361924a0ae92b4140b98f609f271 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 1 Jul 2015 12:00:42 -0500 Subject: [PATCH 02/12] libCom: Make epicsReadline behave the same on Darwin --- src/libCom/osi/os/default/epicsReadline.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libCom/osi/os/default/epicsReadline.c b/src/libCom/osi/os/default/epicsReadline.c index 1d7d9c1d5..7cdcfdafe 100644 --- a/src/libCom/osi/os/default/epicsReadline.c +++ b/src/libCom/osi/os/default/epicsReadline.c @@ -79,6 +79,7 @@ epicsReadlineEnd(void *context) #include #include +#include struct readlineContext { FILE *in; @@ -138,6 +139,14 @@ epicsReadline (const char *prompt, void *context) free (readlineContext->line); readlineContext->line = NULL; if (readlineContext->in == NULL) { + if (!isatty(fileno(stdout))) { + /* The libedit readline emulator on Darwin doesn't + * print the prompt when the terminal isn't a tty. + */ + fputs (prompt, stdout); + fflush (stdout); + rl_already_prompted = 1; + } rlState = rlBusy; line = readline (prompt); rlState = rlIdle; From 138e2f1ad595b97bf3437c59b3dcb0a9eaa46a0a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 16 Jul 2015 17:05:41 -0400 Subject: [PATCH 03/12] callback: fix race on shutdown epicsEventDestory() in the main thread could be called before epicsEventTrigger() completes in the worker. --- src/ioc/db/callback.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index 897279ff9..b15220469 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -75,6 +75,8 @@ enum ctl {ctlInit, ctlRun, ctlPause, ctlExit}; static volatile enum ctl cbCtl; static epicsEventId startStopEvent; +static int callbackIsInit; + /* Static data */ static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = { "cbLow", "cbMedium", "cbHigh" @@ -89,7 +91,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2}; int callbackSetQueueSize(int size) { - if (startStopEvent) { + if (callbackIsInit) { errlogPrintf("Callback system already initialized\n"); return -1; } @@ -99,7 +101,7 @@ int callbackSetQueueSize(int size) int callbackParallelThreads(int count, const char *prio) { - if (startStopEvent) { + if (callbackIsInit) { errlogPrintf("Callback system already initialized\n"); return -1; } @@ -209,8 +211,7 @@ void callbackCleanup(void) } epicsTimerQueueRelease(timerQueue); - epicsEventDestroy(startStopEvent); - startStopEvent = NULL; + callbackIsInit = 0; memset(callbackQueue, 0, sizeof(callbackQueue)); } @@ -220,10 +221,14 @@ void callbackInit(void) int j; char threadName[32]; - if (startStopEvent) + if (callbackIsInit) { + errlogMessage("Warning: callbackInit called again before callbackCleanup\n"); return; + } + callbackIsInit = 1; - startStopEvent = epicsEventMustCreate(epicsEventEmpty); + if(!startStopEvent) + startStopEvent = epicsEventMustCreate(epicsEventEmpty); cbCtl = ctlRun; timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh); From 8bfa40d85802909990948c2d4c03350c40ba326b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 16 Jul 2015 17:05:41 -0400 Subject: [PATCH 04/12] dbScan: avoid race on shutdown --- src/ioc/db/dbScan.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 9c1a1bef9..0e2d09326 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -178,10 +178,6 @@ void scanCleanup(void) epicsRingPointerDelete(onceQ); - epicsEventDestroy(startStopEvent); - epicsEventDestroy(onceSem); - onceSem = startStopEvent = NULL; - free(periodicTaskId); papPeriodic = NULL; periodicTaskId = NULL; @@ -191,7 +187,8 @@ long scanInit(void) { int i; - startStopEvent = epicsEventMustCreate(epicsEventEmpty); + if(!startStopEvent) + startStopEvent = epicsEventMustCreate(epicsEventEmpty); scanCtl = ctlPause; initPeriodic(); @@ -643,7 +640,8 @@ static void initOnce(void) if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) { cantProceed("initOnce: Ring buffer create failed\n"); } - onceSem = epicsEventMustCreate(epicsEventEmpty); + if(!onceSem) + onceSem = epicsEventMustCreate(epicsEventEmpty); onceTaskId = epicsThreadCreate("scanOnce", epicsThreadPriorityScanLow + nPeriodic, epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0); From 002bafdf07c7efe33d8dbb1eea45c5fc91de62e9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 17 Jul 2015 18:06:58 -0500 Subject: [PATCH 05/12] Add testHarnessDone --- src/libCom/misc/epicsUnitTest.c | 4 ++-- src/libCom/misc/epicsUnitTest.h | 2 ++ src/libCom/test/epicsRunLibComTests.c | 7 ++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libCom/misc/epicsUnitTest.c b/src/libCom/misc/epicsUnitTest.c index 9feb59195..730bde7a2 100644 --- a/src/libCom/misc/epicsUnitTest.c +++ b/src/libCom/misc/epicsUnitTest.c @@ -210,7 +210,7 @@ int testDone(void) { /* Our test harness, for RTEMS and vxWorks */ -static void harnessExit(void *dummy) { +void testHarnessExit(void *dummy) { epicsTimeStamp ended; int Faulty; @@ -247,7 +247,7 @@ static void harnessExit(void *dummy) { void testHarness(void) { epicsThreadOnce(&onceFlag, testOnce, NULL); - epicsAtExit(harnessExit, NULL); + epicsAtExit(testHarnessExit, NULL); Harness = 1; Programs = 0; Tests = 0; diff --git a/src/libCom/misc/epicsUnitTest.h b/src/libCom/misc/epicsUnitTest.h index c1f0a5939..47f5cf53a 100644 --- a/src/libCom/misc/epicsUnitTest.h +++ b/src/libCom/misc/epicsUnitTest.h @@ -40,9 +40,11 @@ epicsShareFunc int testDone(void); typedef int (*TESTFUNC)(void); epicsShareFunc void testHarness(void); +epicsShareFunc void testHarnessExit(void *dummy); epicsShareFunc void runTestFunc(const char *name, TESTFUNC func); #define runTest(func) runTestFunc(#func, func) +#define testHarnessDone() testHarnessExit(0) #ifdef __cplusplus } diff --git a/src/libCom/test/epicsRunLibComTests.c b/src/libCom/test/epicsRunLibComTests.c index e49c10166..aa62465ff 100644 --- a/src/libCom/test/epicsRunLibComTests.c +++ b/src/libCom/test/epicsRunLibComTests.c @@ -104,7 +104,12 @@ void epicsRunLibComTests(void) runTest(taskwdTest); /* - * Exit must come last as it never returns + * Report now in case epicsExitTest dies + */ + testHarnessDone(); + + /* + * epicsExitTest must come last as it never returns */ runTest(epicsExitTest); } From ae3d3904d99daed6d6812481a4429c8bb48c55db Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 17 Jul 2015 18:09:14 -0500 Subject: [PATCH 06/12] Fix minor locking issue in dbEvent.c --- src/db/dbEvent.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/db/dbEvent.c b/src/db/dbEvent.c index 76f4f1a2c..3bcca8469 100644 --- a/src/db/dbEvent.c +++ b/src/db/dbEvent.c @@ -916,6 +916,7 @@ static void event_task (void *pParm) { struct event_user * const evUser = (struct event_user *) pParm; struct event_que * ev_que; + unsigned char pendexit; /* init hook */ if (evUser->init_func) { @@ -957,9 +958,10 @@ static void event_task (void *pParm) event_read (ev_que); epicsMutexMustLock ( evUser->lock ); } + pendexit = evUser->pendexit; epicsMutexUnlock ( evUser->lock ); - } while( ! evUser->pendexit ); + } while( ! pendexit ); epicsMutexDestroy(evUser->firstque.writelock); From 1411522a11b2d67d68b5275816232c4119c237a1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 21 Jul 2015 15:53:37 -0400 Subject: [PATCH 07/12] dbCa: only add dbCaExit once --- src/ioc/db/dbCa.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index c7beddeed..f0c6f6f36 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -232,10 +232,11 @@ void dbCaLinkInitIsolated(void) { if (!workListLock) workListLock = epicsMutexMustCreate(); - if (!workListEvent) + if (!workListEvent) { workListEvent = epicsEventMustCreate(epicsEventEmpty); + epicsAtExit(dbCaExit, NULL); + } dbCaCtl = ctlExit; - epicsAtExit(dbCaExit, NULL); } void dbCaLinkInit(void) From 504665bf09a14473c86a8e613e0a3c60ccc28eea Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 21 Jul 2015 19:08:07 -0400 Subject: [PATCH 08/12] iocInit: move dbCaShutdown earlier Switch dbCaShutdown from an exit hook to be called from iocShutdown. Place it after callback shutdown. --- src/ioc/db/dbCa.c | 9 +-------- src/ioc/misc/iocInit.c | 3 +++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index f0c6f6f36..edeeac524 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -223,19 +223,12 @@ void dbCaShutdown(void) } } -static void dbCaExit(void *arg) -{ - dbCaShutdown(); -} - void dbCaLinkInitIsolated(void) { if (!workListLock) workListLock = epicsMutexMustCreate(); - if (!workListEvent) { + if (!workListEvent) workListEvent = epicsEventMustCreate(epicsEventEmpty); - epicsAtExit(dbCaExit, NULL); - } dbCaCtl = ctlExit; } diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 9759ba18f..1e30eeb77 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -682,6 +682,9 @@ int iocShutdown(void) /* stop and "join" threads */ scanStop(); callbackStop(); + } + dbCaShutdown(); + if (iocBuildMode==buildIsolated) { /* free resources */ scanCleanup(); callbackCleanup(); From 144281e0a329efec4c00516c42e861d2489c709d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 23 Jul 2015 12:34:05 -0500 Subject: [PATCH 09/12] Removed extraneous .db file --- src/ioc/db/test/sRecord.db | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/ioc/db/test/sRecord.db diff --git a/src/ioc/db/test/sRecord.db b/src/ioc/db/test/sRecord.db deleted file mode 100644 index 790a7df7c..000000000 --- a/src/ioc/db/test/sRecord.db +++ /dev/null @@ -1 +0,0 @@ -record(ai, "somename") {} From 2f8e6bf17edf40bff55877024657e07160429121 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 23 Jul 2015 13:05:38 -0500 Subject: [PATCH 10/12] Fix smoothing for Nan/Inf values in devAiSoftCallback --- src/std/dev/devAiSoftCallback.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/std/dev/devAiSoftCallback.c b/src/std/dev/devAiSoftCallback.c index d9427d68b..3744509ed 100644 --- a/src/std/dev/devAiSoftCallback.c +++ b/src/std/dev/devAiSoftCallback.c @@ -23,6 +23,7 @@ #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" +#include "epicsMath.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" @@ -190,7 +191,7 @@ static long read_ai(aiRecord *prec) } /* Apply smoothing algorithm */ - if (prec->smoo != 0.0 && pdevPvt->smooth) + if (prec->smoo != 0.0 && pdevPvt->smooth && finite(prec->val)) prec->val = prec->val * prec->smoo + pdevPvt->buffer.value * (1.0 - prec->smoo); else From c56091978c8d91c95742d49d1315acc478748e3f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 23 Jul 2015 17:57:30 -0500 Subject: [PATCH 11/12] db: Fixed obscure dbCa bug This could be triggered if dbCaRemoveLink() is called on a link for which there is an outstanding dbCaPutCallback(). Normally a dbCaPutCallback() callback routine is passed the associated userPvt pointer as an argument, but in the event that dbCaRemoveLink() gets used on the same link between the put and its completion callback, the callback routine was being called with the link pointer as the argument instead. For all the existing Asyn Soft Channel device supports this is not a problem as they all pass the link pointer as their userPvt argument, but that won't necessarily always be the case. Also updated the comments describing the process of removing links. --- src/db/dbCa.c | 36 +++++++++++++++--------------------- src/db/dbCaPvt.h | 1 - 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/db/dbCa.c b/src/db/dbCa.c index bb976bd5b..27ed3cb8b 100644 --- a/src/db/dbCa.c +++ b/src/db/dbCa.c @@ -98,7 +98,7 @@ static void dbCaTask(void *); * that has been deleted. * * Just a few words about handling dbCaRemoveLink because this is when - * it is essential that nothing trys to use a caLink that has been freed. + * it is essential that nothing tries to use a caLink that has been freed. * * dbCaRemoveLink is called when links are being modified. This is only * done with the dbScan mechanism guranteeing that nothing from @@ -117,19 +117,16 @@ static void dbCaTask(void *); * dbCaTask issues a ca_clear_channel and then frees the caLink. * * If any channel access callback gets called before the ca_clear_channel - * it finds pca->plink=0 and does nothing. Once ca_clear_channel + * it finds pca->plink==0 and does nothing. Once ca_clear_channel * is called no other callback for this caLink will be called. * * dbCaPutLinkCallback causes an additional complication because * when dbCaRemoveLink is called the callback may not have occured. - * What is done is the following: - * If callback has not occured dbCaRemoveLink sets plinkPutCallback=plink - * If putCallback is called before dbCaTask calls ca_clear_channel - * it does NOT call the users callback. - * dbCaTask calls the users callback passing plinkPutCallback AFTER - * it has called ca_clear_channel - * Thus the users callback will get called exactly once. -*/ + * If putComplete sees plink==0 it will not call the user's code. + * When dbCaTask performs a CA_CLEAR_CHANNEL action, if pca->putCallback + * is non-zero it will call that callback AFTER ca_clear_channel. + * Thus the user's callback will get called exactly once. + */ static void addAction(caLink *pca, short link_action) { @@ -163,9 +160,9 @@ static void addAction(caLink *pca, short link_action) epicsEventSignal(workListEvent); } -void dbCaCallbackProcess(void *usrPvt) +void dbCaCallbackProcess(void *userPvt) { - struct link *plink = (struct link *)usrPvt; + struct link *plink = (struct link *)userPvt; dbCommon *pdbCommon = plink->value.pv_link.precord; dbScanLock(pdbCommon); @@ -239,8 +236,6 @@ void dbCaRemoveLink(struct link *plink) epicsMutexMustLock(pca->lock); pca->plink = 0; plink->value.pv_link.pvt = 0; - if (pca->putCallback) - pca->plinkPutCallback = plink; /* Unlock before addAction or dbCaTask might free first */ epicsMutexUnlock(pca->lock); addAction(pca, CA_CLEAR_CHANNEL); @@ -735,7 +730,7 @@ static void exceptionCallback(struct exception_handler_args args) } } -static void putCallback(struct event_handler_args arg) +static void putComplete(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; struct link *plink; @@ -866,7 +861,7 @@ static void dbCaTask(void *arg) epicsMutexUnlock(workListLock); /* Give back immediately */ if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */ dbCaCallback callback; - struct link *plinkPutCallback = 0; + void *userPvt = 0; if (pca->chid) { ca_clear_channel(pca->chid); @@ -874,8 +869,7 @@ static void dbCaTask(void *arg) } callback = pca->putCallback; if (callback) { - plinkPutCallback = pca->plinkPutCallback; - pca->plinkPutCallback = 0; + userPvt = pca->putUserPvt; pca->putCallback = 0; pca->putType = 0; } @@ -887,7 +881,7 @@ static void dbCaTask(void *arg) epicsMutexDestroy(pca->lock); free(pca); /* No alarm is raised. Since link is changing so what? */ - if (callback) callback(plinkPutCallback); + if (callback) callback(userPvt); continue; /* No other link_action makes sense */ } if (link_action & CA_CONNECT) { @@ -921,7 +915,7 @@ static void dbCaTask(void *arg) status = ca_array_put_callback( pca->dbrType, pca->nelements, pca->chid, pca->pputNative, - putCallback, pca); + putComplete, pca); } else { status = ECA_PUTFAIL; } @@ -944,7 +938,7 @@ static void dbCaTask(void *arg) status = ca_array_put_callback( DBR_STRING, 1, pca->chid, pca->pputString, - putCallback, pca); + putComplete, pca); } else { status = ECA_PUTFAIL; } diff --git a/src/db/dbCaPvt.h b/src/db/dbCaPvt.h index 9625bfbd6..a0a1f65a0 100644 --- a/src/db/dbCaPvt.h +++ b/src/db/dbCaPvt.h @@ -58,7 +58,6 @@ typedef struct caLink short putType; dbCaCallback putCallback; void *putUserPvt; - struct link *plinkPutCallback; /* The following are for access to additional attributes*/ char gotAttributes; dbCaCallback getAttributes; From c5130468cd511b12be68044c789465abb8a05927 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 24 Jul 2015 10:42:09 -0500 Subject: [PATCH 12/12] Let dbdToHtml work with wrong newlines --- src/tools/dbdToHtml.pl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/dbdToHtml.pl b/src/tools/dbdToHtml.pl index bf162fefc..8e890ec1a 100644 --- a/src/tools/dbdToHtml.pl +++ b/src/tools/dbdToHtml.pl @@ -80,7 +80,7 @@ open my $out, '>', $opt_o or my $pod = join "\n", '=for html
', '', map { # Handle a 'recordtype' Pod directive - if (m/^ =recordtype \s+ (.*)/x) { + if (m/^ =recordtype \s+ (\w+) /x) { my $rn = $1; my $rtyp = $dbd->recordtype($rn); die "Unknown recordtype '$rn' in $infile POD directive\n" @@ -88,7 +88,7 @@ my $pod = join "\n", '=for html
', '', rtypeToPod($rtyp, $dbd); } # Handle a 'menu' Pod directive - elsif (m/^ =menu \s+ (.*)/x) { + elsif (m/^ =menu \s+ (\w+) /x) { my $mn = $1; my $menu = $dbd->menu($mn); die "Unknown menu '$mn' in $infile POD directive\n" @@ -152,7 +152,7 @@ sub rtypeToPod { my ($rtyp, $dbd) = @_; return map { # Handle a 'fields' Pod directive - if (m/^ =fields \s+ (.*)/x) { + if (m/^ =fields \s+ (\w+ (?:\s* , \s* \w+ )* )/x) { my @names = split /\s*,\s*/, $1; # Look up the named fields my @fields = map { @@ -170,7 +170,7 @@ sub rtypeToPod { '', '', '=end html'; } # Handle a 'menu' Pod directive - elsif (m/^ =menu \s+ (.*)/x) { + elsif (m/^ =menu \s+ (\w+) /x) { my $mn = $1; my $menu = $dbd->menu($mn); die "Unknown menu '$mn' in $infile POD directive\n" @@ -218,7 +218,7 @@ sub fieldTableRow { # Native type presented to dbAccess users sub DBD::Recfield::public_type { my $fld = shift; - m/^=type (.+)$/i && return $1 for $fld->comments; + m/^ =type \s+ (.+) /x && return $1 for $fld->comments; my $type = $fld->dbf_type; $type =~ s/^DBF_//; return $type; @@ -227,7 +227,7 @@ sub DBD::Recfield::public_type { # Check if this field is readable sub DBD::Recfield::readable { my $fld = shift; - m/^=read (Yes|No)$/i && return $1 for $fld->comments; + m/^ =read \s+ (?i) (Yes|No) /x && return $1 for $fld->comments; return 'Probably' if $fld->attribute('special') eq "SPC_DBADDR"; return $fld->dbf_type eq 'DBF_NOACCESS' ? 'No' : 'Yes'; @@ -236,7 +236,7 @@ sub DBD::Recfield::readable { # Check if this field is writable sub DBD::Recfield::writable { my $fld = shift; - m/^=write (Yes|No)$/i && return $1 for $fld->comments; + m/^ =write \s+ (?i) (Yes|No) /x && return $1 for $fld->comments; my $special = $fld->attribute('special'); return 'No' if $special eq "SPC_NOMOD";