fixed a bug where the callback control gaurd was used from a different thread

than the one that created it
This commit is contained in:
Jeff Hill
2007-01-11 21:45:58 +00:00
parent 6ed4f091b2
commit cde78f0b70
12 changed files with 119 additions and 167 deletions

View File

@@ -43,6 +43,8 @@ epicsShareDef epicsThreadPrivateId caClientCallbackThreadId;
static epicsThreadOnceId cacOnce = EPICS_THREAD_ONCE_INIT;
const unsigned ca_client_context :: flushBlockThreshold = 0x58000;
extern "C" void cacExitHandler ( void *)
{
epicsThreadPrivateDelete ( caClientCallbackThreadId );
@@ -65,6 +67,7 @@ cacService * ca_client_context::pDefaultService = 0;
epicsMutex * ca_client_context::pDefaultServiceInstallMutex;
ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
createdByThread ( epicsThreadGetIdSelf () ),
ca_exception_func ( 0 ), ca_exception_arg ( 0 ),
pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ),
pndRecvCnt ( 0u ), ioSeqNo ( 0u ), callbackThreadsPending ( 0u ),
@@ -189,22 +192,6 @@ ca_client_context::~ca_client_context ()
}
}
void ca_client_context::destroyChannel (
epicsGuard < epicsMutex > & cbGuard,
epicsGuard < epicsMutex > & guard,
oldChannelNotify & chan )
{
try {
chan.eliminateExcessiveSendBacklog (
&cbGuard, guard );
}
catch ( cacChannel::notConnected & ) {
// intentionally ignored
}
chan.destructor ( cbGuard, guard );
this->oldChannelNotifyFreeList.release ( & chan );
}
void ca_client_context::destroyGetCopy (
epicsGuard < epicsMutex > & guard, getCopy & gc )
{
@@ -663,7 +650,7 @@ void ca_client_context::callbackProcessingCompleteNotify ()
cacChannel & ca_client_context::createChannel (
epicsGuard < epicsMutex > & guard, const char * pChannelName,
oldChannelNotify & chan, cacChannel::priLev pri )
cacChannelNotify & chan, cacChannel::priLev pri )
{
guard.assertIdenticalMutex ( this->mutex );
return this->pServiceContext->createChannel (
@@ -753,32 +740,41 @@ epicsShareFunc int epicsShareAPI ca_clear_subscription ( evid pMon )
{
oldChannelNotify & chan = pMon->channel ();
ca_client_context & cac = chan.getClientCtx ();
epicsGuard < epicsMutex > * pCBGuard = cac.pCallbackGuard.get();
if ( pCBGuard ) {
cac.clearSubscriptionPrivate ( *pCBGuard, *pMon );
}
else {
epicsGuard < epicsMutex > cbGuard ( cac.cbMutex );
cac.clearSubscriptionPrivate ( cbGuard, *pMon );
}
return ECA_NORMAL;
}
void ca_client_context::clearSubscriptionPrivate (
epicsGuard < epicsMutex > & cbGuard, oldSubscription & subscr )
{
epicsGuard < epicsMutex > guard ( this->mutex );
oldChannelNotify & chan = subscr.channel ();
epicsGuard < epicsMutex > guard ( cac.mutex );
try {
// if this stalls out on a live circuit then an exception
// can be forthcoming which we must ignore as the clear
// request must always be successful
chan.eliminateExcessiveSendBacklog (
& cbGuard, guard );
chan.eliminateExcessiveSendBacklog ( guard );
}
catch ( cacChannel::notConnected & ) {
// intentionally ignored
}
subscr.cancel ( cbGuard, guard );
pMon->cancel ( guard );
return ECA_NORMAL;
}
void ca_client_context :: eliminateExcessiveSendBacklog (
epicsGuard < epicsMutex > & guard, cacChannel & chan )
{
if ( chan.requestMessageBytesPending ( guard ) >
ca_client_context :: flushBlockThreshold ) {
if ( this->pCallbackGuard.get() &&
this->createdByThread == epicsThreadGetIdSelf () ) {
// we need to be very careful about lock hierarchy
// inversion in this situation
epicsGuardRelease < epicsMutex > unguard ( guard );
{
epicsGuardRelease < epicsMutex > cbunguard (
* this->pCallbackGuard.get() );
{
epicsGuard < epicsMutex > nestedGuard ( this->mutex );
chan.flush ( nestedGuard );
}
}
}
else {
chan.flush ( guard );
}
}
}