diff --git a/src/db/dbCAC.h b/src/db/dbCAC.h new file mode 100644 index 000000000..86a1c48e1 --- /dev/null +++ b/src/db/dbCAC.h @@ -0,0 +1,112 @@ + +/* + * $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 + * + * NOTES: + * 1) This interface is preliminary and will change in the future + */ + +#include "dbNotify.h" +#include "dbEvent.h" +#include "dbAddr.h" + +extern "C" void putNotifyCompletion ( putNotify *ppn ); + +class dbPutNotifyIO : public cacNotifyIO { +public: + dbPutNotifyIO ( cacNotify ¬ify ); + int initiate ( struct dbAddr &addr, unsigned type, + unsigned long count, const void *pValue); + void destroy (); + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); +private: + putNotify pn; + bool ioComplete; + static tsFreeList < dbPutNotifyIO > freeList; + ~dbPutNotifyIO (); // must allocate out of pool + friend void putNotifyCompletion ( putNotify *ppn ); +}; + +class dbChannelIO; + +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, + int eventsRemaining, struct db_field_log *pfl ); + +class dbSubscriptionIO : public cacNotifyIO { +public: + dbSubscriptionIO ( dbChannelIO &chanIO, cacNotify &, unsigned type, unsigned long count ); + int dbSubscriptionIO::begin ( struct dbAddr &addr, unsigned mask ); + void destroy (); + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); +private: + dbChannelIO &chan; + dbEventSubscription es; + unsigned type; + unsigned long count; + static tsFreeList < dbSubscriptionIO > freeList; + ~dbSubscriptionIO (); // must be allocated from pool + friend void dbSubscriptionEventCallback ( void *user_arg, struct dbAddr *paddr, + int eventsRemaining, struct db_field_log *pfl ); +}; + +class dbServiceIO; + +class dbChannelIO : public cacChannelIO { +public: + dbChannelIO ( cacChannel &chan, const dbAddr &addr, dbServiceIO &serviceIO ); + void destroy (); + void subscriptionUpdate ( unsigned type, unsigned long count, + const struct db_field_log *pfl, cacNotifyIO ¬ify); + dbEventSubscription subscribe ( dbSubscriptionIO &subscr, unsigned mask ); + + static void * operator new ( size_t size); + static void operator delete ( void *pCadaver, size_t size ); + +private: + dbServiceIO &serviceIO; + char *pGetCallbackCache; + unsigned long getCallbackCacheSize; + dbAddr addr; + + static tsFreeList < dbChannelIO > freeList; + + ~dbChannelIO (); // allocate only from pool + + const char *pName () const; + int read ( unsigned type, unsigned long count, void *pValue ); + int read ( unsigned type, unsigned long count, cacNotify & ); + int write ( unsigned type, unsigned long count, const void *pvalue ); + int write ( unsigned type, unsigned long count, const void *pvalue, cacNotify & ); + int subscribe ( unsigned type, unsigned long count, unsigned mask, cacNotify ¬ify ); + short nativeType () const; + unsigned long nativeElementCount () const; +}; + +class dbServiceIO : public cacServiceIO { +public: + dbServiceIO (); + ~dbServiceIO (); + cacChannelIO *createChannelIO ( cacChannel &chan, const char *pName ); + void subscriptionUpdate ( struct dbAddr &addr, unsigned type, unsigned long count, + const struct db_field_log *pfl, cacNotifyIO ¬ify ); + dbEventSubscription subscribe ( struct dbAddr &addr, dbSubscriptionIO &subscr, unsigned mask ); +private: + dbEventCtx ctx; + char *pEventCallbackCache; + unsigned long eventCallbackCacheSize; + osiMutex mutex; +}; diff --git a/src/db/dbChannelIO.cpp b/src/db/dbChannelIO.cpp new file mode 100644 index 000000000..5cf7a4d5a --- /dev/null +++ b/src/db/dbChannelIO.cpp @@ -0,0 +1,194 @@ + +/* + * $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 + */ + +#include "limits.h" + +#include "tsFreeList.h" +#include "osiMutex.h" +#include "cadef.h" +#include "cacIO.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]; + +tsFreeList < dbChannelIO > dbChannelIO::freeList; + +dbChannelIO::dbChannelIO ( cacChannel &chan, const dbAddr &addrIn, dbServiceIO &serviceIO ) : + cacChannelIO ( chan ), serviceIO ( serviceIO ), addr ( addrIn ), + pGetCallbackCache ( 0 ), getCallbackCacheSize ( 0ul ) +{ + this->connectNotify (); +} + +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; +} + +int dbChannelIO::read ( unsigned type, unsigned long count, void *pValue ) +{ + if ( type > INT_MAX ) { + return ECA_BADCOUNT; + } + if ( count > INT_MAX ) { + return ECA_BADCOUNT; + } + int status = db_get_field ( &this->addr, static_cast ( type ), + pValue, static_cast ( count ), 0); + if ( status ) { + return ECA_GETFAIL; + } + else { + return ECA_NORMAL; + } +} + +int dbChannelIO::read ( unsigned type, unsigned long count, cacNotify ¬ify ) +{ + unsigned long size = dbr_size_n ( type, count ); + if ( type > INT_MAX ) { + return ECA_BADCOUNT; + } + if ( count > INT_MAX ) { + return ECA_BADCOUNT; + } + + dbScanLock ( this->addr.precord ); + if ( this->getCallbackCacheSize < size) { + if ( this->pGetCallbackCache ) { + delete [] this->pGetCallbackCache; + } + this->pGetCallbackCache = new char [size]; + if ( ! this->pGetCallbackCache ) { + this->getCallbackCacheSize = 0ul; + dbScanUnlock ( this->addr.precord ); + return ECA_ALLOCMEM; + } + this->getCallbackCacheSize = size; + } + int status = db_get_field ( &this->addr, static_cast ( type ), + this->pGetCallbackCache, static_cast ( count ), 0); + if ( status ) { + notify.exceptionNotify ( ECA_GETFAIL, "db_get_field () completed unsuccessfuly" ); + } + else { + notify.completionNotify ( type, count, this->pGetCallbackCache ); + } + notify.destroy (); + dbScanUnlock ( this->addr.precord ); + return ECA_NORMAL; +} + +int dbChannelIO::write ( unsigned type, unsigned long count, const void *pValue ) +{ + int status; + if ( count > LONG_MAX ) { + return ECA_BADCOUNT; + } + status = db_put_field ( &this->addr, type, pValue, static_cast (count) ); + if (status) { + return ECA_PUTFAIL; + } + else { + return ECA_NORMAL; + } +} + +int dbChannelIO::write ( unsigned type, unsigned long count, + const void *pValue, cacNotify ¬ify ) +{ + dbPutNotifyIO *pIO; + if ( count > LONG_MAX ) { + return ECA_BADCOUNT; + } + pIO = new dbPutNotifyIO ( notify ); + if ( ! pIO ) { + return ECA_ALLOCMEM; + } + int status = pIO->initiate ( this->addr, type, count, pValue ); + if ( status != ECA_NORMAL ) { + pIO->destroy (); + } + return status; +} + +int dbChannelIO::subscribe ( unsigned type, unsigned long count, + unsigned mask, cacNotify ¬ify ) +{ + dbSubscriptionIO *pIO = new dbSubscriptionIO ( *this, notify, type, count ); + if ( ! pIO ) { + return ECA_ALLOCMEM; + } + int status = pIO->begin ( this->addr, mask ); + if ( status != ECA_NORMAL ) { + pIO->destroy (); + } + 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/dbPutNotifyIO.cpp b/src/db/dbPutNotifyIO.cpp new file mode 100644 index 000000000..21e3d8e51 --- /dev/null +++ b/src/db/dbPutNotifyIO.cpp @@ -0,0 +1,109 @@ + +/* + * $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 + */ + +#include "limits.h" +#include "string.h" + +#include "osiMutex.h" +#include "tsFreeList.h" +#include "errMdef.h" + +#include "cadef.h" +#include "cacIO.h" + +#define epicsExportSharedSymbols +#include "dbCAC.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 ) : + cacNotifyIO (notifyIn), ioComplete (false) +{ + memset (&this->pn, '\0', sizeof (this->pn)); + this->pn.userCallback = putNotifyCompletion; + this->pn.usrPvt = this; +} + +dbPutNotifyIO::~dbPutNotifyIO () +{ + if ( ! this->ioComplete ) { + dbNotifyCancel ( &this->pn ); + } +} + +int dbPutNotifyIO::initiate ( struct dbAddr &addr, unsigned type, + unsigned long count, const void *pValue) +{ + int status; + + if ( count > LONG_MAX ) { + return ECA_BADCOUNT; + } + if ( type > SHRT_MAX ) { + return ECA_BADTYPE; + } + this->pn.pbuffer = const_cast ( pValue ); + this->pn.nRequest = static_cast ( count ); + this->pn.paddr = &addr; + status = this->pn.dbrType = dbPutNotifyMapType ( &this->pn, static_cast ( type ) ); + if (status) { + return ECA_BADTYPE; + } + + status = ::dbPutNotify ( &this->pn ); + if ( status && status != S_db_Pending ) { + this->pn.status = status; + putNotifyCallback (); + } + return ECA_NORMAL; +} + +void dbPutNotifyIO::destroy () +{ + delete this; +} + +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/dbServiceIO.cpp b/src/db/dbServiceIO.cpp new file mode 100644 index 000000000..b1c1087ce --- /dev/null +++ b/src/db/dbServiceIO.cpp @@ -0,0 +1,141 @@ + +/* + * $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 + */ + +#include "limits.h" + +#include "osiMutex.h" +#include "tsFreeList.h" + +#include "cadef.h" +#include "cacIO.h" + +#define epicsExportSharedSymbols +#include "db_access_routines.h" +#include "dbCAC.h" + +class loadTimeInit { +public: + loadTimeInit (); +private: + dbServiceIO dbio; +}; + +static loadTimeInit lti; + +loadTimeInit::loadTimeInit () +{ + cacGlobalServiceList.registerService ( this->dbio ); +} + +dbServiceIO::dbServiceIO () : + ctx (0), pEventCallbackCache (0), eventCallbackCacheSize (0ul) +{ +} + +dbServiceIO::~dbServiceIO () +{ + if ( this->pEventCallbackCache ) { + delete [] this->pEventCallbackCache; + } + if (this->ctx) { + db_close_events (this->ctx); + } +} + +cacChannelIO *dbServiceIO::createChannelIO ( cacChannel &chan, const char *pName ) +{ + struct dbAddr addr; + + int status = db_name_to_addr ( pName, &addr ); + if (status) { + return 0; + } + else { + return new dbChannelIO ( chan, addr, *this ); + } +} + +void dbServiceIO::subscriptionUpdate ( struct dbAddr &addr, unsigned type, unsigned long count, + const struct db_field_log *pfl, cacNotifyIO ¬ify ) +{ + unsigned long size = dbr_size_n ( type, count ); + + this->mutex.lock (); + if ( this->eventCallbackCacheSize < size) { + if ( this->pEventCallbackCache ) { + delete [] this->pEventCallbackCache; + } + this->pEventCallbackCache = new char [size]; + if ( ! this->pEventCallbackCache ) { + this->eventCallbackCacheSize = 0ul; + this->mutex.unlock (); + notify.exceptionNotify ( ECA_ALLOCMEM, "unable to allocate callback cache" ); + return; + } + this->eventCallbackCacheSize = size; + } + void *pvfl = (void *) pfl; + int status = db_get_field ( &addr, static_cast ( type ), + this->pEventCallbackCache, static_cast ( count ), pvfl ); + if ( status ) { + notify.exceptionNotify ( ECA_GETFAIL, "subscription update db_get_field () completed unsuccessfuly" ); + } + else { + notify.completionNotify ( type, count, this->pEventCallbackCache ); + } + this->mutex.unlock (); +} + +extern "C" void cacAttachClientCtx ( void * pPrivate ) +{ + int status; + caClientCtx clientCtx = pPrivate; + status = ca_attach_context ( clientCtx ); + assert ( status == ECA_NORMAL ); +} + +dbEventSubscription dbServiceIO::subscribe ( struct dbAddr &addr, dbSubscriptionIO &subscr, unsigned mask ) +{ + static const int slightlyHigherPriority = -1; + int status; + caClientCtx clientCtx; + + status = ca_current_context ( &clientCtx ); + if ( status != ECA_NORMAL ) { + return 0; + } + + this->mutex.lock (); + if ( ! this->ctx ) { + this->ctx = db_init_events (); + if ( ! this->ctx ) { + this->mutex.unlock (); + return 0; + } + status = db_start_events ( this->ctx, "CAC event", + cacAttachClientCtx, clientCtx, slightlyHigherPriority ); + if ( status ) { + db_close_events (this->ctx); + this->ctx = 0; + return 0; + } + } + this->mutex.unlock (); + + return db_add_event ( this->ctx, &addr, + dbSubscriptionEventCallback, (void *) &subscr, mask ); +} diff --git a/src/db/dbSubscriptionIO.cpp b/src/db/dbSubscriptionIO.cpp new file mode 100644 index 000000000..d8ca38453 --- /dev/null +++ b/src/db/dbSubscriptionIO.cpp @@ -0,0 +1,84 @@ + +/* + * $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 + */ + +#include "limits.h" + +#include "osiMutex.h" +#include "tsFreeList.h" + +#include "cadef.h" +#include "cacIO.h" + +#define epicsExportSharedSymbols +#include "dbCAC.h" +#include "db_access_routines.h" + +tsFreeList < dbSubscriptionIO > dbSubscriptionIO::freeList; + +dbSubscriptionIO::dbSubscriptionIO ( dbChannelIO &chanIO, + cacNotify ¬ifyIn, unsigned typeIn, unsigned long countIn ) : + cacNotifyIO ( notifyIn ), chan ( chanIO ), es ( 0 ), + type ( typeIn ), count ( countIn ) +{ +} + +dbSubscriptionIO::~dbSubscriptionIO () +{ + if ( this->es ) { + db_cancel_event ( this->es ); + } +} + +void dbSubscriptionIO::destroy () +{ + delete this; +} + +void * dbSubscriptionIO::operator new ( size_t size ) +{ + return dbSubscriptionIO::freeList.allocate ( size ); +} + +void dbSubscriptionIO::operator delete ( void *pCadaver, size_t size ) +{ + dbSubscriptionIO::freeList.release ( pCadaver, size ); +} + +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, + int eventsRemaining, struct db_field_log *pfl ) +{ + dbSubscriptionIO *pIO = static_cast ( pPrivate ); + pIO->chan.subscriptionUpdate ( pIO->type, pIO->count, pfl, *pIO); +} + +int dbSubscriptionIO::begin ( struct dbAddr &addr, unsigned mask ) +{ + if ( this->type > INT_MAX ) { + return ECA_BADCOUNT; + } + if ( this->count > INT_MAX ) { + return ECA_BADCOUNT; + } + this->es = this->chan.subscribe ( *this, mask ); + if ( this->es ) { + return ECA_NORMAL; + } + else { + return ECA_ALLOCMEM; + } +} +