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();