diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 7eb9fdcc9..5dc38f409 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -700,6 +700,43 @@ tells git to ignore all configure/*.local files.

+

Fixes for Launchpad bugs

+ +

The following launchpad bugs have fixes included:

+ + + +

Updated VxWorks Timezone settings

+ +

Removed the settings for 2017; fixed the hour of the change for MET.

+ +

Fixed camonitor server side relative timestamps bug

+ +

Initialize the first time-stamp from the first monitor, not the client-side +current time in this configuration.

+ +

Build changes for MSVC

+ +

Windows builds using Visual Studio 2015 and later now use the -FS +compiler option to allow parallel builds to work properly.

+ +

We now give the -FC option to tell the compiler to print absolute +paths for source files in diagnostic messages.

+

Extend maximum Posix epicsEventWaitWithTimeout() delay

The Posix implementation of epicsEventWaitWithTimeout() was limiting the diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index 058dc8d8c..ae074141c 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -45,6 +45,7 @@ #include "epicsExport.h" #include "link.h" #include "recSup.h" +#include "dbUnitTest.h" /* for testSyncCallback() */ static int callbackQueueSize = 2000; @@ -352,3 +353,86 @@ void callbackRequestProcessCallbackDelayed(CALLBACK *pcallback, callbackSetProcess(pcallback, Priority, pRec); callbackRequestDelayed(pcallback, seconds); } + +/* Sync. process of testSyncCallback() + * + * 1. For each priority, make a call to callbackRequest() for each worker. + * 2. Wait until all callbacks are concurrently being executed + * 3. Last worker to begin executing signals success and begins waking up other workers + * 4. Last worker to wake signals testSyncCallback() to complete + */ +typedef struct { + epicsEventId wait_phase2, wait_phase4; + int nphase2, nphase3; + epicsCallback cb; +} sync_helper; + +static void sync_callback(epicsCallback *cb) +{ + sync_helper *helper; + callbackGetUser(helper, cb); + + testGlobalLock(); + + assert(helper->nphase2 > 0); + if(--helper->nphase2!=0) { + /* we are _not_ the last to start. */ + testGlobalUnlock(); + epicsEventMustWait(helper->wait_phase2); + testGlobalLock(); + } + + /* we are either the last to start, or have been + * woken by the same and must pass the wakeup along + */ + epicsEventMustTrigger(helper->wait_phase2); + + assert(helper->nphase2 == 0); + assert(helper->nphase3 > 0); + + if(--helper->nphase3==0) { + /* we are the last to wake up. wake up testSyncCallback() */ + epicsEventMustTrigger(helper->wait_phase4); + } + + testGlobalUnlock(); +} + +void testSyncCallback(void) +{ + sync_helper helper[NUM_CALLBACK_PRIORITIES]; + unsigned i; + + testDiag("Begin testSyncCallback()"); + + for(i=0; iupper_alarm_limit = (epicsInt32) ald.upper_alarm_limit; - pal->upper_warning_limit = (epicsInt32) ald.upper_warning_limit; - pal->lower_warning_limit = (epicsInt32) ald.lower_warning_limit; - pal->lower_alarm_limit = (epicsInt32) ald.lower_alarm_limit; + pal->upper_alarm_limit = finite(ald.upper_alarm_limit) ? + (epicsInt32) ald.upper_alarm_limit : 0; + pal->upper_warning_limit = finite(ald.upper_warning_limit) ? + (epicsInt32) ald.upper_warning_limit : 0; + pal->lower_warning_limit = finite(ald.lower_warning_limit) ? + (epicsInt32) ald.lower_warning_limit : 0; + pal->lower_alarm_limit = finite(ald.lower_alarm_limit) ? + (epicsInt32) ald.lower_alarm_limit : 0; if (no_data) *options ^= DBR_AL_LONG; /*Turn off option*/ diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index e7de3ba57..d5ca3d933 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -20,6 +20,7 @@ #include "osiUnistd.h" #include "registry.h" #include "epicsEvent.h" +#include "epicsThread.h" #define epicsExportSharedSymbols #include "dbAccess.h" @@ -414,3 +415,26 @@ unsigned testMonitorCount(testMonitor *mon, unsigned reset) return count; } +static +epicsMutexId test_global; + +static +epicsThreadOnceId test_global_once = EPICS_THREAD_ONCE_INIT; + +static +void test_global_init(void* ignored) +{ + test_global = epicsMutexMustCreate(); +} + +void testGlobalLock(void) +{ + epicsThreadOnce(&test_global_once, &test_global_init, NULL); + epicsMutexMustLock(test_global); +} + +void testGlobalUnlock(void) +{ + epicsMutexUnlock(test_global); +} + diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h index 897e5c67d..e38b8c67a 100644 --- a/src/ioc/db/dbUnitTest.h +++ b/src/ioc/db/dbUnitTest.h @@ -98,6 +98,65 @@ epicsShareFunc void testMonitorWait(testMonitor*); */ epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset); +/** Synchronize the shared callback queues. + * + * Block until all callback queue jobs which were queued, or running, + * have completed. + */ +epicsShareFunc void testSyncCallback(void); + +/** Global mutex for use by test code. + * + * This utility mutex is intended to be used to avoid races in situations + * where some other syncronization primitive is being destroyed (epicsEvent, + * epicsMutex, ...). + * + * For example. The following has a subtle race where the event may be + * destroyed (free()'d) before the call to epicsEventMustSignal() has + * returned. On some targets this leads to a use after free() error. + * + @code + epicsEventId evt; + void thread1() { + evt = epicsEventMustCreate(...); + // spawn thread2() + epicsEventMustWait(evt); + epicsEventDestroy(evt); + } + // ... + void thread2() { + epicsEventMustSignal(evt); + } + @endcode + * + * One way to avoid this race is to use a global mutex to ensure + * that epicsEventMustSignal() has returned before destroying + * the event. + * + @code + epicsEventId evt; + void thread1() { + evt = epicsEventMustCreate(...); + // spawn thread2() + epicsEventMustWait(evt); + testGlobalLock(); // <-- added + epicsEventDestroy(evt); + testGlobalUnlock(); // <-- added + } + // ... + void thread2() { + testGlobalLock(); // <-- added + epicsEventMustSignal(evt); + testGlobalUnlock(); // <-- added + } + @endcode + * + * This must be a global mutex to avoid simply shifting the race + * from the event to a locally allocated mutex. + */ +epicsShareFunc void testGlobalLock(void); +epicsShareFunc void testGlobalUnlock(void); + #ifdef __cplusplus } #endif diff --git a/src/std/rec/longinRecord.c b/src/std/rec/longinRecord.c index bd3c24ab9..72170bbb9 100644 --- a/src/std/rec/longinRecord.c +++ b/src/std/rec/longinRecord.c @@ -22,6 +22,7 @@ #include "dbDefs.h" #include "epicsPrint.h" +#include "epicsMath.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" @@ -226,15 +227,17 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { longinRecord *prec=(longinRecord *)paddr->precord; - if(dbGetFieldIndex(paddr) == indexof(VAL)){ - pad->upper_alarm_limit = prec->hihi; - pad->upper_warning_limit = prec->high; - pad->lower_warning_limit = prec->low; - pad->lower_alarm_limit = prec->lolo; - } else recGblGetAlarmDouble(paddr,pad); - return(0); + if (dbGetFieldIndex(paddr) == indexof(VAL)){ + pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; + pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; + pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; + pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; + } + else + recGblGetAlarmDouble(paddr,pad); + return 0; } - + static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast) { enum { diff --git a/src/std/rec/longoutRecord.c b/src/std/rec/longoutRecord.c index 6062ad741..1e12741a0 100644 --- a/src/std/rec/longoutRecord.c +++ b/src/std/rec/longoutRecord.c @@ -19,6 +19,7 @@ #include "dbDefs.h" #include "epicsPrint.h" +#include "epicsMath.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" @@ -261,15 +262,17 @@ static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) { longoutRecord *prec=(longoutRecord *)paddr->precord; - if(dbGetFieldIndex(paddr) == indexof(VAL)) { - pad->upper_alarm_limit = prec->hihi; - pad->upper_warning_limit = prec->high; - pad->lower_warning_limit = prec->low; - pad->lower_alarm_limit = prec->lolo; - } else recGblGetAlarmDouble(paddr,pad); - return(0); + if (dbGetFieldIndex(paddr) == indexof(VAL)) { + pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; + pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; + pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; + pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; + } + else + recGblGetAlarmDouble(paddr,pad); + return 0; } - + static void checkAlarms(longoutRecord *prec) { epicsInt32 val, hyst, lalm; diff --git a/src/std/rec/test/scanEventTest.c b/src/std/rec/test/scanEventTest.c index 77acbbeca..6b90a413b 100644 --- a/src/std/rec/test/scanEventTest.c +++ b/src/std/rec/test/scanEventTest.c @@ -65,21 +65,18 @@ MAIN(scanEventTest) testPlan(NELEMENTS(events)*2+(MAXEV+1)*5); + testdbPrepare(); + memset(aliases, 0, sizeof(aliases)); memset(expected_count, 0, sizeof(expected_count)); - if (dbReadDatabase(&pdbbase, "scanEventTest.dbd", - "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR - "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Database description 'scanEventTest.dbd' not found"); + testdbReadDatabase("scanEventTest.dbd", NULL, NULL); scanEventTest_registerRecordDeviceDriver(pdbbase); for (i = 0; i < NELEMENTS(events); i++) { char substitutions[256]; sprintf(substitutions, "N=%d,EVENT=%s", i, events[i].name); - if (dbReadDatabase(&pdbbase, "scanEventTest.db", - "." OSI_PATH_LIST_SEPARATOR "..", substitutions)) - testAbort("Error reading test database 'scanEventTest.db'"); + testdbReadDatabase("scanEventTest.db", NULL, substitutions); } testIocInitOk(); testDiag("Test if eventNameToHandle() strips spaces and handles numeric events"); @@ -129,7 +126,7 @@ MAIN(scanEventTest) } } /* Allow records to finish processing */ - epicsThreadSleep(0.1); + testSyncCallback(); testDiag("Check if events have been processed the expected number of times"); for (i = 0; i < NELEMENTS(events); i++) { char pvname[100]; @@ -138,5 +135,9 @@ MAIN(scanEventTest) testdbGetFieldEqual(pvname, DBR_LONG, expected_count[INDX(i)]); } + testIocShutdownOk(); + + testdbCleanup(); + return testDone(); } diff --git a/src/template/base/top/exampleApp/src/xxxRecord.c b/src/template/base/top/exampleApp/src/xxxRecord.c index c2693c48e..97640d2b6 100644 --- a/src/template/base/top/exampleApp/src/xxxRecord.c +++ b/src/template/base/top/exampleApp/src/xxxRecord.c @@ -198,8 +198,8 @@ static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) static void checkAlarms(xxxRecord *prec) { - double val; - float hyst, lalm, hihi, high, low, lolo; + double val, hyst, lalm; + float hihi, high, low, lolo; unsigned short hhsv, llsv, hsv, lsv; if(prec->udf == TRUE ){ diff --git a/src/tools/convertRelease.pl b/src/tools/convertRelease.pl index 0da10bb72..741d3e4b8 100644 --- a/src/tools/convertRelease.pl +++ b/src/tools/convertRelease.pl @@ -24,8 +24,7 @@ use lib ("$Bin/../../lib/perl", $Bin); use EPICS::Path; use EPICS::Release; -use vars qw($arch $top $iocroot $root); - +our ($arch, $top, $iocroot, $root); our ($opt_a, $opt_t, $opt_T); getopts('a:t:T:') or HELP_MESSAGE();