Merge changes from 3.15 into 3.16
This commit is contained in:
@@ -700,6 +700,43 @@ tells git to ignore all configure/*.local files.</p>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
<h3>Fixes for Launchpad bugs</h3>
|
||||
|
||||
<p>The following launchpad bugs have fixes included:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1747091">
|
||||
lp: #1747091</a>, epicsTimeGetEvent() / generalTime bug</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1743076">
|
||||
lp: #1743076</a>, Segfault in ca_attach_context() during exits</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1751380">
|
||||
lp: #1751380</a>, Deadlock in ca_clear_subscription()</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1597809">
|
||||
lp: #1597809</a>, Setting NAME field in DB file may break IOC</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1770292">
|
||||
lp: #1770292</a>, get_alarm_double() inconsistent across record types</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1771298">
|
||||
lp: #1771298</a>, Conversion of NaN to integer relies on undefined
|
||||
behavior</li>
|
||||
</ul>
|
||||
|
||||
<h3>Updated VxWorks Timezone settings</h3>
|
||||
|
||||
<p>Removed the settings for 2017; fixed the hour of the change for MET.</p>
|
||||
|
||||
<h3>Fixed camonitor server side relative timestamps bug</h3>
|
||||
|
||||
<p>Initialize the first time-stamp from the first monitor, not the client-side
|
||||
current time in this configuration.</p>
|
||||
|
||||
<h3>Build changes for MSVC</h3>
|
||||
|
||||
<p>Windows builds using Visual Studio 2015 and later now use the <tt>-FS</tt>
|
||||
compiler option to allow parallel builds to work properly.</p>
|
||||
|
||||
<p>We now give the <tt>-FC</tt> option to tell the compiler to print absolute
|
||||
paths for source files in diagnostic messages.</p>
|
||||
|
||||
<h3>Extend maximum Posix epicsEventWaitWithTimeout() delay</h3>
|
||||
|
||||
<p>The Posix implementation of epicsEventWaitWithTimeout() was limiting the
|
||||
|
||||
@@ -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; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
helper[i].wait_phase2 = epicsEventMustCreate(epicsEventEmpty);
|
||||
helper[i].wait_phase4 = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
/* no real need to lock here, but do so anyway so that valgrind can establish
|
||||
* the locking requirements for sync_helper.
|
||||
*/
|
||||
testGlobalLock();
|
||||
helper[i].nphase2 = helper[i].nphase3 = callbackQueue[i].threadsRunning;
|
||||
testGlobalUnlock();
|
||||
|
||||
callbackSetUser(&helper[i], &helper[i].cb);
|
||||
callbackSetPriority(i, &helper[i].cb);
|
||||
callbackSetCallback(sync_callback, &helper[i].cb);
|
||||
|
||||
callbackRequest(&helper[i].cb);
|
||||
}
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
epicsEventMustWait(helper[i].wait_phase4);
|
||||
}
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
testGlobalLock();
|
||||
epicsEventDestroy(helper[i].wait_phase2);
|
||||
epicsEventDestroy(helper[i].wait_phase4);
|
||||
testGlobalUnlock();
|
||||
}
|
||||
|
||||
testDiag("Complete testSyncCallback()");
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ typedef struct callbackPvt {
|
||||
int priority;
|
||||
void *user; /*for use by callback user*/
|
||||
void *timer; /*for use by callback itself*/
|
||||
}CALLBACK;
|
||||
}epicsCallback;
|
||||
|
||||
typedef epicsCallback CALLBACK;
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
|
||||
@@ -293,10 +293,14 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer,
|
||||
if (*options & DBR_AL_LONG) {
|
||||
struct dbr_alLong *pal = (struct dbr_alLong*) pbuffer;
|
||||
|
||||
pal->upper_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*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 ){
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user