diff --git a/src/db/dbCAC.h b/src/db/dbCAC.h index 798b52390..5c900e5e7 100644 --- a/src/db/dbCAC.h +++ b/src/db/dbCAC.h @@ -126,20 +126,34 @@ private: dbContextPrivateListOfIO & operator = ( const dbContextPrivateListOfIO & ); }; -// allow only one thread at a time to use the cache, but do not hold -// lock when calling the callback +class dbContextReadNotifyCacheAllocator { +public: + dbContextReadNotifyCacheAllocator (); + ~dbContextReadNotifyCacheAllocator (); + char * alloc ( unsigned long size ); + void free ( char * pFree ); + void show ( unsigned level ) const; +private: + struct cacheElem_t { + struct cacheElem_t * pNext; + }; + unsigned long _readNotifyCacheSize; + cacheElem_t * _pReadNotifyCache; + void reclaimAllCacheEntries (); + dbContextReadNotifyCacheAllocator ( const dbContextReadNotifyCacheAllocator & ); + dbContextReadNotifyCacheAllocator & operator = ( const dbContextReadNotifyCacheAllocator & ); +}; + class dbContextReadNotifyCache { public: dbContextReadNotifyCache ( epicsMutex & ); - ~dbContextReadNotifyCache (); void callReadNotify ( epicsGuard < epicsMutex > &, struct dbAddr & addr, unsigned type, unsigned long count, cacReadNotify & notify ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: - unsigned long readNotifyCacheSize; - epicsMutex & mutex; - char * pReadNotifyCache; + dbContextReadNotifyCacheAllocator _allocator; + epicsMutex & _mutex; dbContextReadNotifyCache ( const dbContextReadNotifyCache & ); dbContextReadNotifyCache & operator = ( const dbContextReadNotifyCache & ); }; diff --git a/src/db/dbContextReadNotifyCache.cpp b/src/db/dbContextReadNotifyCache.cpp index baade3bd1..ed056523b 100644 --- a/src/db/dbContextReadNotifyCache.cpp +++ b/src/db/dbContextReadNotifyCache.cpp @@ -14,7 +14,6 @@ */ #include "epicsMutex.h" -#include "tsFreeList.h" #include "cadef.h" // this can be eliminated when the callbacks use the new interface #include "db_access.h" // should be eliminated here in the future @@ -25,35 +24,30 @@ #include "dbCAC.h" dbContextReadNotifyCache::dbContextReadNotifyCache ( epicsMutex & mutexIn ) : - readNotifyCacheSize ( 0 ), mutex ( mutexIn ), pReadNotifyCache ( 0 ) + _mutex ( mutexIn ) { } -dbContextReadNotifyCache::~dbContextReadNotifyCache () -{ - delete this->pReadNotifyCache; -} +class privateAutoDestroyPtr { +public: + privateAutoDestroyPtr ( + dbContextReadNotifyCacheAllocator & allocator, unsigned long size ) : + _allocator ( allocator ), _p ( allocator.alloc ( size ) ) {} + ~privateAutoDestroyPtr () { _allocator.free ( _p ); } + char * get () const { return _p; } +private: + dbContextReadNotifyCacheAllocator & _allocator; + char * _p; + privateAutoDestroyPtr ( const privateAutoDestroyPtr & ); + privateAutoDestroyPtr & operator = ( const privateAutoDestroyPtr & ); +}; -void dbContextReadNotifyCache::show ( - epicsGuard < epicsMutex > & guard, unsigned level ) const -{ - guard.assertIdenticalMutex ( this->mutex ); - - if ( level > 0 ) { - printf ( "\tget call back cache location %p, and its size %lu\n", - static_cast ( this->pReadNotifyCache ), - this->readNotifyCacheSize ); - } -} - -// extra effort taken here to not hold the lock when caslling the callback +// extra effort taken here to not hold the lock when calling the callback void dbContextReadNotifyCache::callReadNotify ( epicsGuard < epicsMutex > & guard, struct dbAddr & addr, unsigned type, unsigned long count, cacReadNotify & notify ) { - guard.assertIdenticalMutex ( this->mutex ); - - unsigned long size = dbr_size_n ( type, count ); + guard.assertIdenticalMutex ( _mutex ); if ( type > INT_MAX ) { notify.exception ( guard, ECA_BADTYPE, @@ -62,38 +56,108 @@ void dbContextReadNotifyCache::callReadNotify ( return; } - if ( count > static_cast < unsigned > ( INT_MAX ) || - count > static_cast < unsigned > ( addr.no_elements ) ) { + if ( addr.no_elements < 0 ) { + notify.exception ( guard, ECA_BADCOUNT, + "database has negetive element count", + type, count); + return; + } + + if ( count > static_cast < unsigned > ( addr.no_elements ) ) { notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; } - if ( this->readNotifyCacheSize < size) { - char * pTmp = new char [size]; - if ( ! pTmp ) { - notify.exception ( guard, ECA_ALLOCMEM, - "unable to allocate callback cache", - type, count ); - return; - } - delete [] this->pReadNotifyCache; - this->pReadNotifyCache = pTmp; - this->readNotifyCacheSize = size; - } + unsigned long size = dbr_size_n ( type, count ); + privateAutoDestroyPtr ptr ( _allocator, size ); int status; { epicsGuardRelease < epicsMutex > unguard ( guard ); status = db_get_field ( &addr, static_cast ( type ), - this->pReadNotifyCache, static_cast ( count ), 0 ); + ptr.get (), static_cast ( count ), 0 ); } if ( status ) { notify.exception ( guard, ECA_GETFAIL, "db_get_field() completed unsuccessfuly", - type, count); + type, count ); } else { - notify.completion ( guard, type, count, this->pReadNotifyCache ); + notify.completion ( + guard, type, count, ptr.get () ); } } + +void dbContextReadNotifyCache::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( _mutex ); + + printf ( "dbContextReadNotifyCache\n" ); + if ( level > 0 ) { + this->_allocator.show ( level - 1 ); + } +} + +dbContextReadNotifyCacheAllocator::dbContextReadNotifyCacheAllocator () : + _readNotifyCacheSize ( 0 ), _pReadNotifyCache ( 0 ) +{ +} + +dbContextReadNotifyCacheAllocator::~dbContextReadNotifyCacheAllocator () +{ + this->reclaimAllCacheEntries (); +} + +void dbContextReadNotifyCacheAllocator::reclaimAllCacheEntries () +{ + + while ( _pReadNotifyCache ) { + cacheElem_t * pNext = _pReadNotifyCache->pNext; + delete [] _pReadNotifyCache; + _pReadNotifyCache = pNext; + } +} + +char * dbContextReadNotifyCacheAllocator::alloc ( unsigned long size ) +{ + if ( size > _readNotifyCacheSize ) { + this->reclaimAllCacheEntries (); + _readNotifyCacheSize = size; + } + + cacheElem_t * pAlloc = _pReadNotifyCache; + if ( pAlloc ) { + _pReadNotifyCache = pAlloc->pNext; + } + else { + size_t nElem = _readNotifyCacheSize / sizeof ( cacheElem_t ); + pAlloc = new cacheElem_t [ nElem + 1 ]; + } + return reinterpret_cast < char * > ( pAlloc ); +} + +void dbContextReadNotifyCacheAllocator::free ( char * pFree ) +{ + cacheElem_t * pAlloc = reinterpret_cast < cacheElem_t * > ( pFree ); + pAlloc->pNext = _pReadNotifyCache; + _pReadNotifyCache = pAlloc; +} + +void dbContextReadNotifyCacheAllocator::show ( unsigned level ) const +{ + printf ( "dbContextReadNotifyCacheAlocator\n" ); + if ( level > 0 ) { + size_t count =0; + cacheElem_t * pNext = _pReadNotifyCache; + while ( pNext ) { + pNext = _pReadNotifyCache->pNext; + count++; + } + printf ( "\tcount %u and size %lu\n", + count, _readNotifyCacheSize ); + } +} + +