diff --git a/src/ca/Makefile b/src/ca/Makefile index db4186d16..45eb8ee48 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -92,7 +92,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/acctst.c b/src/ca/acctst.c index 9c4840996..f2d102868 100644 --- a/src/ca/acctst.c +++ b/src/ca/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/acctstRegister.cpp b/src/ca/acctstRegister.cpp new file mode 100644 index 000000000..291ef9566 --- /dev/null +++ b/src/ca/acctstRegister.cpp @@ -0,0 +1,76 @@ + +/*************************************************************************\ +* 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 Versions 3.13.7 +* and higher are 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" + +namespace { + +/* 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; + +} // end of anonymous namespace + + diff --git a/src/ca/cadef.h b/src/ca/cadef.h index 7095512e3..e62dd7249 100644 --- a/src/ca/cadef.h +++ b/src/ca/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/db/dbEvent.c b/src/db/dbEvent.c index 33b6432d6..76f4f1a2c 100644 --- a/src/db/dbEvent.c +++ b/src/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/db/dbPutNotifyBlocker.cpp b/src/db/dbPutNotifyBlocker.cpp index 9bddf1948..e91234b67 100644 --- a/src/db/dbPutNotifyBlocker.cpp +++ b/src/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 (