diff --git a/src/cas/generic/casAsyncIOI.cc b/src/cas/generic/casAsyncIOI.cc index 9a792da19..d74b86174 100644 --- a/src/cas/generic/casAsyncIOI.cc +++ b/src/cas/generic/casAsyncIOI.cc @@ -25,22 +25,19 @@ // // casAsyncIOI::casAsyncIOI() // -casAsyncIOI::casAsyncIOI ( casCoreClient & clientIn ) : - client ( clientIn ), inTheEventQueue ( false ), - posted ( false ), ioComplete ( false ), serverDelete ( false ) +casAsyncIOI::casAsyncIOI ( const casCtx & ctx ) : + client ( *ctx.getClient() ), inTheEventQueue ( false ), + posted ( false ), ioComplete ( false ), + serverDelete ( false ), duplicate ( false ) { // // catch situation where they create more than one // async IO object per request // - if (client.asyncIOFlag) { - errMessage(S_cas_badParameter, - "- duplicate async IO creation"); - this->duplicate = TRUE; - } - else { - client.asyncIOFlag = TRUE; - this->duplicate = FALSE; + if ( ! client.okToStartAsynchIO () ) { + errMessage ( S_cas_badParameter, + "- duplicate async IO creation" ); + this->duplicate = true; } } diff --git a/src/cas/generic/casAsyncPVAttachIO.cc b/src/cas/generic/casAsyncPVAttachIO.cc index 924e14261..ae2c79844 100644 --- a/src/cas/generic/casAsyncPVAttachIO.cc +++ b/src/cas/generic/casAsyncPVAttachIO.cc @@ -24,25 +24,25 @@ // casAsyncPVAttachIO::casAsyncPVAttachIO() // casAsyncPVAttachIO::casAsyncPVAttachIO (const casCtx &ctx) : - casAsyncIOI (*ctx.getClient()), - msg (*ctx.getMsg()), - retVal (S_cas_badParameter) + casAsyncIOI ( ctx ), + msg ( *ctx.getMsg() ), + retVal ( S_cas_badParameter ) { - this->client.installAsyncIO (*this); + this->client.installAsyncIO ( *this ); } // // casAsyncPVAttachIO::~casAsyncPVAttachIO() // -casAsyncPVAttachIO::~casAsyncPVAttachIO() +casAsyncPVAttachIO::~casAsyncPVAttachIO () { - this->client.removeAsyncIO (*this); + this->client.removeAsyncIO ( *this ); } // // casAsyncPVAttachIO::postIOCompletion() // -caStatus casAsyncPVAttachIO::postIOCompletion(const pvAttachReturn &retValIn) +caStatus casAsyncPVAttachIO::postIOCompletion ( const pvAttachReturn & retValIn ) { this->retVal = retValIn; return this->postIOCompletionI (); @@ -56,14 +56,14 @@ epicsShareFunc caStatus casAsyncPVAttachIO::cbFuncAsyncIO() { caStatus status; - switch (this->msg.m_cmmd) { + switch ( this->msg.m_cmmd ) { case CA_PROTO_CLAIM_CIU: - status = this->client.createChanResponse (this->msg, this->retVal); + status = this->client.createChanResponse ( this->msg, this->retVal ); break; default: - errPrintf (S_cas_invalidAsynchIO, __FILE__, __LINE__, - " - client request type = %u", this->msg.m_cmmd); + errPrintf ( S_cas_invalidAsynchIO, __FILE__, __LINE__, + " - client request type = %u", this->msg.m_cmmd ); status = S_cas_invalidAsynchIO; break; } @@ -82,15 +82,15 @@ void casAsyncPVAttachIO::destroy () // // deprecated // -casAsyncPVCreateIO::casAsyncPVCreateIO(const casCtx &ctx) : - casAsyncPVAttachIO (ctx) +casAsyncPVCreateIO::casAsyncPVCreateIO ( const casCtx & ctx ) : + casAsyncPVAttachIO ( ctx ) { } // // deprecated // -epicsShareFunc casAsyncPVCreateIO::~casAsyncPVCreateIO() +epicsShareFunc casAsyncPVCreateIO::~casAsyncPVCreateIO () { } diff --git a/src/cas/generic/casAsyncPVExistIO.cc b/src/cas/generic/casAsyncPVExistIO.cc index baab90038..85f39d677 100644 --- a/src/cas/generic/casAsyncPVExistIO.cc +++ b/src/cas/generic/casAsyncPVExistIO.cc @@ -25,7 +25,7 @@ // casAsyncPVExistIO::casAsyncPVExistIO() // casAsyncPVExistIO::casAsyncPVExistIO (const casCtx &ctx) : - casAsyncIOI ( *ctx.getClient () ), + casAsyncIOI ( ctx ), msg ( *ctx.getMsg () ), retVal (pverDoesNotExistHere), dgOutAddr ( ctx.getClient ()->fetchLastRecvAddr () ), diff --git a/src/cas/generic/casAsyncReadIO.cc b/src/cas/generic/casAsyncReadIO.cc index 29c9a7c90..6ca7a16ea 100644 --- a/src/cas/generic/casAsyncReadIO.cc +++ b/src/cas/generic/casAsyncReadIO.cc @@ -15,6 +15,7 @@ * 505 665 1831 */ +#include "dbMapper.h" #include "server.h" #include "casChannelIIL.h" // casChannelI in line func @@ -24,8 +25,8 @@ // casAsyncReadIO::casAsyncReadIO() // casAsyncReadIO::casAsyncReadIO ( const casCtx & ctx ) : - casAsyncIOI ( *ctx.getClient() ), msg ( *ctx.getMsg() ), - chan( *ctx.getChannel () ), pDD ( NULL ), completionStatus ( S_cas_internal ) + casAsyncIOI ( ctx ), msg ( *ctx.getMsg() ), + chan ( *ctx.getChannel () ), pDD ( NULL ), completionStatus ( S_cas_internal ) { assert ( & this->chan ); this->chan.installAsyncIO ( *this ); @@ -48,7 +49,7 @@ caStatus casAsyncReadIO::postIOCompletion (caStatus completionStatusIn, { { epicsGuard < casCoreClient > guard ( this->client ); - this->pDD = &valueRead; + this->pDD = & valueRead; this->completionStatus = completionStatusIn; } @@ -73,25 +74,48 @@ epicsShareFunc caStatus casAsyncReadIO::cbFuncAsyncIO() switch ( this->msg.m_cmmd ) { case CA_PROTO_READ: - status = client.readResponse (&this->chan, this->msg, + status = client.readResponse ( &this->chan, this->msg, *this->pDD, this->completionStatus); break; case CA_PROTO_READ_NOTIFY: - status = client.readNotifyResponse (&this->chan, + status = client.readNotifyResponse ( &this->chan, this->msg, this->pDD, this->completionStatus); break; case CA_PROTO_EVENT_ADD: - status = client.monitorResponse (this->chan, + status = client.monitorResponse ( this->chan, this->msg, this->pDD, this->completionStatus); break; + case CA_PROTO_CLAIM_CIU: + unsigned nativeTypeDBR; + status = this->chan.getPVI().bestDBRType ( nativeTypeDBR ); + if ( status ) { + errMessage ( status, "best external dbr type fetch failed" ); + status = client.channelCreateFailedResp ( this->msg, status ); + } + else { + // we end up here if the channel claim protocol response is delayed + // by an asynchronous enum string table fetch response + if ( this->completionStatus == S_cas_success && this->pDD.valid() ) { + this->chan.getPVI().updateEnumStringTableAsyncCompletion ( *this->pDD ); + } + else { + errMessage ( this->completionStatus, + "unable to read application type \"enums\" string" + " conversion table for enumerated PV" ); + } + status = client.enumPostponedCreateChanResponse ( this->chan, + this->msg, nativeTypeDBR ); + } + break; + default: - errPrintf (S_cas_invalidAsynchIO, __FILE__, __LINE__, - " - client request type = %u", this->msg.m_cmmd); + errPrintf ( S_cas_invalidAsynchIO, __FILE__, __LINE__, + " - client request type = %u", this->msg.m_cmmd ); status = S_cas_invalidAsynchIO; break; } diff --git a/src/cas/generic/casAsyncWriteIO.cc b/src/cas/generic/casAsyncWriteIO.cc index 0e5c83f58..d92d75e6b 100644 --- a/src/cas/generic/casAsyncWriteIO.cc +++ b/src/cas/generic/casAsyncWriteIO.cc @@ -23,14 +23,14 @@ // // casAsyncWriteIO::casAsyncWriteIO() // -casAsyncWriteIO::casAsyncWriteIO(const casCtx &ctx) : - casAsyncIOI(*ctx.getClient()), - msg(*ctx.getMsg()), - chan(*ctx.getChannel()), - completionStatus(S_cas_internal) +casAsyncWriteIO::casAsyncWriteIO ( const casCtx & ctx ) : + casAsyncIOI ( ctx ), + msg ( *ctx.getMsg() ), + chan ( *ctx.getChannel() ), + completionStatus ( S_cas_internal ) { - assert (&this->chan); - this->chan.installAsyncIO(*this); + assert ( &this->chan ); + this->chan.installAsyncIO ( *this ); } // @@ -39,40 +39,40 @@ casAsyncWriteIO::casAsyncWriteIO(const casCtx &ctx) : casAsyncWriteIO::~casAsyncWriteIO() { epicsGuard < casCoreClient > guard ( this->client ); - this->chan.removeAsyncIO(*this); + this->chan.removeAsyncIO ( *this ); } // // casAsyncWriteIO::postIOCompletion() // -caStatus casAsyncWriteIO::postIOCompletion(caStatus completionStatusIn) +caStatus casAsyncWriteIO::postIOCompletion ( caStatus completionStatusIn ) { this->completionStatus = completionStatusIn; - return this->postIOCompletionI(); + return this->postIOCompletionI (); } // // casAsyncWriteIO::cbFuncAsyncIO() // (called when IO completion event reaches top of event queue) // -epicsShareFunc caStatus casAsyncWriteIO::cbFuncAsyncIO() +epicsShareFunc caStatus casAsyncWriteIO::cbFuncAsyncIO () { - caStatus status; + caStatus status; - switch (this->msg.m_cmmd) { + switch ( this->msg.m_cmmd ) { case CA_PROTO_WRITE: - status = client.writeResponse(this->msg, - this->completionStatus); + status = client.writeResponse ( this->msg, + this->completionStatus ); break; case CA_PROTO_WRITE_NOTIFY: - status = client.writeNotifyResponse( - this->msg, this->completionStatus); + status = client.writeNotifyResponse ( + this->msg, this->completionStatus ); break; default: - errPrintf (S_cas_invalidAsynchIO, __FILE__, __LINE__, - " - client request type = %u", this->msg.m_cmmd); + errPrintf ( S_cas_invalidAsynchIO, __FILE__, __LINE__, + " - client request type = %u", this->msg.m_cmmd ); status = S_cas_invalidAsynchIO; break; } diff --git a/src/cas/generic/casCoreClient.cc b/src/cas/generic/casCoreClient.cc index 4fa42f0fe..454a49d2c 100644 --- a/src/cas/generic/casCoreClient.cc +++ b/src/cas/generic/casCoreClient.cc @@ -125,7 +125,17 @@ caStatus casCoreClient::monitorResponse ( casChannelI &, const caHdrLargeArray & { return S_casApp_noSupport; } -caStatus casCoreClient::accessRightsResponse (casChannelI *) +caStatus casCoreClient::accessRightsResponse ( casChannelI * ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::enumPostponedCreateChanResponse ( casChannelI &, + const caHdrLargeArray &, unsigned ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::channelCreateFailedResp ( const caHdrLargeArray &, + caStatus createStatus ) { return S_casApp_noSupport; } diff --git a/src/cas/generic/casCoreClientIL.h b/src/cas/generic/casCoreClientIL.h index 699b6cf23..6600d223b 100644 --- a/src/cas/generic/casCoreClientIL.h +++ b/src/cas/generic/casCoreClientIL.h @@ -60,5 +60,14 @@ inline void casCoreClient::removeAsyncIO(casAsyncIOI &ioIn) this->ctx.getServer()->ioBlockedList::signal(); } +inline bool casCoreClient::okToStartAsynchIO () +{ + if ( ! this->asyncIOFlag ) { + this->asyncIOFlag = true; + return true; + } + return false; +} + #endif // casCoreClientIL_h diff --git a/src/cas/generic/casInternal.h b/src/cas/generic/casInternal.h index c93526f9b..d26122cef 100644 --- a/src/cas/generic/casInternal.h +++ b/src/cas/generic/casInternal.h @@ -221,13 +221,13 @@ class casAsyncPVAttachIO; class casAsyncIOI : public casEvent, public tsDLNode { public: - casAsyncIOI (casCoreClient &client); - epicsShareFunc virtual ~casAsyncIOI(); + casAsyncIOI ( const casCtx & ctx ); + epicsShareFunc virtual ~casAsyncIOI (); void serverDestroyIfReadOP (); void serverDestroy (); - caServer *getCAS() const; + caServer *getCAS () const; protected: casCoreClient & client; @@ -392,88 +392,48 @@ class casPVI : public: casPVI (); epicsShareFunc virtual ~casPVI (); - - // - // for use by the server library - // caServerI *getPCAS () const; - - // - // attach to a server - // - caStatus attachToServer (caServerI &cas); - - // - // CA only does 1D arrays for now (and the new server - // temporarily does only scalars) - // + caStatus attachToServer ( caServerI & cas ); aitIndex nativeCount (); - - // - // only for use by casMonitor - // caStatus registerEvent (); void unregisterEvent (); - - // - // only for use by casAsyncIOI - // void unregisterIO (); - - // - // only for use by casChannelI - // - void installChannel (casPVListChan &chan); - - // - // only for use by casChannelI - // - void removeChannel (casPVListChan &chan); - - // - // check for none attached and delete self if so - // + void installChannel ( casPVListChan & chan ); + void removeChannel ( casPVListChan & chan ); void deleteSignal (); - - void postEvent (const casEventMask &select, const gdd &event); - - caServer *getExtServer () const; - - // - // bestDBRType() - // - caStatus bestDBRType (unsigned &dbrType); - + void postEvent ( const casEventMask & select, const gdd & event ); + caServer * getExtServer () const; + caStatus bestDBRType ( unsigned & dbrType ); epicsShareFunc virtual casResType resourceType () const; - const gddEnumStringTable & enumStringTable () const; - void updateEnumStringTable (); + caStatus updateEnumStringTable ( casCtx & ); + void updateEnumStringTableAsyncCompletion ( const gdd & resp ); // // virtual functions in the public interface class // - epicsShareFunc virtual void show (unsigned level) const; + epicsShareFunc virtual void show ( unsigned level ) const; epicsShareFunc virtual caStatus interestRegister () = 0; epicsShareFunc virtual void interestDelete () = 0; epicsShareFunc virtual caStatus beginTransaction () = 0; epicsShareFunc virtual void endTransaction () = 0; - epicsShareFunc virtual caStatus read (const casCtx &ctx, gdd &prototype) = 0; - epicsShareFunc virtual caStatus write (const casCtx &ctx, const gdd &value) = 0; - epicsShareFunc virtual casChannel *createChannel (const casCtx &ctx, - const char * const pUserName, const char * const pHostName) = 0; + epicsShareFunc virtual caStatus read ( const casCtx & ctx, gdd & prototype ) = 0; + epicsShareFunc virtual caStatus write ( const casCtx & ctx, const gdd & value ) = 0; + epicsShareFunc virtual casChannel *createChannel (const casCtx & ctx, + const char * const pUserName, const char * const pHostName ) = 0; epicsShareFunc virtual aitEnum bestExternalType () const = 0; epicsShareFunc virtual unsigned maxDimension () const = 0; - epicsShareFunc virtual aitIndex maxBound (unsigned dimension) const = 0; + epicsShareFunc virtual aitIndex maxBound ( unsigned dimension ) const = 0; epicsShareFunc virtual const char *getName () const = 0; epicsShareFunc casPV *apiPointer (); //retuns NULL if casPVI isnt a base of casPV private: - tsDLList chanList; - gddEnumStringTable enumStrTbl; - caServerI *pCAS; - unsigned nMonAttached; - unsigned nIOAttached; - bool destroyInProgress; + tsDLList < casPVListChan > chanList; + gddEnumStringTable enumStrTbl; + caServerI * pCAS; + unsigned nMonAttached; + unsigned nIOAttached; + bool destroyInProgress; epicsShareFunc virtual void destroy (); // casPVI destructor noop casPVI ( const casPVI & ); diff --git a/src/cas/generic/casPVI.cc b/src/cas/generic/casPVI.cc index d46f49141..22f56d503 100644 --- a/src/cas/generic/casPVI.cc +++ b/src/cas/generic/casPVI.cc @@ -102,6 +102,8 @@ void casPVI::deleteSignal () if ( this->chanList.count() == 0u ) { this->pCAS->removeItem ( *this ); this->pCAS = NULL; + // refresh the table whenever the server reattaches to the PV + this->enumStrTbl.clear (); this->destroy (); // // !! dont access self after destroy !! @@ -124,24 +126,18 @@ void casPVI::destroy () // // casPVI::attachToServer () // -caStatus casPVI::attachToServer (caServerI &cas) +caStatus casPVI::attachToServer ( caServerI & cas ) { if ( this->pCAS ) { // // currently we enforce that the PV can be attached to only // one server at a time // - if ( this->pCAS != &cas ) { + if ( this->pCAS != & cas ) { return S_cas_pvAlreadyAttached; } } else { - // - // update only when attaching to the server so - // this does not change while clients are using it - // - this->updateEnumStringTable (); - // // install the PV into the server // @@ -159,137 +155,145 @@ caStatus casPVI::attachToServer (caServerI &cas) // // what a API complexity nightmare this GDD is // -void casPVI::updateEnumStringTable () +caStatus casPVI::updateEnumStringTable ( casCtx & ctx ) { static const aitUint32 stringTableTypeStaticInit = 0xffffffff; static aitUint32 stringTableType = stringTableTypeStaticInit; // - // reload the enum string table each time that the - // PV is attached to the server + // keep trying to fill in the table if client disconnects + // prevented previous asynchronous IO from finishing, but if + // a previous client has succeeded then dont bother. // - this->enumStrTbl.clear (); - - // - // fetch the native type - // - aitEnum bestAIT = this->bestExternalType (); - - // - // empty string table for non-enumerated PVs - // - if ( bestAIT != aitEnumEnum16 ) { - return; + if ( this->enumStrTbl.numberOfStrings () > 0 ) { + return S_cas_success; } // // lazy init // if ( stringTableType == stringTableTypeStaticInit ) { - stringTableType = gddApplicationTypeTable::app_table.registerApplicationType ("enums"); + stringTableType = + gddApplicationTypeTable::app_table.registerApplicationType ("enums"); } // // create a gdd with the "enum string table" application type // gdd *pTmp = new gddScalar ( stringTableType ); - if (pTmp==NULL) { + if ( pTmp == NULL ) { errMessage ( S_cas_noMemory, -"unable to read application type \"enums\" string conversion table for enumerated PV" ); - return; + "unable to read application type \"enums\" string" + " conversion table for enumerated PV" ); + return S_cas_noMemory; } - // - // create a false context which is guaranteed to cause - // any asynch IO to be ignored - // - casCtx ctx; - // // read the enum string table // caStatus status = this->read ( ctx, *pTmp ); - if (status == S_casApp_asyncCompletion || status == S_casApp_postponeAsyncIO) { + if ( status == S_casApp_asyncCompletion ) { + return status; + } + else if ( status == S_casApp_postponeAsyncIO ) { pTmp->unreference (); - errMessage (status, -" sorry, no support in server library for asynchronous completion of \"enums\" string conversion table for enumerated PV"); - errMessage (status, -" please fetch \"enums\" string conversion table into cache during asychronous PV attach IO completion"); - return; + return status; } else if ( status ) { pTmp->unreference (); - errMessage (status, -"unable to read application type \"enums\" string conversion table for enumerated PV"); - return; + errMessage ( status, + "unable to read application type \"enums\" string" + " conversion table for enumerated PV"); + return status; } - if ( pTmp->isContainer() ) { - errMessage (S_cas_badType, -"application type \"enums\" string conversion table for enumerated PV was a container (expected vector of strings)"); - pTmp->unreference (); + updateEnumStringTableAsyncCompletion ( *pTmp ); + pTmp->unreference (); + + return status; +} + +void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp ) +{ + // + // keep trying to fill in the table if client disconnects + // prevented previous asynchronous IO from finishing, but if + // a previous client has succeeded then dont bother. + // + if ( this->enumStrTbl.numberOfStrings () > 0 ) { return; } - if ( pTmp->dimension() == 0 ) { - if ( pTmp->primitiveType() == aitEnumString ) { - aitString *pStr = (aitString *) pTmp->dataVoid (); + if ( resp.isContainer() ) { + errMessage ( S_cas_badType, + "application type \"enums\" string conversion table for" + " enumerated PV was a container (expected vector of strings)" ); + return; + } + + if ( resp.dimension() == 0 ) { + if ( resp.primitiveType() == aitEnumString ) { + aitString *pStr = (aitString *) resp.dataVoid (); if ( ! this->enumStrTbl.setString ( 0, pStr->string() ) ) { - errMessage ( S_cas_noMemory, "no memory to set enumerated PV string cache" ); + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); } } - else if ( pTmp->primitiveType()==aitEnumFixedString ) { - aitFixedString *pStr = (aitFixedString *) pTmp->dataVoid (); + else if ( resp.primitiveType() == aitEnumFixedString ) { + aitFixedString *pStr = (aitFixedString *) resp.dataVoid (); if ( ! this->enumStrTbl.setString ( 0, pStr->fixed_string ) ) { - errMessage ( S_cas_noMemory, "no memory to set enumerated PV string cache" ); + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); } } else { errMessage ( S_cas_badType, -"application type \"enums\" string conversion table for enumerated PV isnt a string type?" ); + "application type \"enums\" string conversion" + " table for enumerated PV isnt a string type?" ); } } - else if ( pTmp->dimension() == 1 ) { + else if ( resp.dimension() == 1 ) { gddStatus gdd_status; aitIndex index, first, count; - gdd_status = pTmp->getBound ( 0, first, count ); - assert (gdd_status == 0); + gdd_status = resp.getBound ( 0, first, count ); + assert ( gdd_status == 0 ); // // preallocate the correct amount // this->enumStrTbl.reserve ( count ); - if ( pTmp->primitiveType() == aitEnumString ) { - aitString *pStr = (aitString *) pTmp->dataVoid (); + if ( resp.primitiveType() == aitEnumString ) { + aitString *pStr = (aitString *) resp.dataVoid (); for ( index = 0; indexenumStrTbl.setString ( index, pStr[index].string() ) ) { - errMessage ( S_cas_noMemory, "no memory to set enumerated PV string cache" ); + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); } } } - else if ( pTmp->primitiveType() == aitEnumFixedString ) { - aitFixedString *pStr = (aitFixedString *) pTmp->dataVoid (); + else if ( resp.primitiveType() == aitEnumFixedString ) { + aitFixedString *pStr = (aitFixedString *) resp.dataVoid (); for ( index = 0; indexenumStrTbl.setString ( index, pStr[index].fixed_string ) ) { - errMessage ( S_cas_noMemory, "no memory to set enumerated PV string cache" ); + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); } } } else { errMessage ( S_cas_badType, -"application type \"enums\" string conversion table for enumerated PV isnt a string type?" ); + "application type \"enums\" string conversion" + " table for enumerated PV isnt a string type?" ); } } else { errMessage ( S_cas_badType, -"application type \"enums\" string conversion table for enumerated PV was multi-dimensional (expected vector of strings)" ); + "application type \"enums\" string conversion table" + " for enumerated PV was multi-dimensional" + " (expected vector of strings)" ); } - - pTmp->unreference (); - - return; } // diff --git a/src/cas/generic/casStrmClient.cc b/src/cas/generic/casStrmClient.cc index a48b11412..fd3c79e23 100644 --- a/src/cas/generic/casStrmClient.cc +++ b/src/cas/generic/casStrmClient.cc @@ -1006,46 +1006,94 @@ caStatus casStrmClient::claimChannelAction() // // LOCK must be applied // -caStatus casStrmClient::createChanResponse(const caHdrLargeArray &hdr, const pvAttachReturn &pvar) +caStatus casStrmClient::createChanResponse ( const caHdrLargeArray & hdr, const pvAttachReturn & pvar ) { - casPVI *pPV; - casChannel *pChan; - casChannelI *pChanI; - bufSizeT nBytes; - caStatus status; - - if (pvar.getStatus() != S_cas_success) { - return this->channelCreateFailed (&hdr, pvar.getStatus()); + if ( pvar.getStatus() != S_cas_success ) { + return this->channelCreateFailedResp ( hdr, pvar.getStatus() ); } - pPV = pvar.getPV(); + casPVI * pPV = pvar.getPV(); // // If status is ok and the PV isnt set then guess that the // pv isnt in this server // - if (pPV == NULL) { - return this->channelCreateFailed (&hdr, S_casApp_pvNotFound); + if ( pPV == NULL ) { + return this->channelCreateFailedResp ( hdr, S_casApp_pvNotFound ); } - // - // fetch the native type - // - unsigned nativeType; - status = pPV->bestDBRType(nativeType); - if (status) { - errMessage(status, "best external dbr type fetch failed"); - return this->channelCreateFailed (&hdr, status); + unsigned nativeTypeDBR; + caStatus status = pPV->bestDBRType ( nativeTypeDBR ); + if ( status ) { + errMessage ( status, "best external dbr type fetch failed" ); + return this->channelCreateFailedResp ( hdr, status ); } // // attach the PV to this server // - status = pPV->attachToServer (this->getCAS()); - if (status) { - return this->channelCreateFailed (&hdr, status); + status = pPV->attachToServer ( this->getCAS() ); + if ( status ) { + return this->channelCreateFailedResp ( hdr, status ); } + // + // create server tool XXX derived from casChannel + // + this->ctx.setPV ( pPV ); + casChannel * pChan = pPV->createChannel ( this->ctx, this->pUserName, this->pHostName ); + if ( ! pChan ) { + pPV->deleteSignal(); + return this->channelCreateFailedResp ( hdr, S_cas_noMemory ); + } + + pChan->bindToClient ( *this, *pPV, hdr.m_cid ); + + casChannelI * pChanI = (casChannelI *) pChan; + + // + // check to see if the enum table is empty and therefore + // an update is needed every time that a PV attaches + // to the server in case the client disconnected before + // an asynchronous IO to get the table comleted + // + if ( nativeTypeDBR == DBR_ENUM ) { + this->ctx.setPV ( pPV ); + this->ctx.setChannel ( pChanI ); + this->asyncIOFlag = false; + status = pPV->updateEnumStringTable ( this->ctx ); + if ( this->asyncIOFlag ) { + if ( status != S_casApp_asyncCompletion ) { + fprintf ( stderr, + "Application returned %d from casPV::read()" + " - expected S_casApp_asyncCompletion\n", status); + status = S_casApp_asyncCompletion; + } + } + else if ( status == S_casApp_asyncCompletion) { + status = S_cas_badParameter; + errMessage ( status, + "- expected asynch IO creation from casPV::read()"); + } + else if ( status == S_casApp_success ) { + status = enumPostponedCreateChanResponse ( *pChan, hdr, nativeTypeDBR ); + } + } + else { + status = enumPostponedCreateChanResponse ( *pChan, hdr, nativeTypeDBR ); + } + + return status; +} + +// +// casStrmClient::enumPostponedCreateChanResponse() +// +// LOCK must be applied +// +caStatus casStrmClient::enumPostponedCreateChanResponse ( + casChannelI & chan, const caHdrLargeArray & hdr, unsigned nativeTypeDBR ) +{ // // We are allocating enough space for both the claim // response and the access rights response so that we know for @@ -1054,36 +1102,21 @@ caStatus casStrmClient::createChanResponse(const caHdrLargeArray &hdr, const pvA void *pRaw; const outBufCtx outctx = this->out.pushCtx ( 0, 2 * sizeof ( caHdr ), pRaw ); - if (outctx.pushResult()!=outBufCtx::pushCtxSuccess) { + if ( outctx.pushResult() != outBufCtx::pushCtxSuccess ) { return S_cas_sendBlocked; } - // - // create server tool XXX derived from casChannel - // - this->ctx.setPV (pPV); - pChan = pPV->createChannel (this->ctx, this->pUserName, this->pHostName); - if (!pChan) { - this->out.popCtx (outctx); - pPV->deleteSignal(); - return this->channelCreateFailed (&hdr, S_cas_noMemory); - } - - pChan->bindToClient ( *this, *pPV, hdr.m_cid ); - - pChanI = (casChannelI *) pChan; - // // We are certain that the request will complete // here because we allocated enough space for this // and the claim response above. // - status = casStrmClient::accessRightsResponse(pChanI); - if (status) { - this->out.popCtx (outctx); - errMessage(status, "incomplete channel create?"); - pChanI->destroyNoClientNotify(); - return this->channelCreateFailed(&hdr, status); + caStatus status = casStrmClient::accessRightsResponse ( & chan ); + if ( status ) { + this->out.popCtx ( outctx ); + errMessage ( status, "incomplete channel create?" ); + chan.destroyNoClientNotify (); + return this->channelCreateFailedResp ( hdr, status ); } // @@ -1095,17 +1128,17 @@ caStatus casStrmClient::createChanResponse(const caHdrLargeArray &hdr, const pvA // here to be certain that we are at the correct place in // the protocol buffer. // - assert ( nativeType <= 0xffff ); - unsigned nativeCount = pPV->nativeCount(); + assert ( nativeTypeDBR <= 0xffff ); + unsigned nativeCount = chan.getPVI().nativeCount(); status = this->out.copyInHeader ( CA_PROTO_CLAIM_CIU, 0, - static_cast ( nativeType ), + static_cast ( nativeTypeDBR ), static_cast ( nativeCount ), - hdr.m_cid, pChanI->getSID(), 0 ); + hdr.m_cid, chan.getSID(), 0 ); if ( status != S_cas_success ) { this->out.popCtx ( outctx ); errMessage ( status, "incomplete channel create?" ); - pChanI->destroyNoClientNotify(); - return this->channelCreateFailed ( &hdr, status ); + chan.destroyNoClientNotify (); + return this->channelCreateFailedResp ( hdr, status ); } this->out.commitMsg (); @@ -1113,7 +1146,7 @@ caStatus casStrmClient::createChanResponse(const caHdrLargeArray &hdr, const pvA // // commit the message // - nBytes = this->out.popCtx (outctx); + bufSizeT nBytes = this->out.popCtx (outctx); assert ( nBytes == 2*sizeof(caHdr) ); this->out.commitRawMsg (nBytes); @@ -1126,8 +1159,8 @@ caStatus casStrmClient::createChanResponse(const caHdrLargeArray &hdr, const pvA * If we are talking to an CA_V46 client then tell them when a channel * cant be created (instead of just disconnecting) */ -caStatus casStrmClient::channelCreateFailed ( - const caHdrLargeArray *mp, caStatus createStatus ) +caStatus casStrmClient::channelCreateFailedResp ( + const caHdrLargeArray & hdr, caStatus createStatus ) { caStatus status; @@ -1142,7 +1175,7 @@ caStatus casStrmClient::channelCreateFailed ( } if ( CA_V46( this->minor_version_number ) ) { status = this->out.copyInHeader ( CA_PROTO_CLAIM_CIU_FAILED, 0, - 0, 0, mp->m_cid, 0, 0 ); + 0, 0, hdr.m_cid, 0, 0 ); if ( status ) { return status; } @@ -1150,7 +1183,7 @@ caStatus casStrmClient::channelCreateFailed ( createStatus = S_cas_success; } else { - status = this->sendErrWithEpicsStatus ( mp, createStatus, ECA_ALLOCMEM ); + status = this->sendErrWithEpicsStatus ( & hdr, createStatus, ECA_ALLOCMEM ); if ( status ) { return status; } diff --git a/src/cas/generic/server.h b/src/cas/generic/server.h index 9d21c5d52..284dda5b6 100644 --- a/src/cas/generic/server.h +++ b/src/cas/generic/server.h @@ -423,33 +423,22 @@ private: // class casCoreClient : public ioBlocked, public casEventSys { - - // - // allows casAsyncIOI constructor to check for asynch IO duplicates - // - friend casAsyncIOI::casAsyncIOI(casCoreClient &clientIn); - public: - casCoreClient(caServerI &serverInternal); + casCoreClient ( caServerI &serverInternal ); virtual ~casCoreClient(); virtual void destroy(); - virtual caStatus disconnectChan(caResId id); - virtual void show (unsigned level) const; - virtual void installChannel (casChannelI &); - virtual void removeChannel (casChannelI &); + virtual caStatus disconnectChan( caResId id ); + virtual void show (unsigned level ) const; + virtual void installChannel ( casChannelI & ); + virtual void removeChannel ( casChannelI & ); - void installAsyncIO(casAsyncIOI &ioIn); + void installAsyncIO( casAsyncIOI & ioIn ); - void removeAsyncIO(casAsyncIOI &ioIn); + void removeAsyncIO( casAsyncIOI & ioIn ); - casRes *lookupRes(const caResId &idIn, casResType type); + casRes * lookupRes ( const caResId &idIn, casResType type ); - caServerI &getCAS() const; - - virtual caStatus monitorResponse ( casChannelI &chan, const caHdrLargeArray &msg, - const smartConstGDDPointer &pDesc, const caStatus status ); - - virtual caStatus accessRightsResponse(casChannelI *); + caServerI & getCAS () const; void lock (); void unlock (); @@ -465,13 +454,20 @@ public: virtual caStatus createChanResponse ( const caHdrLargeArray &, const pvAttachReturn &); virtual caStatus readResponse ( - casChannelI *, const caHdrLargeArray &, const smartConstGDDPointer &, const caStatus); + casChannelI *, const caHdrLargeArray &, const smartConstGDDPointer &, const caStatus ); virtual caStatus readNotifyResponse ( - casChannelI *, const caHdrLargeArray &, const smartConstGDDPointer &, const caStatus); - virtual caStatus writeResponse (const caHdrLargeArray &, const caStatus); - virtual caStatus writeNotifyResponse (const caHdrLargeArray &, const caStatus); + casChannelI *, const caHdrLargeArray &, const smartConstGDDPointer &, const caStatus ); + virtual caStatus writeResponse ( const caHdrLargeArray &, const caStatus ); + virtual caStatus writeNotifyResponse ( const caHdrLargeArray &, const caStatus ); + virtual caStatus monitorResponse ( casChannelI &chan, const caHdrLargeArray &msg, + const smartConstGDDPointer & pDesc, const caStatus status ); + virtual caStatus accessRightsResponse ( casChannelI * ); + virtual caStatus enumPostponedCreateChanResponse ( casChannelI & chan, + const caHdrLargeArray & hdr, unsigned dbrType ); + virtual caStatus channelCreateFailedResp ( const caHdrLargeArray &, + caStatus createStatus ); - virtual ca_uint16_t protocolRevision() const = 0; + virtual ca_uint16_t protocolRevision () const = 0; // // used only with DG clients @@ -479,6 +475,8 @@ public: virtual caNetAddr fetchLastRecvAddr () const; virtual ca_uint32_t datagramSequenceNumber () const; + bool okToStartAsynchIO (); + protected: epicsMutex mutex; casCtx ctx; @@ -486,7 +484,6 @@ protected: private: tsDLList < casAsyncIOI > ioInProgList; - casCoreClient ( const casCoreClient & ); casCoreClient & operator = ( const casCoreClient & ); }; @@ -616,6 +613,10 @@ public: caStatus writeNotifyResponse (const caHdrLargeArray &msg, const caStatus status); caStatus monitorResponse ( casChannelI & chan, const caHdrLargeArray & msg, const smartConstGDDPointer & pDesc, const caStatus status ); + caStatus enumPostponedCreateChanResponse ( casChannelI & chan, + const caHdrLargeArray & hdr, unsigned dbrType ); + caStatus channelCreateFailedResp ( const caHdrLargeArray &, + caStatus createStatus ); caStatus noReadAccessEvent( casClientMon * ); @@ -673,11 +674,6 @@ private: caStatus read (smartGDDPointer &pDesc); caStatus write (); - // - // channelCreateFailed() - // - caStatus channelCreateFailed ( const caHdrLargeArray *mp, caStatus createStatus ); - caStatus writeArrayData(); caStatus writeScalarData(); caStatus writeString();