diff --git a/configure/RULES.Db b/configure/RULES.Db index 4152beaa0..0af9167dc 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -1,10 +1,9 @@ #************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. -# EPICS BASE Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found +# EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* #RULES.Db @@ -23,8 +22,8 @@ vpath %.acs $(USR_VPATH) $(GENERIC_SRC_DIRS) $(COMMON_DIR) ##################################################### dbdflags # dbExpand -INSTALL_DBDFLAGS += -I $(INSTALL_LOCATION)/dbd -INSTALL_DBFLAGS += -I $(INSTALL_LOCATION)/db +INSTALL_DBDFLAGS += -I $(INSTALL_DBD) +INSTALL_DBFLAGS += -I $(INSTALL_DB) DBDFLAGS = $(USR_DBDFLAGS) -I . -I .. $(INSTALL_DBDFLAGS) $(RELEASE_DBDFLAGS) DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) -I. -I.. $(INSTALL_DBFLAGS) $(RELEASE_DBFLAGS) diff --git a/src/ca/client/Makefile b/src/ca/client/Makefile index 912295ac5..e36a24736 100644 --- a/src/ca/client/Makefile +++ b/src/ca/client/Makefile @@ -85,7 +85,7 @@ PROD_LIBS = ca Com PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 PROD_HOST += caRepeater catime acctst caConnTest casw caEventRate -OBJS_IOC_vxWorks += catime acctst caConnTest casw caEventRate +OBJS_IOC += catime acctst caConnTest casw caEventRate acctstRegister caRepeater_SRCS = caRepeater.cpp catime_SRCS = catimeMain.c catime.c acctst_SRCS = acctstMain.c acctst.c diff --git a/src/ca/client/acctst.c b/src/ca/client/acctst.c index 9c4840996..f2d102868 100644 --- a/src/ca/client/acctst.c +++ b/src/ca/client/acctst.c @@ -10,6 +10,10 @@ /* * CA regression test + * Authors: + * Jeff Hill + * Murali Shankar - initial versions of verifyMultithreadSubscr + * */ /* @@ -26,6 +30,8 @@ */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" +#include "epicsMutex.h" +#include "epicsEvent.h" #include "epicsTime.h" #include "dbDefs.h" #include "envDefs.h" @@ -2719,6 +2725,186 @@ void fdRegCB ( void * parg, int fd, int opened ) } } +typedef struct { + char m_chanName[100u]; + struct ca_client_context * m_pCtx; + chid m_chan; + epicsMutexId m_mutex; + epicsEventId m_testCompleteEvent; + epicsEventId m_threadExitEvent; + size_t m_nUpdatesReceived; + size_t m_nUpdatesRequired; + int m_testInitiated; + int m_testComplete; + unsigned m_interestLevel; +} MultiThreadSubscrTest; + +static void testMultithreadSubscrSubscrCallback + ( struct event_handler_args eha ) +{ + const epicsEventId firstUpdateEvent = ( epicsEventId ) eha.usr; + epicsEventSignal ( firstUpdateEvent ); +} + +static void testMultithreadSubscrCreateSubscr ( void * pParm ) +{ + static unsigned nElem = 0; + int testComplete = FALSE; + evid id; + epicsEventId firstUpdateEvent; + epicsEventWaitStatus eventWaitStatus; + MultiThreadSubscrTest * const pMultiThreadSubscrTest = + ( MultiThreadSubscrTest * ) pParm; + + /* this is required for the ca_flush below to work correctly */ + int status = ca_attach_context ( pMultiThreadSubscrTest->m_pCtx ); + verify ( status == ECA_NORMAL ); + firstUpdateEvent = epicsEventMustCreate ( epicsEventEmpty ); + verify ( firstUpdateEvent ); + status = ca_create_subscription ( + DBR_TIME_LONG, + nElem, + pMultiThreadSubscrTest->m_chan, + DBE_VALUE, + testMultithreadSubscrSubscrCallback, + firstUpdateEvent, + & id ); + verify ( status == ECA_NORMAL ); + status = ca_flush_io (); + verify ( status == ECA_NORMAL ); + /* wait for first update */ + eventWaitStatus = epicsEventWaitWithTimeout ( + firstUpdateEvent, 60.0 * 10 ); + verify ( eventWaitStatus == epicsEventWaitOK ); + epicsEventDestroy ( firstUpdateEvent ); + status = ca_clear_subscription ( id ); + verify ( status == ECA_NORMAL ); + epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex ); + pMultiThreadSubscrTest->m_nUpdatesReceived++; + testComplete = ( pMultiThreadSubscrTest->m_nUpdatesReceived == + pMultiThreadSubscrTest->m_nUpdatesRequired ); + pMultiThreadSubscrTest->m_testComplete = testComplete; + epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex ); + if ( testComplete ) { + epicsEventSignal ( pMultiThreadSubscrTest->m_testCompleteEvent ); + } +} + +void testMultithreadSubscrConnHandler ( struct connection_handler_args args ) +{ + MultiThreadSubscrTest * const pMultiThreadSubscrTest = + ( MultiThreadSubscrTest * ) ca_puser ( args.chid ); + epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex ); + if ( !pMultiThreadSubscrTest->m_testInitiated && + args.op == CA_OP_CONN_UP ) { + int i; + pMultiThreadSubscrTest->m_testInitiated = TRUE; + for ( i = 0; i < pMultiThreadSubscrTest->m_nUpdatesRequired; i++ ) { + char threadname[64]; + epicsThreadId threadId; + sprintf(threadname, "testSubscr%06u", i); + threadId = epicsThreadCreate ( threadname, + epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + testMultithreadSubscrCreateSubscr, + pMultiThreadSubscrTest ); + verify ( threadId ); + } + } + epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex ); +} + +void testMultithreadSubscr ( void * pParm ) +{ + MultiThreadSubscrTest * const pMultiThreadSubscrTest = + ( MultiThreadSubscrTest * ) pParm; + int status; + unsigned i; + + status = ca_context_create ( ca_enable_preemptive_callback ); + verify ( status == ECA_NORMAL ); + pMultiThreadSubscrTest->m_pCtx = ca_current_context (); + verify ( pMultiThreadSubscrTest->m_pCtx ); + status = ca_create_channel ( + pMultiThreadSubscrTest->m_chanName, + testMultithreadSubscrConnHandler, + pMultiThreadSubscrTest, + CA_PRIORITY_MIN, + & pMultiThreadSubscrTest->m_chan ); + verify ( status == ECA_NORMAL ); + + showProgressBegin ( "verifyMultithreadSubscr", + pMultiThreadSubscrTest->m_interestLevel ); + i = 0; + while ( TRUE ) { + int success = FALSE; + epicsEventWaitStatus eventWaitStatus; + epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex ); + success = pMultiThreadSubscrTest->m_testComplete; + epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex ); + if ( success ) { + break; + } + eventWaitStatus = epicsEventWaitWithTimeout ( + pMultiThreadSubscrTest->m_testCompleteEvent, 0.1 ); + verify ( eventWaitStatus == epicsEventWaitOK || + eventWaitStatus == epicsEventWaitTimeout ); + if ( i++ % 100 == 0u ) + showProgress ( pMultiThreadSubscrTest->m_interestLevel ); + verify ( i < 1000 ); + } + showProgressEnd ( pMultiThreadSubscrTest->m_interestLevel ); + + status = ca_clear_channel ( pMultiThreadSubscrTest->m_chan ); + verify ( status == ECA_NORMAL ); + ca_context_destroy (); + epicsEventSignal ( pMultiThreadSubscrTest->m_threadExitEvent ); +} + +/* + * test installation of subscriptions similar to usage paterns + * employed by modern versions of the sequencer + */ +void verifyMultithreadSubscr ( const char * pName, unsigned interestLevel ) +{ + static unsigned nSubscr = 3000; + epicsThreadId threadId; + MultiThreadSubscrTest * const pMultiThreadSubscrTest = + (MultiThreadSubscrTest*) calloc ( 1, + sizeof ( MultiThreadSubscrTest ) ); + verify ( pMultiThreadSubscrTest); + pMultiThreadSubscrTest->m_mutex = epicsMutexMustCreate (); + verify ( pMultiThreadSubscrTest->m_mutex ); + pMultiThreadSubscrTest->m_testCompleteEvent = + epicsEventMustCreate ( epicsEventEmpty ); + verify ( pMultiThreadSubscrTest->m_testCompleteEvent ); + pMultiThreadSubscrTest->m_threadExitEvent = + epicsEventMustCreate ( epicsEventEmpty ); + verify ( pMultiThreadSubscrTest->m_threadExitEvent ); + strncpy ( pMultiThreadSubscrTest->m_chanName, pName, + sizeof ( pMultiThreadSubscrTest->m_chanName ) ); + pMultiThreadSubscrTest->m_chanName + [ sizeof ( pMultiThreadSubscrTest->m_chanName ) - 1u ] = '\0'; + pMultiThreadSubscrTest->m_nUpdatesRequired = nSubscr; + pMultiThreadSubscrTest->m_interestLevel = interestLevel; + threadId = epicsThreadCreate ( + "testMultithreadSubscr", + epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + testMultithreadSubscr, pMultiThreadSubscrTest ); + verify ( threadId ); + { + epicsEventWaitStatus eventWaitStatus; + eventWaitStatus = epicsEventWaitWithTimeout ( + pMultiThreadSubscrTest->m_threadExitEvent, 1000.0 ); + verify ( eventWaitStatus == epicsEventWaitOK ); + } + epicsEventDestroy ( pMultiThreadSubscrTest->m_testCompleteEvent ); + epicsEventDestroy ( pMultiThreadSubscrTest->m_threadExitEvent ); + epicsMutexDestroy ( pMultiThreadSubscrTest->m_mutex ); + free ( pMultiThreadSubscrTest ); +} + void fdManagerVerify ( const char * pName, unsigned interestLevel ) { int status; @@ -2965,17 +3151,22 @@ void verifyContextRundownFlush ( const char * pName, unsigned interestLevel ) SEVCHK ( status, "context create failed" ); status = ca_create_channel ( pName, 0, 0, 0, & chan ); - SEVCHK ( status, NULL ); - - status = ca_pend_io( timeoutToPendIO ); - SEVCHK ( status, "channel connect failed" ); - - status = ca_put ( DBR_DOUBLE, chan, & stim ); - SEVCHK ( status, "channel put failed" ); - - status = ca_clear_channel ( chan ); - SEVCHK ( status, NULL ); - + /* + * currently in-memory channels cant be used with this test + * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! + */ + if ( status != ECA_UNAVAILINSERV ) { + SEVCHK ( status, NULL ); + + status = ca_pend_io( timeoutToPendIO ); + SEVCHK ( status, "channel connect failed" ); + + status = ca_put ( DBR_DOUBLE, chan, & stim ); + SEVCHK ( status, "channel put failed" ); + + status = ca_clear_channel ( chan ); + SEVCHK ( status, NULL ); + } ca_context_destroy (); } @@ -2985,24 +3176,28 @@ void verifyContextRundownFlush ( const char * pName, unsigned interestLevel ) dbr_double_t resp; status = ca_context_create ( ca_disable_preemptive_callback ); SEVCHK ( status, "context create failed" ); - + status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, NULL ); - - status = ca_pend_io( timeoutToPendIO ); - SEVCHK ( status, "channel connect failed" ); - - status = ca_get ( DBR_DOUBLE, chan, & resp ); - SEVCHK ( status, "channel get failed" ); - - status = ca_pend_io ( timeoutToPendIO ); - SEVCHK ( status, "get, pend io failed" ); - - verify ( stim == resp ); - - status = ca_clear_channel ( chan ); - SEVCHK ( status, NULL ); - + /* + * currently in-memory channels cant be used with this test + * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! + */ + if ( status != ECA_UNAVAILINSERV ) { + status = ca_pend_io( timeoutToPendIO ); + SEVCHK ( status, "channel connect failed" ); + + status = ca_get ( DBR_DOUBLE, chan, & resp ); + SEVCHK ( status, "channel get failed" ); + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "get, pend io failed" ); + + verify ( stim == resp ); + + status = ca_clear_channel ( chan ); + SEVCHK ( status, NULL ); + } ca_context_destroy (); } @@ -3028,6 +3223,13 @@ void verifyContextRundownChanStillExist ( for ( i = 0; i < NELEMENTS ( chan ); i++ ) { status = ca_create_channel ( pName, 0, 0, 0, & chan[i] ); + /* + * currently in-memory channels cant be used with this test + * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! + */ + if ( status == ECA_UNAVAILINSERV ) { + break; + } SEVCHK ( status, NULL ); } @@ -3123,6 +3325,7 @@ int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, verifyHighThroughputReadCallback ( chan, interestLevel ); verifyHighThroughputWriteCallback ( chan, interestLevel ); verifyBadString ( chan, interestLevel ); + verifyMultithreadSubscr ( pName, interestLevel ); if ( select != ca_enable_preemptive_callback ) { fdManagerVerify ( pName, interestLevel ); } diff --git a/src/ca/client/acctstRegister.cpp b/src/ca/client/acctstRegister.cpp new file mode 100644 index 000000000..e75ed7fe3 --- /dev/null +++ b/src/ca/client/acctstRegister.cpp @@ -0,0 +1,69 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * CA client library diagnostics IOC shell registration + * Authors: + * Jeff Hill + */ + +#include +#include "caDiagnostics.h" + +/* Information needed by iocsh */ +static const iocshArg acctstArg0 = { "channel name", iocshArgString }; +static const iocshArg acctstArg1 = { "interest level", iocshArgInt }; +static const iocshArg acctstArg2 = { "channel count", iocshArgInt }; +static const iocshArg acctstArg3 = { "repetition count", iocshArgInt }; +static const iocshArg acctstArg4 = { "preemptive callback select", iocshArgInt }; + +static const iocshArg *acctstArgs[] = +{ + &acctstArg0, + &acctstArg1, + &acctstArg2, + &acctstArg3, + &acctstArg4 +}; +static const iocshFuncDef acctstFuncDef = {"acctst", 5, acctstArgs}; + + +/* Wrapper called by iocsh, selects the argument types that print needs */ +static void acctstCallFunc(const iocshArgBuf *args) { + if ( args[1].ival < 0 ) { + printf ( "negative interest level not allowed\n" ); + return; + } + if ( args[2].ival < 0 ) { + printf ( "negative channel count not allowed\n" ); + return; + } + if ( args[3].ival < 0 ) { + printf ( "negative repetition count not allowed\n" ); + return; + } + acctst ( + args[0].sval, /* channel name */ + ( unsigned ) args[1].ival, /* interest level */ + ( unsigned ) args[2].ival, /* channel count */ + ( unsigned ) args[3].ival, /* repetition count */ + ( ca_preemptive_callback_select ) args[4].ival ); /* preemptive callback select */ +} + +struct AutoInit { + AutoInit (); +}; + +AutoInit :: AutoInit () +{ + iocshRegister ( &acctstFuncDef, acctstCallFunc ); +} + +AutoInit autoInit; + diff --git a/src/ca/client/cadef.h b/src/ca/client/cadef.h index 7095512e3..e62dd7249 100644 --- a/src/ca/client/cadef.h +++ b/src/ca/client/cadef.h @@ -185,7 +185,8 @@ epicsShareFunc enum channel_state epicsShareAPI ca_state (chid chan); epicsShareFunc int epicsShareAPI ca_task_initialize (void); enum ca_preemptive_callback_select { ca_disable_preemptive_callback, ca_enable_preemptive_callback }; -epicsShareFunc int epicsShareAPI ca_context_create (enum ca_preemptive_callback_select select); +epicsShareFunc int epicsShareAPI + ca_context_create (enum ca_preemptive_callback_select select); epicsShareFunc void epicsShareAPI ca_detach_context (); /************************************************************************/ diff --git a/src/ca/client/tools/caget.c b/src/ca/client/tools/caget.c index ac742fc73..a2b0e08b0 100644 --- a/src/ca/client/tools/caget.c +++ b/src/ca/client/tools/caget.c @@ -126,7 +126,6 @@ static void event_handler (evargs args) ppv->dbrType = args.type; ppv->value = calloc(1, dbr_size_n(args.type, args.count)); memcpy(ppv->value, args.dbr, dbr_size_n(args.type, args.count)); - ppv->onceConnected = 1; ppv->nElems = args.count; nRead++; } @@ -159,12 +158,13 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, int n, result; for (n = 0; n < nPvs; n++) { + unsigned long nElems; /* Set up pvs structure */ /* -------------------- */ /* Get natural type and array count */ - pvs[n].nElems = ca_element_count(pvs[n].chid); + nElems = ca_element_count(pvs[n].chid); pvs[n].dbfType = ca_field_type(pvs[n].chid); pvs[n].dbrType = dbrType; @@ -183,10 +183,6 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, pvs[n].dbrType = DBR_TIME_STRING; } } - /* Adjust array count */ - if (reqElems > pvs[n].nElems) - reqElems = pvs[n].nElems; - pvs[n].reqElems = reqElems; /* Issue CA request */ /* ---------------- */ @@ -196,21 +192,24 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, nConn++; pvs[n].onceConnected = 1; if (request == callback) - { /* Event handler will allocate value */ + { + /* Event handler will allocate value and set nElems */ + pvs[n].reqElems = reqElems > nElems ? nElems : reqElems; result = ca_array_get_callback(pvs[n].dbrType, pvs[n].reqElems, pvs[n].chid, event_handler, (void*)&pvs[n]); } else { - /* Allocate value structure */ + /* We allocate value structure and set nElems */ + pvs[n].nElems = reqElems && reqElems < nElems ? reqElems : nElems; pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].nElems)); - if(!pvs[n].value) { - fprintf(stderr,"Allocation failed\n"); + if (!pvs[n].value) { + fprintf(stderr,"Memory allocation failed\n"); return 1; } result = ca_array_get(pvs[n].dbrType, - pvs[n].reqElems, + pvs[n].nElems, pvs[n].chid, pvs[n].value); } @@ -252,9 +251,6 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, /* -------------- */ for (n = 0; n < nPvs; n++) { - /* Truncate the data printed to what was requested. */ - if (pvs[n].reqElems != 0 && pvs[n].nElems > pvs[n].reqElems) - pvs[n].nElems = pvs[n].reqElems; switch (format) { case plain: /* Emulate old caget behaviour */ @@ -377,7 +373,7 @@ static void complainIfNotPlainAndSet (OutputT *current, const OutputT requested) int main (int argc, char *argv[]) { - int n = 0; + int n; int result; /* CA result */ OutputT format = plain; /* User specified format */ RequestT request = get; /* User specified request type */ @@ -389,7 +385,7 @@ int main (int argc, char *argv[]) int digits = 0; /* getopt() no. of float digits */ int nPvs; /* Number of PVs */ - pv* pvs = 0; /* Array of PV structures */ + pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ @@ -529,7 +525,7 @@ int main (int argc, char *argv[]) result = ca_context_create(ca_disable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " - "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ diff --git a/src/ca/client/tools/cainfo.c b/src/ca/client/tools/cainfo.c index a287f6984..924c34b10 100644 --- a/src/ca/client/tools/cainfo.c +++ b/src/ca/client/tools/cainfo.c @@ -127,13 +127,13 @@ int cainfo (pv *pvs, int nPvs) int main (int argc, char *argv[]) { - int n = 0; + int n; int result; /* CA result */ int opt; /* getopt() current option */ int nPvs; /* Number of PVs */ - pv* pvs = 0; /* Array of PV structures */ + pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ @@ -195,7 +195,7 @@ int main (int argc, char *argv[]) result = ca_context_create(ca_disable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " - "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ diff --git a/src/ca/client/tools/camonitor.c b/src/ca/client/tools/camonitor.c index 617ee7622..cda00989f 100644 --- a/src/ca/client/tools/camonitor.c +++ b/src/ca/client/tools/camonitor.c @@ -92,7 +92,7 @@ void usage (void) * Function: event_handler * * Description: CA event_handler for request type callback - * Allocates the dbr structure and copies the data + * Prints the event data * * Arg(s) In: args - event handler args (see CA manual) * @@ -107,10 +107,12 @@ static void event_handler (evargs args) { pv->dbrType = args.type; pv->nElems = args.count; - memcpy(pv->value, args.dbr, dbr_size_n(args.type, args.count)); + pv->value = (void *) args.dbr; /* casting away const */ print_time_val_sts(pv, reqElems); fflush(stdout); + + pv->value = NULL; } } @@ -129,51 +131,39 @@ static void connection_handler ( struct connection_handler_args args ) { pv *ppv = ( pv * ) ca_puser ( args.chid ); if ( args.op == CA_OP_CONN_UP ) { + nConn++; + if (!ppv->onceConnected) { + ppv->onceConnected = 1; /* Set up pv structure */ /* ------------------- */ /* Get natural type and array count */ - ppv->nElems = ca_element_count(ppv->chid); - ppv->dbfType = ca_field_type(ppv->chid); + ppv->dbfType = ca_field_type(ppv->chid); + ppv->dbrType = dbf_type_to_DBR_TIME(ppv->dbfType); /* Use native type */ + if (dbr_type_is_ENUM(ppv->dbrType)) /* Enums honour -n option */ + { + if (enumAsNr) ppv->dbrType = DBR_TIME_INT; + else ppv->dbrType = DBR_TIME_STRING; + } + else if (floatAsString && + (dbr_type_is_FLOAT(ppv->dbrType) || dbr_type_is_DOUBLE(ppv->dbrType))) + { + ppv->dbrType = DBR_TIME_STRING; + } + /* Set request count */ + ppv->nElems = ca_element_count(ppv->chid); + ppv->reqElems = reqElems > ppv->nElems ? ppv->nElems : reqElems; - /* Set up value structures */ - ppv->dbrType = dbf_type_to_DBR_TIME(ppv->dbfType); /* Use native type */ - if (dbr_type_is_ENUM(ppv->dbrType)) /* Enums honour -n option */ - { - if (enumAsNr) ppv->dbrType = DBR_TIME_INT; - else ppv->dbrType = DBR_TIME_STRING; - } - - else if (floatAsString && - (dbr_type_is_FLOAT(ppv->dbrType) || dbr_type_is_DOUBLE(ppv->dbrType))) - { - ppv->dbrType = DBR_TIME_STRING; - } - /* Adjust array count */ - if (reqElems > ppv->nElems) - reqElems = ppv->nElems; - ppv->reqElems = reqElems; - - ppv->onceConnected = 1; - nConn++; /* Issue CA request */ /* ---------------- */ - /* install monitor once with first connect */ - if ( ! ppv->value ) { - /* Allocate value structure */ - ppv->value = calloc(1, dbr_size_n(ppv->dbrType, ppv->nElems)); - if ( ppv->value ) { - ppv->status = ca_create_subscription(ppv->dbrType, + /* install monitor once with first connect */ + ppv->status = ca_create_subscription(ppv->dbrType, ppv->reqElems, ppv->chid, eventMask, event_handler, (void*)ppv, NULL); - if ( ppv->status != ECA_NORMAL ) { - free ( ppv->value ); - } - } } } else if ( args.op == CA_OP_CONN_DOWN ) { @@ -203,7 +193,7 @@ static void connection_handler ( struct connection_handler_args args ) int main (int argc, char *argv[]) { int returncode = 0; - int n = 0; + int n; int result; /* CA result */ IntFormatT outType; /* Output type */ @@ -211,7 +201,7 @@ int main (int argc, char *argv[]) int digits = 0; /* getopt() no. of float digits */ int nPvs; /* Number of PVs */ - pv* pvs = 0; /* Array of PV structures */ + pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ @@ -356,7 +346,7 @@ int main (int argc, char *argv[]) result = ca_context_create(ca_disable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " - "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ diff --git a/src/ca/client/tools/caput.c b/src/ca/client/tools/caput.c index 9aaba38dc..62815b104 100644 --- a/src/ca/client/tools/caput.c +++ b/src/ca/client/tools/caput.c @@ -251,7 +251,7 @@ int caget (pv *pvs, int nPvs, OutputT format, int main (int argc, char *argv[]) { - int n = 0; + int n; int i; int result; /* CA result */ OutputT format = plain; /* User specified format */ @@ -273,7 +273,7 @@ int main (int argc, char *argv[]) struct dbr_gr_enum bufGrEnum; int nPvs; /* Number of PVs */ - pv* pvs = 0; /* Array of PV structures */ + pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */ @@ -372,7 +372,7 @@ int main (int argc, char *argv[]) result = ca_context_create(ca_enable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " - "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 33b6432d6..76f4f1a2c 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -203,35 +203,44 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) } if ( level > 1 ) { - unsigned nEntriesFree = ringSpace ( pevent->ev_que ); + unsigned nEntriesFree; + const void * taskId; + LOCKEVQUE(pevent->ev_que); + nEntriesFree = ringSpace ( pevent->ev_que ); + taskId = ( void * ) pevent->ev_que->evUser->taskid; + UNLOCKEVQUE(pevent->ev_que); if ( nEntriesFree == 0u ) { printf ( ", thread=%p, queue full", - (void *) pevent->ev_que->evUser->taskid ); + (void *) taskId ); } else if ( nEntriesFree == EVENTQUESIZE ) { printf ( ", thread=%p, queue empty", - (void *) pevent->ev_que->evUser->taskid ); + (void *) taskId ); } else { printf ( ", thread=%p, unused entries=%u", - (void *) pevent->ev_que->evUser->taskid, nEntriesFree ); + (void *) taskId, nEntriesFree ); } } if ( level > 2 ) { + unsigned nDuplicates; + unsigned nCanceled; if ( pevent->nreplace ) { printf (", discarded by replacement=%ld", pevent->nreplace); } if ( ! pevent->valque ) { printf (", queueing disabled" ); } - if ( pevent->ev_que->nDuplicates ) { - printf (", duplicate count =%u\n", - pevent->ev_que->nDuplicates ); + LOCKEVQUE(pevent->ev_que); + nDuplicates = pevent->ev_que->nDuplicates; + nCanceled = pevent->ev_que->nCanceled; + UNLOCKEVQUE(pevent->ev_que); + if ( nDuplicates ) { + printf (", duplicate count =%u\n", nDuplicates ); } - if ( pevent->ev_que->nCanceled ) { - printf (", canceled count =%u\n", - pevent->ev_que->nCanceled ); + if ( nCanceled ) { + printf (", canceled count =%u\n", nCanceled ); } } @@ -264,7 +273,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) */ dbEventCtx epicsShareAPI db_init_events (void) { - struct event_user *evUser; + struct event_user * evUser; if (!dbevEventUserFreeList) { freeListInitPvt(&dbevEventUserFreeList, @@ -326,7 +335,7 @@ dbEventCtx epicsShareAPI db_init_events (void) */ void epicsShareAPI db_close_events (dbEventCtx ctx) { - struct event_user *evUser = (struct event_user *) ctx; + struct event_user * const evUser = (struct event_user *) ctx; /* * Exit not forced on event blocks for now - this is left to channel @@ -336,13 +345,32 @@ void epicsShareAPI db_close_events (dbEventCtx ctx) * NOTE: not deleting events before calling this routine could be * hazardous to the system's health. */ - + epicsMutexMustLock ( evUser->lock ); evUser->pendexit = TRUE; - + epicsMutexUnlock ( evUser->lock ); /* notify the waiting task */ epicsEventSignal(evUser->ppendsem); } +/* + * create_ev_que() + */ +static struct event_que * create_ev_que ( struct event_user * const evUser ) +{ + struct event_que * const ev_que = (struct event_que *) + freeListCalloc ( dbevEventQueueFreeList ); + if ( ! ev_que ) { + return NULL; + } + ev_que->writelock = epicsMutexCreate(); + if ( ! ev_que->writelock ) { + freeListFree ( dbevEventQueueFreeList, ev_que ); + return NULL; + } + ev_que->evUser = evUser; + return ev_que; +} + /* * DB_ADD_EVENT() */ @@ -350,13 +378,9 @@ dbEventSubscription epicsShareAPI db_add_event ( dbEventCtx ctx, struct dbAddr *paddr, EVENTFUNC *user_sub, void *user_arg, unsigned select) { - struct event_user *evUser = (struct event_user *) ctx; - struct dbCommon *precord; - struct event_que *ev_que; - struct event_que *tmp_que; - struct evSubscrip *pevent; - - precord = paddr->precord; + struct event_user * const evUser = (struct event_user *) ctx; + struct event_que * ev_que; + struct evSubscrip * pevent; /* * Don't add events which will not be triggered @@ -365,38 +389,42 @@ dbEventSubscription epicsShareAPI db_add_event ( return NULL; } - pevent = freeListCalloc (dbevEventBlockFreeList); - if (!pevent) { + pevent = freeListCalloc ( dbevEventBlockFreeList ); + if ( ! pevent ) { return NULL; } /* find an event que block with enough quota */ /* otherwise add a new one to the list */ - ev_que = &evUser->firstque; - while (TRUE) { - if (ev_que->quota + ev_que->nCanceled < EVENTQUESIZE - EVENTENTRIES) { + epicsMutexMustLock ( evUser->lock ); + ev_que = & evUser->firstque; + while ( TRUE ) { + int success = 0; + LOCKEVQUE ( ev_que ); + success = ( ev_que->quota + ev_que->nCanceled < + EVENTQUESIZE - EVENTENTRIES ); + if ( success ) { + ev_que->quota += EVENTENTRIES; + } + UNLOCKEVQUE ( ev_que ); + if ( success ) { break; } - if (!ev_que->nextque) { - tmp_que = (struct event_que *) - freeListCalloc(dbevEventQueueFreeList); - if (!tmp_que) { - freeListFree (dbevEventBlockFreeList, pevent); - return NULL; + if ( ! ev_que->nextque ) { + ev_que->nextque = create_ev_que ( evUser ); + if ( ! ev_que->nextque ) { + ev_que = NULL; + break; } - tmp_que->evUser = evUser; - tmp_que->writelock = epicsMutexCreate(); - if (!tmp_que->writelock) { - freeListFree (dbevEventBlockFreeList, pevent); - freeListFree (dbevEventQueueFreeList, tmp_que); - return NULL; - } - ev_que->nextque = tmp_que; - ev_que = tmp_que; - break; } ev_que = ev_que->nextque; } + epicsMutexUnlock ( evUser->lock ); + + if ( ! ev_que ) { + freeListFree ( dbevEventBlockFreeList, pevent ); + return NULL; + } pevent->npend = 0ul; pevent->nreplace = 0ul; @@ -409,10 +437,6 @@ dbEventSubscription epicsShareAPI db_add_event ( pevent->enabled = FALSE; pevent->ev_que = ev_que; - LOCKEVQUE(ev_que); - ev_que->quota += EVENTENTRIES; - UNLOCKEVQUE(ev_que); - /* * Simple types values queued up for reliable interprocess * communication (for other types they get whatever happens to be @@ -434,10 +458,9 @@ dbEventSubscription epicsShareAPI db_add_event ( */ void epicsShareAPI db_event_enable (dbEventSubscription es) { - struct evSubscrip *pevent = (struct evSubscrip *) es; - struct dbCommon *precord; - - precord = (struct dbCommon *) pevent->paddr->precord; + struct evSubscrip * const pevent = (struct evSubscrip *) es; + struct dbCommon * const precord = + (struct dbCommon *) pevent->paddr->precord; LOCKREC(precord); if ( ! pevent->enabled ) { @@ -452,10 +475,9 @@ void epicsShareAPI db_event_enable (dbEventSubscription es) */ void epicsShareAPI db_event_disable (dbEventSubscription es) { - struct evSubscrip *pevent = (struct evSubscrip *) es; - struct dbCommon *precord; - - precord = (struct dbCommon *) pevent->paddr->precord; + struct evSubscrip * const pevent = (struct evSubscrip *) es; + struct dbCommon * const precord = + (struct dbCommon *) pevent->paddr->precord; LOCKREC(precord); if ( pevent->enabled ) { @@ -472,7 +494,7 @@ void epicsShareAPI db_event_disable (dbEventSubscription es) static void event_remove ( struct event_que *ev_que, unsigned short index, struct evSubscrip *placeHolder ) { - struct evSubscrip *pEvent = ev_que->evque[index]; + struct evSubscrip * const pEvent = ev_que->evque[index]; ev_que->evque[index] = placeHolder; if ( pEvent->npend == 1u ) { @@ -495,12 +517,9 @@ static void event_remove ( struct event_que *ev_que, */ void epicsShareAPI db_cancel_event (dbEventSubscription es) { - struct evSubscrip * pevent = ( struct evSubscrip * ) es; - struct dbCommon * precord; + struct evSubscrip * const pevent = ( struct evSubscrip * ) es; unsigned short getix; - precord = ( struct dbCommon * ) pevent->paddr->precord; - db_event_disable ( es ); /* @@ -564,12 +583,12 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es) */ void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx) { - struct event_user *evUser = (struct event_user *) ctx; + struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); while ( evUser->extraLaborBusy ) { epicsMutexUnlock ( evUser->lock ); - epicsThreadSleep(1.0); + epicsThreadSleep(0.1); epicsMutexMustLock ( evUser->lock ); } epicsMutexUnlock ( evUser->lock ); @@ -585,7 +604,7 @@ void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx) int epicsShareAPI db_add_extra_labor_event ( dbEventCtx ctx, EXTRALABORFUNC *func, void *arg) { - struct event_user *evUser = (struct event_user *) ctx; + struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); evUser->extralabor_sub = func; @@ -600,7 +619,7 @@ int epicsShareAPI db_add_extra_labor_event ( */ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) { - struct event_user *evUser = (struct event_user *) ctx; + struct event_user * const evUser = (struct event_user *) ctx; int doit; epicsMutexMustLock ( evUser->lock ); @@ -627,12 +646,10 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) */ static void db_post_single_event_private (struct evSubscrip *event) { - struct event_que *ev_que; - db_field_log *pLog; - int firstEventFlag; - unsigned rngSpace; - - ev_que = event->ev_que; + struct event_que * const ev_que = event->ev_que; + db_field_log * pLog; + int firstEventFlag; + unsigned rngSpace; /* * evUser ring buffer must be locked for the multiple @@ -747,10 +764,10 @@ void *pField, unsigned int caEventMask ) { - struct dbCommon *pdbc = (struct dbCommon *)pRecord; - struct evSubscrip *event; + struct dbCommon * const pdbc = (struct dbCommon *)pRecord; + struct evSubscrip * event; - if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ + if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ LOCKREC(pdbc); @@ -762,8 +779,7 @@ unsigned int caEventMask * changed or pval==NULL and waiting on alarms and alarms changed */ if ( (event->paddr->pfield == (void *)pField || pField==NULL) && - (caEventMask & event->select)) { - + (caEventMask & event->select) ) { db_post_single_event_private (event); } } @@ -778,8 +794,8 @@ unsigned int caEventMask */ void epicsShareAPI db_post_single_event (dbEventSubscription es) { - struct evSubscrip *event = (struct evSubscrip *) es; - struct dbCommon *precord = event->paddr->precord; + struct evSubscrip * const event = (struct evSubscrip *) es; + struct dbCommon * const precord = event->paddr->precord; dbScanLock (precord); db_post_single_event_private (event); @@ -898,8 +914,8 @@ static int event_read ( struct event_que *ev_que ) */ static void event_task (void *pParm) { - struct event_user *evUser = (struct event_user *) pParm; - struct event_que *ev_que; + struct event_user * const evUser = (struct event_user *) pParm; + struct event_que * ev_que; /* init hook */ if (evUser->init_func) { @@ -934,12 +950,14 @@ static void event_task (void *pParm) epicsMutexMustLock ( evUser->lock ); } evUser->extraLaborBusy = FALSE; - epicsMutexUnlock ( evUser->lock ); for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) { + epicsMutexUnlock ( evUser->lock ); event_read (ev_que); + epicsMutexMustLock ( evUser->lock ); } + epicsMutexUnlock ( evUser->lock ); } while( ! evUser->pendexit ); @@ -975,7 +993,7 @@ int epicsShareAPI db_start_events ( dbEventCtx ctx,const char *taskname, void (*init_func)(void *), void *init_func_arg, unsigned osiPriority ) { - struct event_user *evUser = (struct event_user *) ctx; + struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); @@ -995,7 +1013,8 @@ int epicsShareAPI db_start_events ( taskname = EVENT_PEND_NAME; } evUser->taskid = epicsThreadCreate ( - taskname, osiPriority, epicsThreadGetStackSize(epicsThreadStackMedium), + taskname, osiPriority, + epicsThreadGetStackSize(epicsThreadStackMedium), event_task, (void *)evUser); if (!evUser->taskid) { epicsMutexUnlock ( evUser->lock ); @@ -1008,9 +1027,10 @@ int epicsShareAPI db_start_events ( /* * db_event_change_priority() */ -void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority ) +void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, + unsigned epicsPriority ) { - struct event_user * evUser = ( struct event_user * ) ctx; + struct event_user * const evUser = ( struct event_user * ) ctx; epicsThreadSetPriority ( evUser->taskid, epicsPriority ); } @@ -1019,9 +1039,11 @@ void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPrio */ void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx) { - struct event_user *evUser = (struct event_user *) ctx; + struct event_user * const evUser = (struct event_user *) ctx; + epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = TRUE; + epicsMutexUnlock ( evUser->lock ); /* * notify the event handler task */ @@ -1036,9 +1058,11 @@ void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx) */ void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx) { - struct event_user *evUser = (struct event_user *) ctx; + struct event_user * const evUser = (struct event_user *) ctx; + epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = FALSE; + epicsMutexUnlock ( evUser->lock ); /* * notify the event handler task */ diff --git a/src/ioc/db/dbPutNotifyBlocker.cpp b/src/ioc/db/dbPutNotifyBlocker.cpp index 9bddf1948..e91234b67 100644 --- a/src/ioc/db/dbPutNotifyBlocker.cpp +++ b/src/ioc/db/dbPutNotifyBlocker.cpp @@ -74,7 +74,7 @@ void dbPutNotifyBlocker::cancel ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); - if ( this->pn.paddr ) { + if ( this->pNotify ) { epicsGuardRelease < epicsMutex > unguard ( guard ); dbNotifyCancel ( &this->pn ); } @@ -100,26 +100,31 @@ void dbPutNotifyBlocker::expandValueBuf ( extern "C" void putNotifyCompletion ( putNotify *ppn ) { - dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); - { - epicsGuard < epicsMutex > guard ( pBlocker->mutex ); - if ( pBlocker->pNotify ) { - if ( pBlocker->pn.status != putNotifyOK) { - pBlocker->pNotify->exception ( - guard, ECA_PUTFAIL, "put notify unsuccessful", - static_cast (pBlocker->pn.dbrType), - static_cast (pBlocker->pn.nRequest) ); - } - else { - pBlocker->pNotify->completion ( guard ); - } + dbPutNotifyBlocker * const pBlocker = + static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); + epicsGuard < epicsMutex > guard ( pBlocker->mutex ); + cacWriteNotify * const pNtfy = pBlocker->pNotify; + if ( pNtfy ) { + pBlocker->pNotify = 0; + // Its necessary to signal the initiators now before we call + // the user callback. This is less efficent, and potentially + // causes more thread context switching, but its probably + // unavoidable because its possible that the use callback + // might destroy this object. + pBlocker->block.signal (); + if ( pBlocker->pn.status != putNotifyOK ) { + pNtfy->exception ( + guard, ECA_PUTFAIL, "put notify unsuccessful", + static_cast < unsigned > (pBlocker->pn.dbrType), + static_cast < unsigned > (pBlocker->pn.nRequest) ); } else { - errlogPrintf ( "put notify completion with nill pNotify?\n" ); + pNtfy->completion ( guard ); } - pBlocker->pNotify = 0; } - pBlocker->block.signal (); + else { + errlogPrintf ( "put notify completion with nill pNotify?\n" ); + } } void dbPutNotifyBlocker::initiatePutNotify ( diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 2a398b713..9f9c1a385 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -44,6 +44,7 @@ #include "dbLock.h" #include "devSup.h" #include "drvSup.h" +#include "menuConvert.h" #include "menuPini.h" #include "registryRecordType.h" #include "registryDeviceSupport.h" @@ -68,6 +69,7 @@ static enum { } iocState = iocVirgin; /* define forward references*/ +static int checkDatabase(dbBase *pdbbase); static void initDrvSup(void); static void initRecSup(void); static void initDevSup(void); @@ -98,8 +100,8 @@ int iocBuild(void) } errlogPrintf("Starting iocInit\n"); - if (!pdbbase) { - errlogPrintf("iocBuild: Aborting, no database loaded!\n"); + if (checkDatabase(pdbbase)) { + errlogPrintf("iocBuild: Aborting, bad database definition (DBD)!\n"); return -1; } epicsSignalInstallSigHupIgnore(); @@ -203,6 +205,83 @@ int iocPause(void) return 0; } +/* + * Database sanity checks + * + * This is not an attempt to sanity-check the whole .dbd file, only + * two menus normally get modified by users: menuConvert and menuScan. + * + * The menuConvert checks were added to flag problems with IOCs + * converted from 3.13.x, where the SLOPE choice didn't exist. + * + * The menuScan checks make sure the user didn't fiddle too much + * when creating new periodic scan choices. + */ + +static int checkDatabase(dbBase *pdbbase) +{ + const dbMenu *pMenu; + + if (!pdbbase) { + errlogPrintf("checkDatabase: No database definitions loaded.\n"); + return -1; + } + + pMenu = dbFindMenu(pdbbase, "menuConvert"); + if (!pMenu) { + errlogPrintf("checkDatabase: menuConvert not defined.\n"); + return -1; + } + if (pMenu->nChoice <= menuConvertLINEAR) { + errlogPrintf("checkDatabase: menuConvert has too few choices.\n"); + return -1; + } + if (strcmp(pMenu->papChoiceName[menuConvertNO_CONVERSION], + "menuConvertNO_CONVERSION")) { + errlogPrintf("checkDatabase: menuConvertNO_CONVERSION doesn't match.\n"); + return -1; + } + if (strcmp(pMenu->papChoiceName[menuConvertSLOPE], "menuConvertSLOPE")) { + errlogPrintf("checkDatabase: menuConvertSLOPE doesn't match.\n"); + return -1; + } + if (strcmp(pMenu->papChoiceName[menuConvertLINEAR], "menuConvertLINEAR")) { + errlogPrintf("checkDatabase: menuConvertLINEAR doesn't match.\n"); + return -1; + } + + pMenu = dbFindMenu(pdbbase, "menuScan"); + if (!pMenu) { + errlogPrintf("checkDatabase: menuScan not defined.\n"); + return -1; + } + if (pMenu->nChoice <= menuScanI_O_Intr) { + errlogPrintf("checkDatabase: menuScan has too few choices.\n"); + return -1; + } + if (strcmp(pMenu->papChoiceName[menuScanPassive], + "menuScanPassive")) { + errlogPrintf("checkDatabase: menuScanPassive doesn't match.\n"); + return -1; + } + if (strcmp(pMenu->papChoiceName[menuScanEvent], + "menuScanEvent")) { + errlogPrintf("checkDatabase: menuScanEvent doesn't match.\n"); + return -1; + } + if (strcmp(pMenu->papChoiceName[menuScanI_O_Intr], + "menuScanI_O_Intr")) { + errlogPrintf("checkDatabase: menuScanI_O_Intr doesn't match.\n"); + return -1; + } + if (pMenu->nChoice <= SCAN_1ST_PERIODIC) { + errlogPrintf("checkDatabase: menuScan has no periodic choices.\n"); + return -1; + } + + return 0; +} + static void initDrvSup(void) /* Locate all driver support entry tables */ { diff --git a/src/libCom/osi/os/posix/osdThread.c b/src/libCom/osi/os/posix/osdThread.c index 501826715..f7e47b100 100644 --- a/src/libCom/osi/os/posix/osdThread.c +++ b/src/libCom/osi/os/posix/osdThread.c @@ -232,19 +232,30 @@ int min = sched_get_priority_min(prm->policy); int max = sched_get_priority_max(prm->policy); int low, try; - prm->min_pri = -1; - prm->max_pri = -1; - if ( -1 == min || -1 == max ) { - /* something is very wrong... */ + /* something is very wrong; maintain old behavior + * (warning message if sched_get_priority_xxx() fails + * and use default policy's sched_priority [even if + * that is likely to cause epicsThreadCreate to fail + * because that priority is not suitable for SCHED_FIFO]). + */ + prm->min_pri = prm->max_pri = -1; return 0; } + if ( try_pri(min, prm->policy) ) { /* cannot create thread at minimum priority; * probably no permission to use SCHED_FIFO - * at all. + * at all. However, we still must return + * a priority range accepted by the SCHED_FIFO + * policy. Otherwise, epicsThreadCreate() cannot + * detect the unsufficient permission (EPERM) + * and fall back to a non-RT thread (because + * pthread_attr_setschedparam would fail with + * EINVAL due to the bad priority). */ + prm->min_pri = prm->max_pri = min; return 0; } diff --git a/src/tools/installEpics.pl b/src/tools/installEpics.pl index 248e662f7..3340bd5c3 100644 --- a/src/tools/installEpics.pl +++ b/src/tools/installEpics.pl @@ -1,11 +1,10 @@ #!/usr/bin/perl #************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. -# EPICS BASE Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found +# EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # @@ -62,18 +61,13 @@ foreach $source ( @files ) $basename=$source; $basename=~s'.*[/\\]''; $target = "$install_dir/$basename"; + $temp = "$target.$$"; - # The Win32 filesystem seems to be 'slow', - # i.e. $target may look like 'up to date' - # unless you wait an hour. - # -> skip this test on WIN32 ? - #if (-f $target and $^O ne "MSWin32") if (-f $target) { - if (-M $target < -M $source and + if (-M $target < -M $source and -C $target < -C $source) { - print "$target is up to date\n"; next; } else @@ -84,8 +78,9 @@ foreach $source ( @files ) } } - # print "Installing $source into $install_dir\n"; - copy ($source, $target) or die "Copy failed"; + # Using copy + rename fixes problems with parallel builds: + copy ($source, $temp) or die "Copy failed: $!\n"; + rename ($temp, $target) or die "Rename failed: $!\n"; # chmod 0555 DOES work on WIN32, but: # Another chmod 0777 to make it write- and deletable