diff --git a/src/db/dbCAC.h b/src/db/dbCAC.h index e9c8910b2..405956d3b 100644 --- a/src/db/dbCAC.h +++ b/src/db/dbCAC.h @@ -18,9 +18,13 @@ * 1) This interface is preliminary and will change in the future */ + #include "dbNotify.h" #include "dbEvent.h" #include "dbAddr.h" +#include "dbLock.h" +#include "dbCommon.h" +#include "db_convert.h" extern "C" void putNotifyCompletion ( putNotify *ppn ); @@ -29,19 +33,18 @@ class dbPutNotifyBlocker; class dbPutNotifyIO : public cacNotifyIO { public: - dbPutNotifyIO ( cacNotify ¬ify, dbPutNotifyBlocker &blockerIn ); + dbPutNotifyIO ( cacNotify ¬ify, dbPutNotifyBlocker &blocker ); int initiate ( struct dbAddr &addr, unsigned type, unsigned long count, const void *pValue); + void completion (); void destroy (); static void * operator new ( size_t size ); static void operator delete ( void *pCadaver, size_t size ); private: putNotify pn; dbPutNotifyBlocker &blocker; - bool ioComplete; static tsFreeList < dbPutNotifyIO > freeList; ~dbPutNotifyIO (); // must allocate out of pool - friend void putNotifyCompletion ( putNotify *ppn ); }; extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, @@ -61,8 +64,6 @@ private: unsigned long count; static tsFreeList < dbSubscriptionIO > freeList; ~dbSubscriptionIO (); // must be allocated from pool - friend dbPutNotifyIO::dbPutNotifyIO ( cacNotify ¬ify, dbPutNotifyBlocker &blockerIn ); - friend dbPutNotifyIO::~dbPutNotifyIO ( ); friend void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, int eventsRemaining, struct db_field_log *pfl ); }; @@ -73,6 +74,9 @@ class dbPutNotifyBlocker { public: dbPutNotifyBlocker ( dbChannelIO &chanIn ); void destroy (); + int initiatePutNotify (cacNotify ¬ify, struct dbAddr &addr, + unsigned type, unsigned long count, const void *pValue); + void putNotifyDestroyNotify (); static void * operator new ( size_t size ); static void operator delete ( void *pCadaver, size_t size ); private: @@ -82,9 +86,9 @@ private: static tsFreeList < dbPutNotifyBlocker > freeList; ~dbPutNotifyBlocker (); // must allocate out of pool - - friend dbPutNotifyIO::dbPutNotifyIO ( cacNotify ¬ify, dbPutNotifyBlocker &blockerIn ); - friend dbPutNotifyIO::~dbPutNotifyIO (); + void lock () const; + void unlock () const; + friend void putNotifyCompletion ( putNotify *ppn ); }; class dbChannelIO : public cacLocalChannelIO { @@ -119,10 +123,14 @@ private: short nativeType () const; unsigned long nativeElementCount () const; + void lock () const; + void unlock () const; + friend dbSubscriptionIO::dbSubscriptionIO ( dbChannelIO &chanIO, cacNotify &, unsigned type, unsigned long count ); friend dbSubscriptionIO::~dbSubscriptionIO (); - friend dbPutNotifyBlocker::dbPutNotifyBlocker ( dbChannelIO &chanIn ); - friend dbPutNotifyBlocker::~dbPutNotifyBlocker (); + + friend void dbPutNotifyBlocker::lock () const; + friend void dbPutNotifyBlocker::unlock () const; }; class dbServiceIO : public cacServiceIO { @@ -139,3 +147,4 @@ private: unsigned long eventCallbackCacheSize; osiMutex mutex; }; + diff --git a/src/db/dbChannelIO.cpp b/src/db/dbChannelIO.cpp index c8ba3d4aa..431daaf2b 100644 --- a/src/db/dbChannelIO.cpp +++ b/src/db/dbChannelIO.cpp @@ -17,24 +17,22 @@ #include "limits.h" -#include "tsFreeList.h" -#include "osiMutex.h" #include "cadef.h" #include "cacIO.h" +#include "tsFreeList.h" +#include "osiMutex.h" #define epicsExportSharedSymbols #include "db_access_routines.h" #include "dbCAC.h" -#include "dbLock.h" -#include "dbCommon.h" - -extern "C" unsigned short dbDBRnewToDBRold[DBR_ENUM+1]; +#include "dbChannelIOIL.h" +#include "dbNotifyBlockerIL.h" tsFreeList < dbChannelIO > dbChannelIO::freeList; dbChannelIO::dbChannelIO ( cacChannel &chan, const dbAddr &addrIn, dbServiceIO &serviceIO ) : cacLocalChannelIO ( chan ), serviceIO ( serviceIO ), pGetCallbackCache ( 0 ), - getCallbackCacheSize ( 0ul ), pBlocker (0), addr ( addrIn ) + pBlocker (0), getCallbackCacheSize ( 0ul ), addr ( addrIn ) { chan.attachIO ( *this ); this->connectNotify (); @@ -42,17 +40,21 @@ dbChannelIO::dbChannelIO ( cacChannel &chan, const dbAddr &addrIn, dbServiceIO & dbChannelIO::~dbChannelIO () { + // this must go in the derived class's destructor because + // this calls virtual functions in the cacChannelIO base + this->ioReleaseNotify (); + + this->lock (); + /* * remove any subscriptions attached to this channel */ - this->lock (); tsDLIterBD iter = this->eventq.first (); while ( iter != iter.eol () ) { tsDLIterBD next = iter.itemAfter (); iter->destroy (); iter = next; } - this->unlock (); if ( this->pBlocker ) { this->pBlocker->destroy (); @@ -61,26 +63,8 @@ dbChannelIO::~dbChannelIO () if ( this->pGetCallbackCache ) { delete [] this->pGetCallbackCache; } -} -void dbChannelIO::destroy () -{ - delete this; -} - -void * dbChannelIO::operator new ( size_t size ) -{ - return dbChannelIO::freeList.allocate ( size ); -} - -void dbChannelIO::operator delete ( void *pCadaver, size_t size ) -{ - dbChannelIO::freeList.release ( pCadaver, size ); -} - -const char *dbChannelIO::pName () const -{ - return addr.precord->name; + this->unlock (); } int dbChannelIO::read ( unsigned type, unsigned long count, void *pValue ) @@ -111,7 +95,7 @@ int dbChannelIO::read ( unsigned type, unsigned long count, cacNotify ¬ify ) return ECA_BADCOUNT; } - dbScanLock ( this->addr.precord ); + this->lock (); if ( this->getCallbackCacheSize < size) { if ( this->pGetCallbackCache ) { delete [] this->pGetCallbackCache; @@ -119,7 +103,7 @@ int dbChannelIO::read ( unsigned type, unsigned long count, cacNotify ¬ify ) this->pGetCallbackCache = new char [size]; if ( ! this->pGetCallbackCache ) { this->getCallbackCacheSize = 0ul; - dbScanUnlock ( this->addr.precord ); + this->unlock (); return ECA_ALLOCMEM; } this->getCallbackCacheSize = size; @@ -132,8 +116,8 @@ int dbChannelIO::read ( unsigned type, unsigned long count, cacNotify ¬ify ) else { notify.completionNotify ( type, count, this->pGetCallbackCache ); } + this->unlock (); notify.destroy (); - dbScanUnlock ( this->addr.precord ); return ECA_NORMAL; } @@ -155,31 +139,27 @@ int dbChannelIO::write ( unsigned type, unsigned long count, const void *pValue int dbChannelIO::write ( unsigned type, unsigned long count, const void *pValue, cacNotify ¬ify ) { - dbPutNotifyIO *pIO; - if ( count > LONG_MAX ) { return ECA_BADCOUNT; } - this->lock (); if ( ! this->pBlocker ) { - this->pBlocker = new dbPutNotifyBlocker ( *this ); + this->lock (); if ( ! this->pBlocker ) { - this->unlock (); - return ECA_ALLOCMEM; + this->pBlocker = new dbPutNotifyBlocker ( *this ); + if ( ! this->pBlocker ) { + this->unlock (); + return ECA_ALLOCMEM; + } } - } - this->unlock (); - - pIO = new dbPutNotifyIO ( notify, *this->pBlocker ); - if ( ! pIO ) { - return ECA_ALLOCMEM; + this->unlock (); } - int status = pIO->initiate ( this->addr, type, count, pValue ); - if ( status != ECA_NORMAL ) { - pIO->destroy (); - } + // must release the lock here so that this can block + // for put notify completion without monopolizing the lock + int status = this->pBlocker->initiatePutNotify ( notify, + this->addr, type, count, pValue ); + return status; } @@ -198,28 +178,4 @@ int dbChannelIO::subscribe ( unsigned type, unsigned long count, return status; } -short dbChannelIO::nativeType () const -{ - return dbDBRnewToDBRold[this->addr.field_type]; -} -unsigned long dbChannelIO::nativeElementCount () const -{ - if ( this->addr.no_elements >= 0u ) { - return static_cast < unsigned long > ( this->addr.no_elements ); - } - else { - return 0u; - } -} - -void dbChannelIO::subscriptionUpdate ( unsigned type, unsigned long count, - const struct db_field_log *pfl, cacNotifyIO ¬ify ) -{ - this->serviceIO.subscriptionUpdate ( this->addr, type, count, pfl, notify ); -} - -dbEventSubscription dbChannelIO::subscribe ( dbSubscriptionIO &subscr, unsigned mask ) -{ - return this->serviceIO.subscribe ( this->addr, subscr, mask ); -} diff --git a/src/db/dbChannelIOIL.h b/src/db/dbChannelIOIL.h new file mode 100644 index 000000000..b1641c129 --- /dev/null +++ b/src/db/dbChannelIOIL.h @@ -0,0 +1,74 @@ + + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + + +inline unsigned long dbChannelIO::nativeElementCount () const +{ + if ( this->addr.no_elements >= 0u ) { + return static_cast < unsigned long > ( this->addr.no_elements ); + } + else { + return 0u; + } +} + +inline void dbChannelIO::destroy () +{ + delete this; +} + +inline void * dbChannelIO::operator new ( size_t size ) +{ + return dbChannelIO::freeList.allocate ( size ); +} + +inline void dbChannelIO::operator delete ( void *pCadaver, size_t size ) +{ + dbChannelIO::freeList.release ( pCadaver, size ); +} + +inline const char *dbChannelIO::pName () const +{ + return addr.precord->name; +} + +inline short dbChannelIO::nativeType () const +{ + return dbDBRnewToDBRold[this->addr.field_type]; +} + +inline void dbChannelIO::subscriptionUpdate ( unsigned type, unsigned long count, + const struct db_field_log *pfl, cacNotifyIO ¬ify ) +{ + this->serviceIO.subscriptionUpdate ( this->addr, type, count, pfl, notify ); +} + +inline void dbChannelIO::lock () const +{ + dbScanLock ( this->addr.precord ); +} + +inline void dbChannelIO::unlock () const +{ + dbScanUnlock ( this->addr.precord ); +} + +inline dbEventSubscription dbChannelIO::subscribe ( dbSubscriptionIO &subscr, unsigned mask ) +{ + return this->serviceIO.subscribe ( this->addr, subscr, mask ); +} diff --git a/src/db/dbNotifyBlockerIL.h b/src/db/dbNotifyBlockerIL.h new file mode 100644 index 000000000..af8a82d4c --- /dev/null +++ b/src/db/dbNotifyBlockerIL.h @@ -0,0 +1,41 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +inline void dbPutNotifyBlocker::lock () const +{ + this->chan.lock (); +} + +inline void dbPutNotifyBlocker::unlock () const +{ + this->chan.unlock (); +} + +inline void dbPutNotifyBlocker::destroy () +{ + delete this; +} + +inline void * dbPutNotifyBlocker::operator new ( size_t size ) +{ + return dbPutNotifyBlocker::freeList.allocate ( size ); +} + +inline void dbPutNotifyBlocker::operator delete ( void *pCadaver, size_t size ) +{ + dbPutNotifyBlocker::freeList.release ( pCadaver, size ); +} diff --git a/src/db/dbPutNotifyBlocker.cpp b/src/db/dbPutNotifyBlocker.cpp index d4c782eb3..6c05ceb1f 100644 --- a/src/db/dbPutNotifyBlocker.cpp +++ b/src/db/dbPutNotifyBlocker.cpp @@ -20,6 +20,7 @@ #include "string.h" #include "osiMutex.h" +#include "osiTime.h" #include "tsFreeList.h" #include "errMdef.h" @@ -28,6 +29,9 @@ #define epicsExportSharedSymbols #include "dbCAC.h" +#include "dbChannelIOIL.h" +#include "dbNotifyBlockerIL.h" +#include "dbPutNotifyIOIL.h" #define S_db_Blocked (M_dbAccess|39) #define S_db_Pending (M_dbAccess|37) @@ -35,28 +39,70 @@ tsFreeList dbPutNotifyBlocker::freeList; dbPutNotifyBlocker::dbPutNotifyBlocker ( dbChannelIO &chanIn ) : - chan ( chanIn ), pPN (0) + pPN (0), chan ( chanIn ) { } dbPutNotifyBlocker::~dbPutNotifyBlocker () { + this->lock (); if ( this->pPN ) { this->pPN->destroy (); } + this->unlock (); } -void dbPutNotifyBlocker::destroy () +void dbPutNotifyBlocker::putNotifyDestroyNotify () { - delete this; + this->lock (); + this->pPN = 0; + this->unlock (); } -void * dbPutNotifyBlocker::operator new ( size_t size ) + +int dbPutNotifyBlocker::initiatePutNotify ( cacNotify ¬ify, + struct dbAddr &addr, unsigned type, unsigned long count, + const void *pValue ) { - return dbPutNotifyBlocker::freeList.allocate ( size ); + dbPutNotifyIO *pIO = new dbPutNotifyIO ( notify, *this ); + if ( ! pIO ) { + return ECA_ALLOCMEM; + } + + // wait for current put notify to complete + this->lock (); + if ( this->pPN ) { + osiTime begin = osiTime::getCurrent (); + do { + this->unlock (); + this->block.wait ( 1.0 ); + if ( osiTime::getCurrent () - begin > 30.0 ) { + pIO->destroy (); + return ECA_PUTCBINPROG; + } + this->lock (); + } while ( this->pPN ); + } + this->pPN = pIO; + int status = this->pPN->initiate ( addr, type, count, pValue ); + if ( status != ECA_NORMAL ) { + this->pPN->destroy (); + this->pPN = 0; + } + this->unlock (); + return status; } -void dbPutNotifyBlocker::operator delete ( void *pCadaver, size_t size ) +extern "C" void putNotifyCompletion ( putNotify *ppn ) { - dbPutNotifyBlocker::freeList.release ( pCadaver, size ); + dbPutNotifyBlocker *pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); + pBlocker->lock (); + pBlocker->pPN->completion (); + pBlocker->pPN->destroy (); + pBlocker->pPN = 0; + pBlocker->unlock (); + pBlocker->block.signal (); } + + + diff --git a/src/db/dbPutNotifyIO.cpp b/src/db/dbPutNotifyIO.cpp index 4d5626e68..03af3ef84 100644 --- a/src/db/dbPutNotifyIO.cpp +++ b/src/db/dbPutNotifyIO.cpp @@ -28,54 +28,27 @@ #define epicsExportSharedSymbols #include "dbCAC.h" +#include "dbPutNotifyIOIL.h" #define S_db_Blocked (M_dbAccess|39) #define S_db_Pending (M_dbAccess|37) tsFreeList dbPutNotifyIO::freeList; -extern "C" void putNotifyCompletion ( putNotify *ppn ) -{ - dbPutNotifyIO *pNotify = static_cast < dbPutNotifyIO * > ( ppn->usrPvt ); - if ( pNotify->pn.status ) { - if (pNotify->pn.status == S_db_Blocked) { - pNotify->cacNotifyIO::exceptionNotify ( ECA_PUTCBINPROG, "put notify blocked" ); - } - else { - pNotify->cacNotifyIO::exceptionNotify ( ECA_PUTFAIL, "put notify unsuccessful"); - } - } - else { - pNotify->cacNotifyIO::completionNotify (); - } - pNotify->ioComplete = true; - pNotify->destroy (); -} - dbPutNotifyIO::dbPutNotifyIO ( cacNotify ¬ifyIn, dbPutNotifyBlocker &blockerIn ) : - cacNotifyIO ( notifyIn ), blocker ( blockerIn ), ioComplete ( false ) + cacNotifyIO ( notifyIn ), blocker ( blockerIn ) { memset ( &this->pn, '\0', sizeof ( this->pn ) ); this->pn.userCallback = putNotifyCompletion; - this->pn.usrPvt = this; - // wait for current put notify to complete - this->blocker.chan.lock (); - while ( this->blocker.pPN ) { - this->blocker.chan.unlock (); - this->blocker.block.wait ( 1.0 ); - this->blocker.chan.lock (); - } - this->blocker.pPN = this; - this->blocker.chan.unlock (); + this->pn.usrPvt = &blockerIn; } dbPutNotifyIO::~dbPutNotifyIO () { - if ( ! this->ioComplete ) { + if ( this->pn.paddr ) { dbNotifyCancel ( &this->pn ); } - this->blocker.pPN = 0; - this->blocker.block.signal (); + this->blocker.putNotifyDestroyNotify (); } int dbPutNotifyIO::initiate ( struct dbAddr &addr, unsigned type, @@ -94,28 +67,35 @@ int dbPutNotifyIO::initiate ( struct dbAddr &addr, unsigned type, this->pn.paddr = &addr; status = this->pn.dbrType = dbPutNotifyMapType ( &this->pn, static_cast ( type ) ); if (status) { + this->pn.paddr = 0; return ECA_BADTYPE; } status = ::dbPutNotify ( &this->pn ); if ( status && status != S_db_Pending ) { + this->pn.paddr = 0; this->pn.status = status; this->cacNotifyIO::exceptionNotify ( ECA_PUTFAIL, "dbPutNotify() returned failure"); } return ECA_NORMAL; } -void dbPutNotifyIO::destroy () +void dbPutNotifyIO::completion () { - delete this; + if ( ! this->pn.paddr ) { + errlogPrintf ( "completion pn=%p\n", this ); + } + this->pn.paddr = 0; + if ( this->pn.status ) { + if ( this->pn.status == S_db_Blocked ) { + this->cacNotifyIO::exceptionNotify ( ECA_PUTCBINPROG, "put notify blocked" ); + } + else { + this->cacNotifyIO::exceptionNotify ( ECA_PUTFAIL, "put notify unsuccessful"); + } + } + else { + this->cacNotifyIO::completionNotify (); + } } -void * dbPutNotifyIO::operator new ( size_t size ) -{ - return dbPutNotifyIO::freeList.allocate ( size ); -} - -void dbPutNotifyIO::operator delete ( void *pCadaver, size_t size ) -{ - dbPutNotifyIO::freeList.release ( pCadaver, size ); -} diff --git a/src/db/dbPutNotifyIOIL.h b/src/db/dbPutNotifyIOIL.h new file mode 100644 index 000000000..d5560ab93 --- /dev/null +++ b/src/db/dbPutNotifyIOIL.h @@ -0,0 +1,33 @@ + + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +inline void dbPutNotifyIO::destroy () +{ + delete this; +} + +inline void * dbPutNotifyIO::operator new ( size_t size ) +{ + return dbPutNotifyIO::freeList.allocate ( size ); +} + +inline void dbPutNotifyIO::operator delete ( void *pCadaver, size_t size ) +{ + dbPutNotifyIO::freeList.release ( pCadaver, size ); +} + diff --git a/src/db/dbServiceIO.cpp b/src/db/dbServiceIO.cpp index fdbb7be6f..281e79b37 100644 --- a/src/db/dbServiceIO.cpp +++ b/src/db/dbServiceIO.cpp @@ -26,6 +26,7 @@ #define epicsExportSharedSymbols #include "db_access_routines.h" #include "dbCAC.h" +#include "dbChannelIOIL.h" class dbServiceIOLoadTimeInit { public: @@ -115,8 +116,9 @@ extern "C" void cacAttachClientCtx ( void * pPrivate ) dbEventSubscription dbServiceIO::subscribe ( struct dbAddr &addr, dbSubscriptionIO &subscr, unsigned mask ) { static const int slightlyHigherPriority = -1; - int status; caClientCtx clientCtx; + dbEventSubscription es; + int status; status = ca_current_context ( &clientCtx ); if ( status != ECA_NORMAL ) { @@ -140,6 +142,11 @@ dbEventSubscription dbServiceIO::subscribe ( struct dbAddr &addr, dbSubscription } this->mutex.unlock (); - return db_add_event ( this->ctx, &addr, + es = db_add_event ( this->ctx, &addr, dbSubscriptionEventCallback, (void *) &subscr, mask ); + if (es) { + db_post_single_event ( es ); + } + + return es; } diff --git a/src/db/dbSubscriptionIO.cpp b/src/db/dbSubscriptionIO.cpp index 49ad646a1..1225995ed 100644 --- a/src/db/dbSubscriptionIO.cpp +++ b/src/db/dbSubscriptionIO.cpp @@ -25,6 +25,7 @@ #define epicsExportSharedSymbols #include "dbCAC.h" +#include "dbChannelIOIL.h" #include "db_access_routines.h" tsFreeList < dbSubscriptionIO > dbSubscriptionIO::freeList;