diff --git a/src/cas/RELEASE_NOTES b/src/cas/RELEASE_NOTES index a4a675737..8662ff906 100644 --- a/src/cas/RELEASE_NOTES +++ b/src/cas/RELEASE_NOTES @@ -118,3 +118,78 @@ The defaults in base class casPV implement identical behavior to the past if these routines are not supplied by the derived class. +Changes between epics 3.13 Beta 6 and 3.13 Beta ???? + +**** API Change **** + +o The member function "casChannel::postEvent()" has been replaced by +"casChannel::postAccessRightsEvent()". An access rights state change +event is now posted to the client each time that +"casChannel::postAccessRightsEvent()" is called. + +o The virtual functions "casChannel::interestRegister()" +and "casChannel::interestDelete()" have been eliminated. + +o The constructor "caServer::caServer()" no-longer has an argument specifying +the maximum PV name length. It also no longer has an argument specifying +the maximum simultaneous IO operations. THIS IS LIKELY TO BREAK YOUR CODE +BECAUSE THE FIRST TWO ARGUMENTS WERE REMOVED AND THERE ARE DEFAULT ARGUMENTS. +This change was made because we would like to remove all limits on +the PV name length (real or perceived). We also felt that if a server +tool wishes to postpone an asynchronious IO operation then it +should return S_casApp_postponeAsyncIO from caServer::pvExistTest() and +caServer::createPV() (instead of relying on the server to keep track of +the number of simultaneous asynchronous IO operations). This provides a +less complex and more flexible API. + +o The member function "casPV::casPV(caServer &cas)" replaces the member +function "casPV::casPV(const casCtx &ctx, const char * const pPVName)". + +o The virtual member function +"caServer::createPV(const casCtx &ctx, const char *pPVName)" +has been replaced by the virtual member function +"pvCreateReturn createPV (const casCtx &ctx, const char *pPVAliasName)" +This change was made in order to allow asynchronous completion of a +PV create operation. + +o The data type (class) pvExistReturn has been changed to an enum - +"enum pvExistReturn {pverExistsHere, pverDoesNotExistHere, + pverAsyncCompletion, pverNoMemoryForAsyncOP}" +This impacts the virtual member function +"pvExistTest (const casCtx &ctx, const char *pPVAliasName)" + +o The server tool is now required to supply the virtual function +"casPV::getName()" so that the server is able to identify the process +variable when diagnostics are printed. + +o The virtual function casPV::maxSimultAsyncOps() has been eliminated +in favor of allowing the server tool to return S_casApp_postponeAsyncIO +from casPV::read() or casPV::write() when there are too many simultaneous +asynchronous IO operations and the server tool would like to postpone +the current (and future) request(s) until one of the outstanding asynchronous +IO operations (read or write) completes. + +o All "show()" virtual member functions in the interface classes +have had the "const" attribute added. + +**** Semantic Change **** + +o IMPORTANT: It is now the responsibility of the server tool to detect attempts +by the server lib to create a 2nd PV with the same canonical name as an +existing PV and avoid this by returning a pointer to the first PV created. +Likewise, if there are several aliases for one canonical PV name then it is +the responsibility of the server tool to return "pvExistsHere" from +"caServerDerived::pvExistTest()" for each of the aliases. Likewise, if there +are several aliases for one canonical PV name then it is the responsibility +of the server tool to return a single PV with the canonical name from +"caServerDerived::createPV()" (even if createPV() is called multiple times +each with a different alias name). This change was made to simplify the API +and to eliminate redundant data structures and labor occurring within the server +tool and the server library. + +o PV creation is now allowed to complete asynchronously + +o It is now the responsibility of the server tool to limit the +number of simultaneous asynchronous IO operations allowed (by returning +S_casApp_postponeAsyncIO). + diff --git a/src/cas/example/simple/Makefile.Host b/src/cas/example/simple/Makefile.Host index 2e58b6a94..aed46bc9a 100644 --- a/src/cas/example/simple/Makefile.Host +++ b/src/cas/example/simple/Makefile.Host @@ -21,6 +21,9 @@ PROD := excas include $(TOP)/config/RULES.Host +pexcas: $(PROD_OBJS) $(PRODDEPLIBS) + $(PURIFY) $(PROD_LINKER) $(PROD_OBJS) $(LDLIBS) + clean:: @$(RM) excas @$(RM) fexcas diff --git a/src/cas/example/simple/exAsyncPV.cc b/src/cas/example/simple/exAsyncPV.cc index e674835b8..d9f2ef212 100644 --- a/src/cas/example/simple/exAsyncPV.cc +++ b/src/cas/example/simple/exAsyncPV.cc @@ -4,16 +4,7 @@ // (asynchrronous process variable) // -#include - -// -// exAsyncPV::maxSimultAsyncOps() -// (virtual replacement for the default) -// -unsigned exAsyncPV::maxSimultAsyncOps () const -{ - return 500u; -} +#include "exServer.h" // // exAsyncPV::read() @@ -23,6 +14,12 @@ caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn) { exAsyncReadIO *pIO; + if (this->simultAsychIOCount>=maxSimultAsyncIO) { + return S_casApp_postponeAsyncIO; + } + + this->simultAsychIOCount++; + pIO = new exAsyncReadIO(ctx, *this, valueIn); if (!pIO) { return S_casApp_noMemory; @@ -39,6 +36,12 @@ caStatus exAsyncPV::write (const casCtx &ctx, gdd &valueIn) { exAsyncWriteIO *pIO; + if (this->simultAsychIOCount>=maxSimultAsyncIO) { + return S_casApp_postponeAsyncIO; + } + + this->simultAsychIOCount++; + pIO = new exAsyncWriteIO(ctx, *this, valueIn); if (!pIO) { return S_casApp_noMemory; diff --git a/src/cas/example/simple/exChannel.cc b/src/cas/example/simple/exChannel.cc index 18802d574..68e56055a 100644 --- a/src/cas/example/simple/exChannel.cc +++ b/src/cas/example/simple/exChannel.cc @@ -3,5 +3,5 @@ // Example EPICS CA server // -#include +#include "exServer.h" diff --git a/src/cas/example/simple/exPV.cc b/src/cas/example/simple/exPV.cc index 3d1918e70..aa7890908 100644 --- a/src/cas/example/simple/exPV.cc +++ b/src/cas/example/simple/exPV.cc @@ -2,25 +2,33 @@ // Example EPICS CA server // -#include -#include +#include "exServer.h" +#include "gddApps.h" osiTime exPV::currentTime; // // exPV::exPV() // -exPV::exPV (const casCtx &ctxIn, const pvInfo &setup) : +exPV::exPV (caServer &casIn, pvInfo &setup, aitBool preCreateFlag) : pValue(NULL), - pScanTimer(NULL), info(setup), - casPV(ctxIn, setup.getName().string()), - interest(aitFalse) + casPV(casIn), + interest(aitFalse), + preCreate(preCreateFlag) { // // no dataless PV allowed // assert (this->info.getElementCount()>=1u); + + // + // start a very slow background scan + // (we will speed this up to the normal rate when + // someone is watching the PV) + // + this->pScanTimer = + new exScanTimer (this->getScanPeriod(), *this); } // @@ -36,6 +44,19 @@ exPV::~exPV() this->pValue->unreference(); this->pValue = NULL; } + this->info.destroyPV(); +} + +// +// exPV::destroy() +// this is replaced by a noop since we are +// pre-creating most of the PVs during init in this simple server +// +void exPV::destroy() +{ + if (!this->preCreate) { + delete this; + } } // @@ -102,7 +123,7 @@ osiBool exScanTimer::again() const // const osiTime exScanTimer::delay() const { - return pv.getScanRate(); + return pv.getScanPeriod(); } // @@ -132,19 +153,26 @@ caStatus exPV::interestRegister() return S_casApp_success; } - if (!this->pScanTimer) { + this->interest = aitTrue; + + // + // If a slow scan is pending then reschedule it + // with the specified scan period. + // + if (this->pScanTimer) { + this->pScanTimer->reschedule(this->info.getScanPeriod()); + } + else { this->pScanTimer = new exScanTimer - (this->info.getScanRate(), *this); + (this->info.getScanPeriod(), *this); if (!this->pScanTimer) { errPrintf (S_cas_noMemory, __FILE__, __LINE__, "Scan init for %s failed\n", - this->info.getName().string()); + this->info.getName()); return S_cas_noMemory; } } - this->interest = aitTrue; - return S_casApp_success; } @@ -153,17 +181,16 @@ caStatus exPV::interestRegister() // void exPV::interestDelete() { - if (this->pScanTimer) { - delete this->pScanTimer; - this->pScanTimer = NULL; - } this->interest = aitFalse; + if (this->pScanTimer) { + this->pScanTimer->reschedule(this->getScanPeriod()); + } } // // exPV::show() // -void exPV::show(unsigned level) +void exPV::show(unsigned level) const { if (level>1u) { if (this->pValue) { diff --git a/src/cas/example/simple/exScalarPV.cc b/src/cas/example/simple/exScalarPV.cc index 074215948..294ce2976 100644 --- a/src/cas/example/simple/exScalarPV.cc +++ b/src/cas/example/simple/exScalarPV.cc @@ -1,10 +1,11 @@ -#include -#include #include #include #include +#include "exServer.h" +#include "gddApps.h" + #define myPI 3.14159265358979323846 // @@ -31,7 +32,7 @@ void exScalarPV::scan() gdd *pDD; float newValue; float limit; - + // // update current time (so we are not required to do // this every time that we write the PV which impacts diff --git a/src/cas/example/simple/exServer.cc b/src/cas/example/simple/exServer.cc index fba5d736f..c3b5583c7 100644 --- a/src/cas/example/simple/exServer.cc +++ b/src/cas/example/simple/exServer.cc @@ -7,124 +7,267 @@ // Example EPICS CA server // -#include - -const pvInfo exServer::pvList[] = { - pvInfo (1.0e-1, "jane", 10.0f, 0.0f, excasIoSync, 1u), - pvInfo (2.0, "fred", 10.0f, -10.0f, excasIoSync, 1u), - pvInfo (1.0e-1, "janet", 10.0f, 0.0f, excasIoAsync, 1u), - pvInfo (2.0, "freddy", 10.0f, -10.0f, excasIoAsync, 1u), - pvInfo (2.0, "alan", 10.0f, -10.0f, excasIoSync, 100u), - pvInfo (20.0, "albert", 10.0f, -10.0f, excasIoSync, 1000u) -}; +#include "exServer.h" // // static data for exServer // gddAppFuncTable exServer::ft; +// +// static list of pre-created PVs +// +pvInfo exServer::pvList[] = { + pvInfo (1.0e-1, "jane", 10.0f, 0.0f, excasIoSync, 1u), + pvInfo (2.0, "fred", 10.0f, -10.0f, excasIoSync, 1u), + pvInfo (1.0e-1, "janet", 10.0f, 0.0f, excasIoAsync, 1u), + pvInfo (2.0, "freddy", 10.0f, -10.0f, excasIoAsync, 1u), + pvInfo (2.0, "alan", 10.0f, -10.0f, excasIoSync, 100u), + pvInfo (20.0, "albert", 10.0f, -10.0f, excasIoSync, 1000u) +}; +// +// static on-the-fly PVs +// +pvInfo exServer::bill (2.0, "bill", 10.0f, -10.0f, excasIoSync, 1u); +pvInfo exServer::billy (2.0, "billy", 10.0f, -10.0f, excasIoAsync, 1u); + // // exServer::exServer() // -exServer::exServer(unsigned pvMaxNameLength, unsigned pvCountEstimate, - unsigned maxSimultaneousIO) : - caServer(pvMaxNameLength, pvCountEstimate, maxSimultaneousIO) +exServer::exServer(const char * const pvPrefix, unsigned aliasCount) : + caServer(NELEMENTS(this->pvList)+2u), + simultAsychIOCount(0u) { - ft.installReadFunc("status",exPV::getStatus); - ft.installReadFunc("severity",exPV::getSeverity); - ft.installReadFunc("seconds",exPV::getSeconds); - ft.installReadFunc("nanoseconds",exPV::getNanoseconds); - ft.installReadFunc("precision",exPV::getPrecision); - ft.installReadFunc("graphicHigh",exPV::getHighLimit); - ft.installReadFunc("graphicLow",exPV::getLowLimit); - ft.installReadFunc("controlHigh",exPV::getHighLimit); - ft.installReadFunc("controlLow",exPV::getLowLimit); - ft.installReadFunc("alarmHigh",exPV::getHighLimit); - ft.installReadFunc("alarmLow",exPV::getLowLimit); - ft.installReadFunc("alarmHighWarning",exPV::getHighLimit); - ft.installReadFunc("alarmLowWarning",exPV::getLowLimit); - ft.installReadFunc("units",exPV::getUnits); - ft.installReadFunc("value",exPV::getValue); - ft.installReadFunc("enums",exPV::getEnums); + unsigned i; + exPV *pPV; + pvInfo *pPVI; + pvInfo *pPVAfter = + &exServer::pvList[NELEMENTS(exServer::pvList)]; + int resLibStatus; + char pvAlias[256]; + const char * const pNameFmtStr = "%.100s%.20s"; + const char * const pAliasFmtStr = "%.100s%.20s%u"; + + ft.installReadFunc ("status", &exPV::getStatus); + ft.installReadFunc ("severity", &exPV::getSeverity); + ft.installReadFunc ("seconds", &exPV::getSeconds); + ft.installReadFunc ("nanoseconds", &exPV::getNanoseconds); + ft.installReadFunc ("precision", &exPV::getPrecision); + ft.installReadFunc ("graphicHigh", &exPV::getHighLimit); + ft.installReadFunc ("graphicLow", &exPV::getLowLimit); + ft.installReadFunc ("controlHigh", &exPV::getHighLimit); + ft.installReadFunc ("controlLow", &exPV::getLowLimit); + ft.installReadFunc ("alarmHigh", &exPV::getHighLimit); + ft.installReadFunc ("alarmLow", &exPV::getLowLimit); + ft.installReadFunc ("alarmHighWarning", &exPV::getHighLimit); + ft.installReadFunc ("alarmLowWarning", &exPV::getLowLimit); + ft.installReadFunc ("units", &exPV::getUnits); + ft.installReadFunc ("value", &exPV::getValue); + ft.installReadFunc ("enums", &exPV::getEnums); + + // + // hash table size may need adjustment here? + // + resLibStatus = this->stringResTbl.init(NELEMENTS(this->pvList)*(aliasCount+1u)+2u); + if (resLibStatus) { + fprintf(stderr, "CAS: string resource id table init failed\n"); + // + // should throw an exception once this is portable + // + assert(resLibStatus==0); + } + + // + // pre-create all of the simple PVs that this server will export + // + for (pPVI = exServer::pvList; pPVI < pPVAfter; pPVI++) { + pPV = pPVI->createPV (*this, aitTrue); + if (!pPV) { + fprintf(stderr, "Unable to create new PV \"%s\"\n", + pPVI->getName()); + } + + + // + // Install canonical (root) name + // + sprintf(pvAlias, pNameFmtStr, pvPrefix, pPVI->getName()); + this->installAliasName(*pPVI, pvAlias); + + // + // Install numbered alias names + // + for (i=0u; igetName(), i); + this->installAliasName(*pPVI, pvAlias); + } + } + + // + // Install create on-the-fly PVs + // into the PV name hash table + // + sprintf(pvAlias, pNameFmtStr, pvPrefix, bill.getName()); + this->installAliasName(bill, pvAlias); + sprintf(pvAlias, pNameFmtStr, pvPrefix, billy.getName()); + this->installAliasName(billy, pvAlias); +} + +// +// exServer::installAliasName() +// +void exServer::installAliasName(pvInfo &info, const char *pAliasName) +{ + pvEntry *pEntry; + + pEntry = new pvEntry(info, *this, pAliasName); + if (pEntry) { + int resLibStatus; + resLibStatus = this->stringResTbl.add(*pEntry); + if (resLibStatus==0) { + return; + } + else { + delete pEntry; + } + } + fprintf(stderr, +"Unable to enter PV=\"%s\" Alias=\"%s\" in PV name alias hash table\n", + info.getName(), pAliasName); } // // exServer::pvExistTest() // -pvExistReturn exServer::pvExistTest(const casCtx &ctxIn, const char *pPVName) +pvExistReturn exServer::pvExistTest(const casCtx& ctxIn, const char *pPVName) { - const pvInfo *pPVI; + // + // lifetime of id is shorter than lifetime of pName + // + stringId id(pPVName, stringId::refString); + pvEntry *pPVE; - pPVI = exServer::findPV(pPVName); - if (pPVI) { - if (pPVI->getIOType()==excasIoAsync) { - exAsyncExistIO *pIO; - pIO = new exAsyncExistIO(*pPVI, ctxIn); - if (pIO) { - return pvExistReturn(S_casApp_asyncCompletion); - } - else { - return pvExistReturn(S_casApp_noMemory); - } - } - - const char *pName = pPVI->getName(); - return pvExistReturn(S_casApp_success, pName); + // + // Look in hash table for PV name (or PV alias name) + // + pPVE = this->stringResTbl.lookup(id); + if (!pPVE) { + return pverDoesNotExistHere; } - return pvExistReturn(S_casApp_pvNotFound); -} + pvInfo &pvi(pPVE->getInfo()); -// -// findPV() -// -const pvInfo *exServer::findPV(const char *pName) -{ - const pvInfo *pPVI; - const pvInfo *pPVAfter = - &exServer::pvList[NELEMENTS(exServer::pvList)]; + // + // Initiate async IO if this is an async PV + // + if (pvi.getIOType() == excasIoSync) { + return pverExistsHere; + } + else { + if (this->simultAsychIOCount>=maxSimultAsyncIO) { + return pverDoesNotExistHere; + } - for (pPVI = exServer::pvList; pPVI < pPVAfter; pPVI++) { - if (strcmp (pName, pPVI->getName().string()) == '\0') { - return pPVI; + this->simultAsychIOCount++; + + exAsyncExistIO *pIO; + pIO = new exAsyncExistIO(pvi, ctxIn, *this); + if (pIO) { + return pverAsyncCompletion; + } + else { + return pverDoesNotExistHere; } } - return NULL; } // // exServer::createPV() // -casPV *exServer::createPV (const casCtx &ctxIn, const char *pPVName) +pvCreateReturn exServer::createPV (const casCtx &ctx, const char *pName) { - const pvInfo *pInfo; - exPV *pPV; + // + // lifetime of id is shorter than lifetime of pName + // + stringId id(pName, stringId::refString); + exPV *pPV; + pvEntry *pPVE; - pInfo = exServer::findPV(pPVName); - if (!pInfo) { - return NULL; + pPVE = this->stringResTbl.lookup(id); + if (!pPVE) { + return pvCreateReturn(S_casApp_pvNotFound); } + pvInfo &pvi(pPVE->getInfo()); + + // + // If this is a synchronous PV create the PV now + // + if (pvi.getIOType() == excasIoSync) { + pPV = pvi.createPV(*this, aitFalse); + if (!pPV) { + pvCreateReturn(S_casApp_noMemory); + } + return pvCreateReturn(*pPV); + } + // + // Initiate async IO if this is an async PV + // + else { + if (this->simultAsychIOCount>=maxSimultAsyncIO) { + return pvCreateReturn(S_casApp_postponeAsyncIO); + } + + this->simultAsychIOCount++; + + exAsyncCreateIO *pIO = + new exAsyncCreateIO(pvi, *this, ctx); + if (pIO) { + return pvCreateReturn(S_casApp_asyncCompletion); + } + else { + return pvCreateReturn(S_casApp_noMemory); + } + } +} + +// +// pvInfo::createPV() +// +exPV *pvInfo::createPV (exServer &exCAS, aitBool preCreateFlag) +{ + if (this->pPV) { + return this->pPV; + } + + exPV *pNewPV; + // // create an instance of the appropriate class // depending on the io type and the number // of elements // - if (pInfo->getElementCount()==1u) { - switch (pInfo->getIOType()){ + if (this->elementCount==1u) { + switch (this->ioType){ case excasIoSync: - pPV = new exScalarPV (ctxIn, *pInfo); + pNewPV = new exScalarPV (exCAS, *this, preCreateFlag); break; case excasIoAsync: - pPV = new exAsyncPV (ctxIn, *pInfo); + pNewPV = new exAsyncPV (exCAS, *this, preCreateFlag); break; default: - pPV = NULL; + pNewPV = NULL; break; } } else { - pPV = new exVectorPV (ctxIn, *pInfo); + if (this->ioType==excasIoSync) { + pNewPV = new exVectorPV (exCAS, *this, preCreateFlag); + } + else { + pNewPV = NULL; + } } // @@ -132,21 +275,23 @@ casPV *exServer::createPV (const casCtx &ctxIn, const char *pPVName) // the constructor because the base class's // pure virtual function would be called) // - if (pPV) { - pPV->scan(); + if (pNewPV) { + this->pPV = pNewPV; + pNewPV->scan(); } - return pPV; + return pNewPV; } // -// exServer::show() +// exServer::show() // -void exServer::show (unsigned level) +void exServer::show (unsigned level) const { // // server tool specific show code goes here // + this->stringResTbl.show(level); // // print information about ca server libarary @@ -170,12 +315,10 @@ void exOSITimer::destroy() // void exAsyncExistIO::expire() { - const char *pName = pvi.getName(); - // // post IO completion // - this->postIOCompletion (pvExistReturn(S_cas_success, pName)); + this->postIOCompletion (pverExistsHere); } // @@ -186,3 +329,28 @@ const char *exAsyncExistIO::name() const return "exAsyncExistIO"; } +// +// exAsyncCreateIO::expire() +// (a virtual function that runs when the base timer expires) +// +void exAsyncCreateIO::expire() +{ + exPV *pPV; + + pPV = this->pvi.createPV(this->cas, aitFalse); + if (pPV) { + this->postIOCompletion (pvCreateReturn(*pPV)); + } + else { + this->postIOCompletion (pvCreateReturn(S_casApp_noMemory)); + } +} + +// +// exAsyncCreateIO::name() +// +const char *exAsyncCreateIO::name() const +{ + return "exAsyncCreateIO"; +} + diff --git a/src/cas/example/simple/exServer.h b/src/cas/example/simple/exServer.h index 80cb2b10b..1cd21793f 100644 --- a/src/cas/example/simple/exServer.h +++ b/src/cas/example/simple/exServer.h @@ -26,37 +26,38 @@ // // EPICS // -#include -#include -#include -#include - -#ifndef max -#define max(A,B) ((A)<(B)?(B):(A)) -#endif - -#ifndef min -#define min(A,B) ((A)>(B)?(B):(A)) -#endif +#include "epicsAssert.h" +#include "casdef.h" +#include "gddAppFuncTable.h" +#include "osiTimer.h" +#include "resourceLib.h" +#include "minmax.h" #ifndef NELEMENTS # define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) #endif -#define LOCAL static +#define maxSimultAsyncIO 1000u // // info about all pv in this server // enum excasIoType {excasIoSync, excasIoAsync}; +class exPV; +class exServer; + +// +// pvInfo +// class pvInfo { public: - pvInfo (double scanRateIn, const char *pName, + pvInfo (double scanPeriodIn, const char *pNameIn, aitFloat32 hoprIn, aitFloat32 loprIn, excasIoType ioTypeIn, unsigned countIn) : - scanRate(scanRateIn), name(pName), hopr(hoprIn), - lopr(loprIn), ioType(ioTypeIn), elementCount(countIn) + scanPeriod(scanPeriodIn), pName(pNameIn), + hopr(hoprIn), lopr(loprIn), ioType(ioTypeIn), + elementCount(countIn), pPV(0) { } @@ -65,29 +66,64 @@ public: // for this class // pvInfo (const pvInfo ©In) : - scanRate(copyIn.scanRate), name(copyIn.name), + scanPeriod(copyIn.scanPeriod), pName(copyIn.pName), hopr(copyIn.hopr), lopr(copyIn.lopr), - ioType(copyIn.ioType), elementCount(copyIn.elementCount) + ioType(copyIn.ioType), elementCount(copyIn.elementCount), + pPV(copyIn.pPV) { } - const double getScanRate () const { return this->scanRate; } - const aitString &getName () const { return this->name; } + const double getScanPeriod () const { return this->scanPeriod; } + const char *getName () const { return this->pName; } const double getHopr () const { return this->hopr; } const double getLopr () const { return this->lopr; } const excasIoType getIOType () const { return this->ioType; } const unsigned getElementCount() const { return this->elementCount; } + void destroyPV() { this->pPV=NULL; } + exPV *createPV (exServer &exCAS, aitBool preCreateFlag); private: - const double scanRate; - const aitString name; + const double scanPeriod; + const char *pName; const double hopr; const double lopr; const excasIoType ioType; const unsigned elementCount; + exPV *pPV; }; -class exPV; +// +// pvEntry +// +// o entry in the string hash table for the pvInfo +// o Since there may be aliases then we may end up +// with several of this class all referencing +// the same pv info class (justification +// for this breaking out into a seperate class +// from pvInfo) +// +class pvEntry : public stringId, public tsSLNode { +public: + pvEntry (pvInfo &infoIn, exServer &casIn, const char *pAliasName) : + stringId(pAliasName), info(infoIn), cas(casIn) + { + assert(this->stringId::resourceName()!=NULL); + } + inline ~pvEntry(); + + pvInfo &getInfo() const { return this->info; } + + void destroy () + { + // + // always created with new + // + delete this; + } +private: + pvInfo &info; + exServer &cas; +}; // // exScanTimer @@ -107,13 +143,12 @@ private: // // exPV // -class exPV : public casPV { - +class exPV : public casPV, public tsSLNode { public: - exPV (const casCtx &ctxIn, const pvInfo &setup); + exPV (caServer &cas, pvInfo &setup, aitBool preCreateFlag); virtual ~exPV(); - void show(unsigned level); + void show(unsigned level) const; // // Called by the server libary each time that it wishes to @@ -170,20 +205,43 @@ public: // aitTimeStamp getTS(); - const float getScanRate() + // + // If no one is watching scan the PV with 10.0 + // times the specified period + // + const float getScanPeriod() { - return this->info.getScanRate(); + double curPeriod; + + curPeriod = this->info.getScanPeriod(); + if (!this->interest) { + curPeriod *= 10.0L; + } + return curPeriod; } caStatus read (const casCtx &, gdd &protoIn); caStatus write (const casCtx &, gdd &protoIn); + void destroy(); + + const pvInfo &getPVInfo() + { + return this->info; + } + + const char *getName() const + { + return this->info.getName(); + } + protected: gdd *pValue; exScanTimer *pScanTimer; - const pvInfo & info; + pvInfo & info; aitBool interest; + aitBool preCreate; static osiTime currentTime; virtual caStatus updateValue (gdd &value) = 0; @@ -194,8 +252,8 @@ protected: // class exScalarPV : public exPV { public: - exScalarPV (const casCtx &ctxIn, const pvInfo &setup) : - exPV (ctxIn, setup) {} + exScalarPV (caServer &cas, pvInfo &setup, aitBool preCreateFlag) : + exPV (cas, setup, preCreateFlag) {} void scan(); private: caStatus updateValue (gdd &value); @@ -206,8 +264,8 @@ private: // class exVectorPV : public exPV { public: - exVectorPV (const casCtx &ctxIn, const pvInfo &setup) : - exPV (ctxIn, setup) {} + exVectorPV (caServer &cas, pvInfo &setup, aitBool preCreateFlag) : + exPV (cas, setup, preCreateFlag) {} void scan(); unsigned maxDimension() const; @@ -222,20 +280,46 @@ private: // class exServer : public caServer { public: - exServer(unsigned pvMaxNameLength, unsigned pvCountEstimate=0x3ff, - unsigned maxSimultaneousIO=1u); - void show (unsigned level); - pvExistReturn pvExistTest (const casCtx &ctxIn, const char *pPVName); - casPV *createPV (const casCtx &ctxIn, const char *pPVName); + exServer(const char * const pvPrefix, unsigned aliasCount); + void show (unsigned level) const; + pvExistReturn pvExistTest (const casCtx&, const char *pPVName); + pvCreateReturn createPV (const casCtx &ctx, const char *pPVName); - static const pvInfo *findPV(const char *pName); + void installAliasName(pvInfo &info, const char *pAliasName); + inline void removeAliasName(pvEntry &entry); static gddAppFuncTableStatus read(exPV &pv, gdd &value) { return exServer::ft.read(pv, value); } + + // + // removeIO + // + void removeIO() + { + if (this->simultAsychIOCount>0u) { + this->simultAsychIOCount--; + } + else { + fprintf(stderr, "inconsistent simultAsychIOCount?\n"); + } + } private: - static const pvInfo pvList[]; + resTable stringResTbl; + unsigned simultAsychIOCount; + + // + // list of pre-created PVs + // + static pvInfo pvList[]; + + // + // on-the-fly PVs + // + static pvInfo bill; + static pvInfo billy; + static gddAppFuncTable ft; }; @@ -247,8 +331,9 @@ public: // // exAsyncPV() // - exAsyncPV (const casCtx &ctxIn, const pvInfo &setup) : - exScalarPV (ctxIn, setup) {} + exAsyncPV (caServer &cas, pvInfo &setup, aitBool preCreateFlag) : + exScalarPV (cas, setup, preCreateFlag), + simultAsychIOCount(0u) {} // // read @@ -260,8 +345,20 @@ public: // caStatus write(const casCtx &ctxIn, gdd &value); - unsigned maxSimultAsyncOps () const; + // + // removeIO + // + void removeIO() + { + if (this->simultAsychIOCount>0u) { + this->simultAsychIOCount--; + } + else { + fprintf(stderr, "inconsistent simultAsychIOCount?\n"); + } + } private: + unsigned simultAsychIOCount; }; // @@ -332,6 +429,7 @@ public: ~exAsyncWriteIO() { + this->pv.removeIO(); this->value.unreference(); } @@ -365,6 +463,7 @@ public: ~exAsyncReadIO() { + this->pv.removeIO(); this->proto.unreference(); } @@ -391,8 +490,14 @@ public: // // exAsyncExistIO() // - exAsyncExistIO(const pvInfo &pviIn, const casCtx &ctxIn) : - casAsyncPVExistIO(ctxIn), pvi(pviIn) {} + exAsyncExistIO(const pvInfo &pviIn, const casCtx &ctxIn, + exServer &casIn) : + casAsyncPVExistIO(ctxIn), pvi(pviIn), cas(casIn) {} + + ~exAsyncExistIO() + { + this->cas.removeIO(); + } // // expire() @@ -404,6 +509,56 @@ public: const char *name() const; private: const pvInfo &pvi; + exServer &cas; }; +// +// exAsyncCreateIO +// (PV create async IO) +// +class exAsyncCreateIO : public casAsyncPVCreateIO, public exOSITimer { +public: + // + // exAsyncCreateIO() + // + exAsyncCreateIO(pvInfo &pviIn, exServer &casIn, + const casCtx &ctxIn) : + casAsyncPVCreateIO(ctxIn), pvi(pviIn), cas(casIn) {} + + ~exAsyncCreateIO() + { + this->cas.removeIO(); + } + + // + // expire() + // (a virtual function that runs when the base timer expires) + // see exServer.cc + // + void expire(); + + const char *name() const; +private: + pvInfo &pvi; + exServer &cas; +}; + +// +// exServer::removeAliasName() +// +inline void exServer::removeAliasName(pvEntry &entry) +{ + pvEntry *pE; + pE = this->stringResTbl.remove(entry); + assert(pE = &entry); +} + +// +// pvEntry::~pvEntry() +// +inline pvEntry::~pvEntry() +{ + this->cas.removeAliasName(*this); +} + diff --git a/src/cas/example/simple/exVectorPV.cc b/src/cas/example/simple/exVectorPV.cc index d855635a6..b5ed0bc62 100644 --- a/src/cas/example/simple/exVectorPV.cc +++ b/src/cas/example/simple/exVectorPV.cc @@ -1,6 +1,6 @@ -#include -#include +#include "exServer.h" +#include "gddApps.h" #define myPI 3.14159265358979323846 diff --git a/src/cas/example/simple/main.cc b/src/cas/example/simple/main.cc index bd627254b..03370d83f 100644 --- a/src/cas/example/simple/main.cc +++ b/src/cas/example/simple/main.cc @@ -1,17 +1,19 @@ -#include -#include +#include "exServer.h" +#include "fdManager.h" // // main() // (example single threaded ca server tool main loop) // -int main (int argc, const char **argv) +extern int main (int argc, const char **argv) { osiTime begin(osiTime::getCurrent()); exServer *pCAS; unsigned debugLevel = 0u; float executionTime; + char pvPrefix[128] = ""; + unsigned aliasCount = 1u; aitBool forever = aitTrue; int i; @@ -23,12 +25,20 @@ int main (int argc, const char **argv) forever = aitFalse; continue; } - printf ("usage: %s -d -t\n", + if (sscanf(argv[i],"-p %127s", pvPrefix)==1) { + continue; + } + if (sscanf(argv[i],"-c %u", &aliasCount)==1) { + continue; + } + printf ( +"usage: %s [-d -t -p -c]\n", argv[0]); + return (1); } - pCAS = new exServer(32u,5u,500u); + pCAS = new exServer(pvPrefix, aliasCount); if (!pCAS) { return (-1); } @@ -56,6 +66,7 @@ int main (int argc, const char **argv) delay = osiTime::getCurrent() - begin; } } + pCAS->show(2u); delete pCAS; return (0); } diff --git a/src/cas/example/simple/templInst.cc b/src/cas/example/simple/templInst.cc index 88999c7be..0f3e1cad4 100644 --- a/src/cas/example/simple/templInst.cc +++ b/src/cas/example/simple/templInst.cc @@ -9,11 +9,13 @@ #include "exServer.h" #include "gddAppFuncTable.cc" +#include "resourceLib.cc" // -// Sun C++ 4.1 still appears to be lacking support in this area +// if the compiler supports explicit instantiation of +// template member functions // -#if !defined(__SUNPRO_CC) +#if defined(EXPL_TEMPL) // // From Stroustrups's "The C++ Programming Language" // Appendix A: r.14.9 @@ -22,5 +24,6 @@ // member functions into "templInst.o" // template class gddAppFuncTable ; + template class resTable ; #endif diff --git a/src/cas/example/simple/vxEntry.cc b/src/cas/example/simple/vxEntry.cc index a214fa7e0..2337be540 100644 --- a/src/cas/example/simple/vxEntry.cc +++ b/src/cas/example/simple/vxEntry.cc @@ -3,14 +3,19 @@ // Author: Jeff HIll (LANL) // // $Log$ +// Revision 1.1 1996/12/06 22:20:22 jhill +// moved down one level +// // Revision 1.2 1996/09/16 18:22:09 jhill // added cvs log entries // // -#include +#include #include +#include "exServer.h" + // // so we can call this from the vxWorks shell // diff --git a/src/cas/generic/caServer.cc b/src/cas/generic/caServer.cc index 2c4494bdf..768d4b76c 100644 --- a/src/cas/generic/caServer.cc +++ b/src/cas/generic/caServer.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.4 1996/11/02 00:53:53 jhill + * many improvements + * * Revision 1.3 1996/09/16 18:23:56 jhill * vxWorks port changes * @@ -41,26 +44,18 @@ * */ -#include -#include // caServerI in line func -#include // ait to dbr types -#include // EPICS application type table +#include "server.h" +#include "caServerIIL.h" // caServerI in line func +#include "dbMapper.h" // ait to dbr types +#include "gddAppTable.h" // EPICS application type table -// -// NOTES -// .01 All use of member pCAS in this file must first verify -// that we successfully created a caServerI in -// the constructor -// // // caServer::caServer() // -caServer::caServer(unsigned pvMaxNameLengthIn, unsigned pvCountEstimateIn, - unsigned maxSimultaneousIOIn) : - pCAS (new caServerI(*this, pvMaxNameLengthIn, - pvCountEstimateIn, maxSimultaneousIOIn)), +caServer::caServer(unsigned pvCountEstimateIn) : + pCAS (new caServerI(*this, pvCountEstimateIn)), valueEventMask(this->registerEvent("value")), logEventMask(this->registerEvent("log")), alarmEventMask(this->registerEvent("alarm")) @@ -112,7 +107,7 @@ casEventMask caServer::registerEvent (const char *pName) // // caServer::show() // -void caServer::show(unsigned level) +void caServer::show(unsigned level) const { if (this->pCAS) { this->pCAS->show(level); @@ -149,6 +144,8 @@ unsigned caServer::getDebugLevel () } } +// +// casRes::~casRes() // // This must be virtual so that derived destructor will // be run indirectly. Therefore it cannot be inline. diff --git a/src/cas/generic/caServerI.cc b/src/cas/generic/caServerI.cc index 51cb5b9bb..0ae2cb2a0 100644 --- a/src/cas/generic/caServerI.cc +++ b/src/cas/generic/caServerI.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.6 1996/11/02 00:53:54 jhill + * many improvements + * * Revision 1.5 1996/09/16 18:23:56 jhill * vxWorks port changes * @@ -50,8 +53,8 @@ #define CAS_VERSION_GLOBAL #define caServerGlobal -#include -#include // casCtx in line func +#include "server.h" +#include "casCtxIL.h" // casCtx in line func static const osiTime CAServerMaxBeaconPeriod (5.0 /* sec */); static const osiTime CAServerMinBeaconPeriod (1.0e-3 /* sec */); @@ -60,9 +63,8 @@ static const osiTime CAServerMinBeaconPeriod (1.0e-3 /* sec */); // // caServerI::show() // -void caServerI::show (unsigned level) +void caServerI::show (unsigned level) const { - casStrmClient *pClient; int bytes_reserved; printf( "Channel Access Server Status V%d.%d\n", @@ -71,16 +73,19 @@ void caServerI::show (unsigned level) this->osiMutex::show(level); this->osiLock(); - tsDLFwdIter iterCl(this->clientList); - while ( (pClient = iterCl.next()) ) { - pClient->show(level); + const tsDLIterBD eolSC; + tsDLIterBD iterCl(this->clientList.first()); + while ( iterCl!=eolSC ) { + iterCl->show(level); + ++iterCl; } this->dgClient.show(level); - casIntfOS *pIF; - tsDLFwdIter iterIF(this->intfList); - while ( (pIF = iterIF.next()) ) { - pIF->show(level); + const tsDLIterBD eolIOS; + tsDLIterBD iterIF(this->intfList.first()); + while ( iterIF!=eolIOS ) { + iterIF->show(level); + ++iterIF; } this->osiUnlock(); @@ -113,11 +118,6 @@ void caServerI::show (unsigned level) this->osiLock(); this->uintResTable::show(level); this->osiUnlock(); - printf( - "The server's character string resource id conversion table:\n"); - this->osiLock(); - this->stringResTbl.show(level); - this->osiUnlock(); } // @@@@@@ caPrintAddrList(&destAddr); @@ -129,8 +129,7 @@ void caServerI::show (unsigned level) // // caServerI::caServerI() // -caServerI::caServerI (caServer &tool, unsigned maxNameLength, - unsigned nPV, unsigned maxSimultIO) : +caServerI::caServerI (caServer &tool, unsigned nPV) : caServerOS(*this), casEventRegistry(* (osiMutex *) this), dgClient(*this), @@ -141,18 +140,14 @@ caServerI::caServerI (caServer &tool, unsigned maxNameLength, // beaconPeriod(CAServerMinBeaconPeriod), adapter(tool), - pvCount(0u), debugLevel(0u), - nExistTestInProg(0u), - pvMaxNameLength(maxNameLength), pvCountEstimate(nPV<100u?100u:nPV), - maxSimultaneousIO(maxSimultIO), haveBeenInitialized(FALSE) { caStatus status; assert(&adapter); - ctx.setServer(this); + //ctx.setServer(this); status = this->init(); if (status) { @@ -200,21 +195,12 @@ caStatus caServerI::init() // // hash table size may need adjustment here? // - resLibStatus = this->uintResTable::init(this->pvCountEstimate*8u); + resLibStatus = this->uintResTable::init(this->pvCountEstimate*2u); if (resLibStatus) { ca_printf("CAS: integer resource id table init failed\n"); return S_cas_noMemory; } - // - // hash table size may need adjustment here? - // - resLibStatus = this->stringResTbl.init(this->pvCountEstimate*2u); - if (resLibStatus) { - ca_printf("CAS: string resource id table init failed\n"); - return S_cas_noMemory; - } - this->haveBeenInitialized = TRUE; return S_cas_success; } @@ -232,13 +218,18 @@ caServerI::~caServerI() // // delete all clients // - casClient *pClient; - tsDLFwdIter iter(this->clientList); - pClient = iter.next(); - while (pClient) { - casClient *pNextClient = iter.next(); - delete pClient; - pClient = pNextClient; + tsDLIterBD iter(this->clientList.first()); + tsDLIterBD eol; + tsDLIterBD tmp; + while ( iter!=eol ) { + tmp = iter; + ++tmp; + // + // destructor takes client out of list + // + casStrmClient *pC = iter; + delete pC; + iter = tmp; } casIntfOS *pIF; @@ -247,11 +238,6 @@ caServerI::~caServerI() } this->osiUnlock(); - - // - // verify that we didnt leak a PV - // - assert (this->pvCount==0u); } @@ -380,8 +366,6 @@ caStatus caServerI::addAddr(const caAddr &caAddr, int autoBeaconAddr, // void caServerI::sendBeacon() { - casIntfOS *pIntf; - // // send a broadcast beacon over each configured // interface unless EPICS_CA_AUTO_ADDR_LIST specifies @@ -389,9 +373,10 @@ void caServerI::sendBeacon() // addresses. // this->osiLock(); - tsDLFwdIter iter(this->intfList); - while ( (pIntf = iter.next()) ) { - pIntf->requestBeacon(); + tsDLIterBD iter(this->intfList.first()); + while ( iter ) { + iter->requestBeacon(); + iter++; } this->osiUnlock(); diff --git a/src/cas/generic/caServerIIL.h b/src/cas/generic/caServerIIL.h index 8ef1d1b4e..b6e156c07 100644 --- a/src/cas/generic/caServerIIL.h +++ b/src/cas/generic/caServerIIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.3 1996/11/02 00:53:56 jhill + * many improvements + * * Revision 1.2 1996/09/16 18:23:57 jhill * vxWorks port changes * @@ -93,83 +96,13 @@ inline casChannelI *caServerI::resIdToChannel(const caResId &id) return (casChannelI *) pRes; } -// -// caServerI::pvExistTestPossible() -// -inline aitBool caServerI::pvExistTestPossible() -{ - if (this->nExistTestInProg < this->maxSimultaneousIO) { - return aitTrue; - } - return aitFalse; -} - // // find the channel associated with a resource id // -inline pvExistReturn caServerI::pvExistTest( +inline casPVExistReturn caServerI::pvExistTest( const casCtx &ctxIn, const char *pPVName) { - this->osiLock(); - if (pvExistTestPossible()) { - this->nExistTestInProg++; - osiUnlock(); - return (*this)->pvExistTest(ctxIn, pPVName); - } - else { - osiUnlock(); - return pvExistReturn(S_cas_ioBlocked); - } -} - -// -// caServerI::pvExistTestCompletion() -// -inline void caServerI::pvExistTestCompletion() -{ - this->osiLock(); - this->nExistTestInProg--; - this->osiUnlock(); - this->ioBlockedList::signal(); -} - - -// -// install a PV into the server -// -inline void caServerI::installPV (casPVI &pv) -{ - int resLibStatus; - - this->osiLock (); - this->pvCount++; - resLibStatus = this->stringResTbl.add (pv); - this->osiUnlock (); - assert (resLibStatus==0); -} - -// -// remove PV from the server -// -inline void caServerI::removePV(casPVI &pv) -{ - casPVI *pPV; - - this->osiLock(); - casVerify (this->pvCount>=1u); - this->pvCount--; - pPV = this->stringResTbl.remove (pv); - this->osiUnlock(); - casVerify (pPV!=0); - casVerify (pPV==&pv); -} - -// -// caServerI::getPVMaxNameLength() -// -inline unsigned caServerI::getPVMaxNameLength() const -{ - return this->pvMaxNameLength; + return casPVExistReturn((*this)->pvExistTest(ctxIn, pPVName)); } // diff --git a/src/cas/generic/casAsyncExIOI.cc b/src/cas/generic/casAsyncExIOI.cc index cacdac49b..0efb85048 100644 --- a/src/cas/generic/casAsyncExIOI.cc +++ b/src/cas/generic/casAsyncExIOI.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.2 1996/11/06 22:15:53 jhill + * allow monitor init read to using rd async io + * * Revision 1.1 1996/11/02 01:01:02 jhill * installed * @@ -37,12 +40,12 @@ */ -#include -#include // casAsyncIOI in line func -#include // casChannelI in line func -#include // casOpaqueAddr in line func -#include // casCtx in line func -#include // casCoreClient in line func +#include "server.h" +#include "casAsyncIOIIL.h" // casAsyncIOI in line func +#include "casChannelIIL.h" // casChannelI in line func +#include "casOpaqueAddrIL.h" // casOpaqueAddr in line func +#include "casCtxIL.h" // casCtx in line func +#include "casCoreClientIL.h" // casCoreClient in line func // // casAsyncExIOI::casAsyncExIOI() @@ -69,7 +72,7 @@ casAsyncExIOI::~casAsyncExIOI() // // casAsyncExIOI::postIOCompletion() // -caStatus casAsyncExIOI::postIOCompletion(const pvExistReturn &retValIn) +caStatus casAsyncExIOI::postIOCompletion(const pvExistReturn retValIn) { this->retVal = retValIn; return this->postIOCompletionI(); @@ -94,10 +97,6 @@ caStatus casAsyncExIOI::cbFuncAsyncIO() this->dgOutAddr.get(), this->msg, this->retVal); break; - case CA_PROTO_CLAIM_CIU: - status = this->client.createChanResponse(this->msg, this->retVal); - break; - default: this->reportInvalidAsynchIO(this->msg.m_cmmd); status = S_cas_internal; diff --git a/src/cas/generic/casAsyncIO.cc b/src/cas/generic/casAsyncIO.cc index fcabdc884..21f41b00a 100644 --- a/src/cas/generic/casAsyncIO.cc +++ b/src/cas/generic/casAsyncIO.cc @@ -29,13 +29,16 @@ * * History * $Log$ + * Revision 1.2 1996/11/02 00:53:57 jhill + * many improvements + * * Revision 1.1.1.1 1996/06/20 00:28:14 jhill * ca server installation * * */ -#include +#include"server.h" // // This must be virtual so that derived destructor will diff --git a/src/cas/generic/casAsyncIOI.cc b/src/cas/generic/casAsyncIOI.cc index cd12d65fb..9a0facb1a 100644 --- a/src/cas/generic/casAsyncIOI.cc +++ b/src/cas/generic/casAsyncIOI.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.5 1996/12/13 00:08:35 jhill + * dont unlock after destroy + * * Revision 1.4 1996/11/02 00:53:58 jhill * many improvements * @@ -36,10 +39,10 @@ */ -#include -#include // casEventSys in line func -#include // casAsyncIOI in line func -#include // casCtx in line func +#include "server.h" +#include "casEventSysIL.h" // casEventSys in line func +#include "casAsyncIOIIL.h" // casAsyncIOI in line func +#include "casCtxIL.h" // casCtx in line func // // casAsyncIOI::casAsyncIOI() @@ -52,6 +55,19 @@ casAsyncIOI::casAsyncIOI(casCoreClient &clientIn, casAsyncIO &ioExternalIn) : ioComplete(FALSE), serverDelete(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; + } } // @@ -165,6 +181,18 @@ caStatus casAsyncIOI::postIOCompletionI() { this->lock(); + if (this->duplicate) { + errMessage(S_cas_badParameter, + "- duplicate async IO"); + // + // dont use "this" after potentially destroying the + // object here + // + this->serverDelete = TRUE; + (*this)->destroy(); + return S_cas_redundantPost; + } + // // verify that they dont post completion more than once // @@ -194,7 +222,7 @@ caStatus casAsyncIOI::postIOCompletionI() // casAsyncIOI::getCAS() // (not inline because this is used by the interface class) // -caServer *casAsyncIOI::getCAS() +caServer *casAsyncIOI::getCAS() const { return this->client.getCAS().getAdapter(); } @@ -215,13 +243,13 @@ int casAsyncIOI::readOP() // void casAsyncIOI::destroyIfReadOP() { - casCoreClient &client = this->client; + casCoreClient &clientCopy = this->client; // // client lock used because this object's // lock may be destroyed // - client.osiLock(); + clientCopy.osiLock(); if (this->readOP()) { this->serverDelete = TRUE; @@ -232,7 +260,8 @@ void casAsyncIOI::destroyIfReadOP() // NO REF TO THIS OBJECT BELOW HERE // BECAUSE OF THE DELETE ABOVE // - client.osiUnlock(); + + clientCopy.osiUnlock(); } // diff --git a/src/cas/generic/casAsyncRdIOI.cc b/src/cas/generic/casAsyncRdIOI.cc index 7a0c817d0..d0b102cfd 100644 --- a/src/cas/generic/casAsyncRdIOI.cc +++ b/src/cas/generic/casAsyncRdIOI.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.2 1996/11/06 22:15:54 jhill + * allow monitor init read to using rd async io + * * Revision 1.1 1996/11/02 01:01:03 jhill * installed * @@ -45,10 +48,10 @@ */ -#include -#include // casAsyncIOI in line func -#include // casChannelI in line func -#include // casCtxI in line func +#include "server.h" +#include "casAsyncIOIIL.h" // casAsyncIOI in line func +#include "casChannelIIL.h" // casChannelI in line func +#include "casCtxIL.h" // casCtxI in line func // // casAsyncRdIOI::casAsyncRdIOI() diff --git a/src/cas/generic/casAsyncWtIOI.cc b/src/cas/generic/casAsyncWtIOI.cc index cb0fc4b80..bacb2cefa 100644 --- a/src/cas/generic/casAsyncWtIOI.cc +++ b/src/cas/generic/casAsyncWtIOI.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.2 1996/11/06 22:15:56 jhill + * allow monitor init read to using rd async io + * * Revision 1.1 1996/11/02 01:01:04 jhill * installed * @@ -36,10 +39,10 @@ */ -#include -#include // casAsyncIOI in line func -#include // casChannelI in line func -#include // casCtx in line func +#include "server.h" +#include "casAsyncIOIIL.h" // casAsyncIOI in line func +#include "casChannelIIL.h" // casChannelI in line func +#include "casCtxIL.h" // casCtx in line func // // casAsyncWtIOI::casAsyncWtIOI() diff --git a/src/cas/generic/casAsyncXXIO.cc b/src/cas/generic/casAsyncXXIO.cc index 2af260b30..404195ef2 100644 --- a/src/cas/generic/casAsyncXXIO.cc +++ b/src/cas/generic/casAsyncXXIO.cc @@ -29,11 +29,14 @@ * * History * $Log$ + * Revision 1.1 1996/11/02 01:01:05 jhill + * installed + * * * */ -#include +#include "casdef.h" // // This must be virtual so that derived destructor will @@ -59,3 +62,11 @@ casAsyncPVExistIO::~casAsyncPVExistIO() { } +// +// This must be virtual so that derived destructor will +// be run indirectly. Therefore it cannot be inline. +// +casAsyncPVCreateIO::~casAsyncPVCreateIO() +{ +} + diff --git a/src/cas/generic/casChanDelEv.cc b/src/cas/generic/casChanDelEv.cc index 75ae7f47e..8476f8740 100644 --- a/src/cas/generic/casChanDelEv.cc +++ b/src/cas/generic/casChanDelEv.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.2 1996/11/02 00:54:01 jhill + * many improvements + * * Revision 1.1.1.1 1996/06/20 00:28:15 jhill * ca server installation * @@ -36,8 +39,8 @@ */ -#include -#include // casEventSys in line func +#include "server.h" +#include "casEventSysIL.h" // casEventSys in line func // // casChanDelEv() diff --git a/src/cas/generic/casChannel.cc b/src/cas/generic/casChannel.cc index fcf335ecb..42f3a4328 100644 --- a/src/cas/generic/casChannel.cc +++ b/src/cas/generic/casChannel.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.4 1996/09/04 20:17:34 jhill + * use ptr not ref to satisfy MSVISC++ + * * Revision 1.3 1996/08/13 22:52:31 jhill * changes for MVC++ * @@ -42,9 +45,9 @@ */ -#include -#include // casChannelI inline func -#include // casPVListChan inline func +#include "server.h" +#include "casChannelIIL.h" // casChannelI inline func +#include "casPVListChanIL.h" // casPVListChan inline func // // casChannel::casChannel() @@ -73,14 +76,6 @@ casPV *casChannel::getPV() } } -// -// casChannel::postEvent() -// -void casChannel::postEvent (const casEventMask &select, gdd &event) -{ - this->casChannelI::postEvent(select, event); -} - // // casChannel::setOwner() // @@ -92,21 +87,6 @@ void casChannel::setOwner(const char * const /* pUserName */, // } -// -// casChannel::interestRegister() -// -caStatus casChannel::interestRegister() -{ - return S_casApp_success; -} - -// -// casChannel::interestDelete() -// -void casChannel::interestDelete() -{ -} - // // casChannel::readAccess() // @@ -135,7 +115,7 @@ aitBool casChannel::confirmationRequested() const // // casChannel::show() // -void casChannel::show(unsigned level) +void casChannel::show(unsigned level) const { if (level>2u) { printf("casChannel: read access = %d\n", @@ -155,4 +135,11 @@ void casChannel::destroy() delete this; } +// +// casChannel::postAccessRightsEvent() +// +void casChannel::postAccessRightsEvent() +{ + this->casChannelI::postAccessRightsEvent(); +} diff --git a/src/cas/generic/casChannelI.cc b/src/cas/generic/casChannelI.cc index 3b9f5767b..dc7c83a85 100644 --- a/src/cas/generic/casChannelI.cc +++ b/src/cas/generic/casChannelI.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.3 1996/11/02 00:54:02 jhill + * many improvements + * * Revision 1.2 1996/09/04 20:18:03 jhill * init new chan member * @@ -38,11 +41,11 @@ * */ -#include -#include // casEventSys inline func -#include // casAsyncIOI inline func -#include // casPVI inline func -#include // casCtx inline func +#include "server.h" +#include "casEventSysIL.h" // casEventSys inline func +#include "casAsyncIOIIL.h" // casAsyncIOI inline func +#include "casPVIIL.h" // casPVI inline func +#include "casCtxIL.h" // casCtx inline func // @@ -52,7 +55,9 @@ casChannelI::casChannelI(const casCtx &ctx, casChannel &chanAdapter) : client(* (casStrmClient *) ctx.getClient()), pv(*ctx.getPV()), chan(chanAdapter), - cid(ctx.getMsg()->m_cid) + cid(ctx.getMsg()->m_cid), + clientDestroyPending(FALSE), + accessRightsEvPending(FALSE) { assert(&this->client); assert(&this->pv); @@ -67,8 +72,6 @@ casChannelI::casChannelI(const casCtx &ctx, casChannel &chanAdapter) : // casChannelI::~casChannelI() { - casAsyncIOI *pIO; - casMonitor *pMonitor; casChanDelEv *pCDEV; caStatus status; @@ -77,31 +80,34 @@ casChannelI::~casChannelI() // // cancel any pending asynchronous IO // - tsDLFwdIter iterIO(this->ioInProgList); - pIO = iterIO.next(); - while (pIO) { - casAsyncIOI *pNextIO; + tsDLIterBD iterAIO(this->ioInProgList.first()); + const tsDLIterBD eolAIO; + tsDLIterBD tmpAIO; + while ( iterAIO!=eolAIO ) { // // destructor removes from this list // - pNextIO = iterIO.next(); - pIO->destroy(); - pIO = pNextIO; + tmpAIO = iterAIO; + ++tmpAIO; + iterAIO->destroy(); + iterAIO = tmpAIO; } // // cancel the monitors // - tsDLFwdIter iterMon(this->monitorList); - pMonitor = iterMon.next(); - while (pMonitor) { - casMonitor *pNextMon; + tsDLIterBD iterMon(this->monitorList.first()); + const tsDLIterBD eolMon; + tsDLIterBD tmpMon; + while ( iterMon!=eolMon ) { + casMonitor *pMonitor; // // destructor removes from this list // - pNextMon = iterMon.next(); + pMonitor = tmpMon = iterMon; + ++tmpMon; delete pMonitor; - pMonitor = pNextMon; + iterMon = tmpMon; } this->client.removeChannel(*this); @@ -112,9 +118,9 @@ casChannelI::~casChannelI() this->pv.deleteSignal(); // - // if its an old client and there is no memory - // we disconnect them here (and delete the client) - // - therefore dont use member client after this point + // If we are not in the process of deleting the client + // then inform the client that we have deleted its + // channel // if (!this->clientDestroyPending) { pCDEV = new casChanDelEv(this->getCID()); @@ -124,10 +130,19 @@ casChannelI::~casChannelI() else { status = this->client.disconnectChan (this->getCID()); if (status) { - errMessage(status, NULL); - if (status == S_cas_disconnect) { - this->client.destroy(); - } + // + // At this point there is no space in pool + // for a tiny object and there is also + // no outgoing buffer space in which to place + // a message in which we inform the client + // that his channel was deleted. + // + // => disconnect this client via the event + // queue because deleting the client here + // will result in bugs because no doubt this + // could be called by a client member function. + // + this->client.setDestroyPending(); } } } @@ -141,23 +156,22 @@ casChannelI::~casChannelI() // void casChannelI::clearOutstandingReads() { - casAsyncIOI *pIO; - this->lock(); // // cancel any pending asynchronous IO // - tsDLFwdIter iterIO(this->ioInProgList); - pIO = iterIO.next(); - while (pIO) { - casAsyncIOI *pNextIO; + tsDLIterBD iterIO(this->ioInProgList.first()); + tsDLIterBD eolIO; + tsDLIterBD tmp; + while (iterIO!=eolIO) { // // destructor removes from this list // - pNextIO = iterIO.next(); - pIO->destroyIfReadOP(); - pIO = pNextIO; + tmp = iterIO; + ++tmp; + iterIO->destroyIfReadOP(); + iterIO = tmp; } this->unlock(); @@ -169,12 +183,12 @@ void casChannelI::clearOutstandingReads() // void casChannelI::postAllModifiedEvents() { - casMonitor *pMon; - this->lock(); - tsDLFwdIter iter(this->monitorList); - while ( (pMon=iter.next()) ) { - pMon->postIfModified(); + tsDLIterBD iter(this->monitorList.first()); + tsDLIterBD eol; + while ( iter!=eol ) { + iter->postIfModified(); + ++iter; } this->unlock(); } @@ -183,21 +197,19 @@ void casChannelI::postAllModifiedEvents() // // casChannelI::show() // -void casChannelI::show(unsigned level) +void casChannelI::show(unsigned level) const { - casMonitor *pMon; - this->lock(); - tsDLFwdIter iter(this->monitorList); - if ( (pMon = iter.next()) ) { + tsDLIterBD iter(this->monitorList.first()); + tsDLIterBD eol; + if ( iter!=eol ) { printf("List of CA events (monitors) for \"%s\".\n", - this->pv.resourceName()); + this->pv->getName()); } - - while (pMon) { - pMon->show(level); - pMon = iter.next(); + while ( iter!=eol ) { + iter->show(level); + ++iter; } (*this)->show(level); @@ -205,3 +217,38 @@ void casChannelI::show(unsigned level) this->unlock(); } + +// +// casChannelI::cbFunc() +// +// access rights event call back +// +caStatus casChannelI::cbFunc(casEventSys &) +{ + caStatus stat; + + stat = this->client.accessRightsResponse(this); + if (stat==S_cas_success) { + this->accessRightsEvPending = FALSE; + } + return stat; +} + +// +// casChannelI::destroy() +// +// call the destroy in the server tool +// +void casChannelI::destroy() +{ + this->chan.destroy(); +} + +// +// casChannelI::resourceType() +// +casResType casChannelI::resourceType() const +{ + return casChanT; +} + diff --git a/src/cas/generic/casChannelIIL.h b/src/cas/generic/casChannelIIL.h index c860bd015..bde107958 100644 --- a/src/cas/generic/casChannelIIL.h +++ b/src/cas/generic/casChannelIIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.5 1996/11/02 00:54:03 jhill + * many improvements + * * Revision 1.4 1996/09/16 18:23:59 jhill * vxWorks port changes * @@ -48,12 +51,13 @@ #ifndef casChannelIIL_h #define casChannelIIL_h -#include +#include "casCoreClientIL.h" +#include "casEventSysIL.h" // // casChannelI::operator -> () // -inline casChannel * casChannelI::operator -> () +inline casChannel * casChannelI::operator -> () const { return &this->chan; } @@ -61,7 +65,7 @@ inline casChannel * casChannelI::operator -> () // // casChannelI::lock() // -inline void casChannelI::lock() +inline void casChannelI::lock() const { this->client.osiLock(); } @@ -69,7 +73,7 @@ inline void casChannelI::lock() // // casChannelI::unlock() // -inline void casChannelI::unlock() +inline void casChannelI::unlock() const { this->client.osiUnlock(); } @@ -79,12 +83,12 @@ inline void casChannelI::unlock() // inline void casChannelI::postEvent(const casEventMask &select, gdd &event) { - casMonitor *pMon; - this->lock(); - tsDLFwdIter iter(this->monitorList); - while ( (pMon = iter.next()) ) { - pMon->post(select, event); + tsDLIterBD iter(this->monitorList.first()); + const tsDLIterBD eol; + while ( iter!=eol ) { + iter->post(select, event); + ++iter; } this->unlock(); } @@ -124,14 +128,15 @@ inline void casChannelI::addMonitor(casMonitor &mon) // inline casMonitor *casChannelI::findMonitor(const caResId clientIdIn) { - casMonitor *pMon; - this->lock(); - tsDLFwdIter iter(this->monitorList); - while ( (pMon = iter.next()) ) { - if ( clientIdIn == pMon->getClientId()) { + tsDLIterBD iter(this->monitorList.first()); + tsDLIterBD eol; + while ( iter!=eol ) { + if ( clientIdIn == iter->getClientId()) { + casMonitor *pMon = iter; return pMon; } + ++iter; } this->unlock(); return NULL; @@ -146,7 +151,7 @@ inline void casChannelI::clientDestroy() (*this)->destroy(); } -#include +#include "casPVIIL.h" // // functions that use casPVIIL.h below here @@ -158,7 +163,6 @@ inline void casChannelI::clientDestroy() inline void casChannelI::installAsyncIO(casAsyncIOI &io) { this->lock(); - this->pv.registerIO(); this->ioInProgList.add(io); this->unlock(); } @@ -183,6 +187,17 @@ inline const caResId casChannelI::getSID() return this->uintId::getId(); } +// +// casChannelI::postAccessRightsEvent() +// +inline void casChannelI::postAccessRightsEvent() +{ + if (!this->accessRightsEvPending) { + this->accessRightsEvPending = TRUE; + this->client.addToEventQueue(*this); + } +} + #endif // casChannelIIL_h diff --git a/src/cas/generic/casClient.cc b/src/cas/generic/casClient.cc index 07b755ec3..1ba0249be 100644 --- a/src/cas/generic/casClient.cc +++ b/src/cas/generic/casClient.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.6 1996/12/11 00:58:35 jhill + * better diagnostic + * * Revision 1.5 1996/11/02 00:54:04 jhill * many improvements * @@ -47,13 +50,15 @@ * */ +#include -#include -#include // inline func for casClient -#include // inline func for casEventSys -#include // inline func for casCtx -#include // inline func for inBuf -#include +#include "server.h" +#include "casClientIL.h" // inline func for casClient +#include "casEventSysIL.h" // inline func for casEventSys +#include "casCtxIL.h" // inline func for casCtx +#include "inBufIL.h" // inline func for inBuf +#include "casPVIIL.h" // inline func for casPVI +#include "db_access.h" VERSIONID(camsgtaskc,"%W% %G%") @@ -124,59 +129,60 @@ void casClient::loadProtoJumpTable() // // Request Protocol Jump Table + // (use of & here is more portable) // casClient::msgHandlers[CA_PROTO_NOOP] = - casClient::noopAction; + &casClient::noopAction; casClient::msgHandlers[CA_PROTO_EVENT_ADD] = - casClient::eventAddAction; + &casClient::eventAddAction; casClient::msgHandlers[CA_PROTO_EVENT_CANCEL] = - casClient::eventCancelAction; + &casClient::eventCancelAction; casClient::msgHandlers[CA_PROTO_READ] = - casClient::readAction; + &casClient::readAction; casClient::msgHandlers[CA_PROTO_WRITE] = - casClient::writeAction; + &casClient::writeAction; casClient::msgHandlers[CA_PROTO_SNAPSHOT] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlers[CA_PROTO_SEARCH] = - casClient::searchAction; + &casClient::searchAction; casClient::msgHandlers[CA_PROTO_BUILD] = - casClient::ignoreMsgAction; + &casClient::ignoreMsgAction; casClient::msgHandlers[CA_PROTO_EVENTS_OFF] = - casClient::eventsOffAction; + &casClient::eventsOffAction; casClient::msgHandlers[CA_PROTO_EVENTS_ON] = - casClient::eventsOnAction; + &casClient::eventsOnAction; casClient::msgHandlers[CA_PROTO_READ_SYNC] = - casClient::readSyncAction; + &casClient::readSyncAction; casClient::msgHandlers[CA_PROTO_ERROR] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlers[CA_PROTO_CLEAR_CHANNEL] = - casClient::clearChannelAction; + &casClient::clearChannelAction; casClient::msgHandlers[CA_PROTO_RSRV_IS_UP] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlers[CA_PROTO_NOT_FOUND] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlers[CA_PROTO_READ_NOTIFY] = - casClient::readNotifyAction; + &casClient::readNotifyAction; casClient::msgHandlers[CA_PROTO_READ_BUILD] = - casClient::ignoreMsgAction; + &casClient::ignoreMsgAction; casClient::msgHandlers[REPEATER_CONFIRM] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlers[CA_PROTO_CLAIM_CIU] = - casClient::claimChannelAction; + &casClient::claimChannelAction; casClient::msgHandlers[CA_PROTO_WRITE_NOTIFY] = - casClient::writeNotifyAction; + &casClient::writeNotifyAction; casClient::msgHandlers[CA_PROTO_CLIENT_NAME] = - casClient::clientNameAction; + &casClient::clientNameAction; casClient::msgHandlers[CA_PROTO_HOST_NAME] = - casClient::hostNameAction; + &casClient::hostNameAction; casClient::msgHandlers[CA_PROTO_ACCESS_RIGHTS] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlers[CA_PROTO_ECHO] = - casClient::echoAction; + &casClient::echoAction; casClient::msgHandlers[REPEATER_REGISTER] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlers[CA_PROTO_CLAIM_CIU_FAILED] = - casClient::uknownMessageAction; + &casClient::uknownMessageAction; casClient::msgHandlersInit = TRUE; } @@ -552,7 +558,7 @@ void casClient::dumpMsg(const caHdr *mp, const void *dp) pciu = this->resIdToChannel(mp->m_cid); if (pciu) { - strncpy(pPVName, pciu->getPVI().resourceName(), sizeof(pPVName)); + strncpy(pPVName, pciu->getPVI()->getName(), sizeof(pPVName)); if (&pciu->getClient()!=this) { strncat(pPVName, "!Bad Client!", sizeof(pPVName)); } diff --git a/src/cas/generic/casClientIL.h b/src/cas/generic/casClientIL.h index 090890781..0a5d5eb17 100644 --- a/src/cas/generic/casClientIL.h +++ b/src/cas/generic/casClientIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.3 1996/12/11 00:59:37 jhill + * added bad chan attachment detection + * * Revision 1.2 1996/11/02 00:54:06 jhill * many improvements * @@ -42,8 +45,8 @@ #ifndef casClientIL_h #define casClientIL_h -#include // caServerI inline func -#include // caServerI inline func +#include "caServerIIL.h" // caServerI inline func +#include "casCtxIL.h" // caServerI inline func // // find the channel associated with a resource id @@ -56,6 +59,15 @@ inline casChannelI *casClient::resIdToChannel(const caResId &id) // look up the id in a hash table // pChan = this->ctx.getServer()->resIdToChannel(id); + + // + // update the context + // + this->ctx.setChannel(pChan); + if (!pChan) { + return NULL; + } + // // If the channel isnt attached to this client then // something has gone wrong @@ -63,13 +75,11 @@ inline casChannelI *casClient::resIdToChannel(const caResId &id) if (&pChan->getClient()!=this) { return NULL; } + // // update the context // - this->ctx.setChannel(pChan); - if (pChan) { - this->ctx.setPV(&pChan->getPVI()); - } + this->ctx.setPV(&pChan->getPVI()); return pChan; } diff --git a/src/cas/generic/casClientMon.cc b/src/cas/generic/casClientMon.cc index a3d5f1c22..bc7ee8b57 100644 --- a/src/cas/generic/casClientMon.cc +++ b/src/cas/generic/casClientMon.cc @@ -29,13 +29,16 @@ * * History * $Log$ + * Revision 1.1.1.1 1996/06/20 00:28:15 jhill + * ca server installation + * * */ -#include +#include "server.h" -#include +#include "casChannelIIL.h" // @@ -81,3 +84,22 @@ caStatus casClientMon::callBack(gdd &value) return status; } +// +// casClientMon::destroy() +// +// this class is always created inside the server +// lib with new +// +void casClientMon::destroy() +{ + delete this; +} + +// +// casClientMon::resourceType() +// +casResType casClientMon::resourceType() const +{ + return casClientMonT; +} + diff --git a/src/cas/generic/casCoreClient.cc b/src/cas/generic/casCoreClient.cc index 61de29543..64948b505 100644 --- a/src/cas/generic/casCoreClient.cc +++ b/src/cas/generic/casCoreClient.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.4 1996/11/02 00:54:07 jhill + * many improvements + * * Revision 1.3 1996/09/16 18:23:59 jhill * vxWorks port changes * @@ -42,13 +45,13 @@ */ -#include -#include // caServerI in line func -#include // casAsyncIOI in line func -#include // casEventSys in line func -#include // casCtx in line func -#include // inBuf in line func -#include // outBuf in line func +#include "server.h" +#include "caServerIIL.h" // caServerI in line func +#include "casAsyncIOIIL.h" // casAsyncIOI in line func +#include "casEventSysIL.h" // casEventSys in line func +#include "casCtxIL.h" // casCtx in line func +#include "inBufIL.h" // inBuf in line func +#include "outBufIL.h" // outBuf in line func // // casCoreClient::init() @@ -124,27 +127,26 @@ void casCoreClient::loadProtoJumpTable() // casCoreClient::~casCoreClient() { - casAsyncIOI *pCurIO; - if (this->ctx.getServer()->getDebugLevel()>0u) { ca_printf ("CAS: Connection Terminated\n"); } this->osiLock(); - tsDLFwdIter iterIO(this->ioInProgList); + tsDLIterBD iterIO(this->ioInProgList.first()); + tsDLIterBD tmpIO; + tsDLIterBD eolIO; // // cancel any pending asynchronous IO // - pCurIO = iterIO.next(); - while (pCurIO) { - casAsyncIOI *pNextIO; + while (iterIO!=eolIO) { // // destructor removes from this list // - pNextIO = iterIO.next(); - pCurIO->destroy(); - pCurIO = pNextIO; + tmpIO = iterIO; + ++tmpIO; + iterIO->destroy(); + iterIO = tmpIO; } this->osiUnlock(); @@ -180,11 +182,11 @@ void casCoreClient::show (unsigned level) const // asynchronous completion // caStatus casCoreClient::asyncSearchResponse(casDGIntfIO &, - const caAddr &, const caHdr &, const pvExistReturn &) + const caAddr &, const caHdr &, const pvExistReturn) { return S_casApp_noSupport; } -caStatus casCoreClient::createChanResponse(const caHdr &, const pvExistReturn &) +caStatus casCoreClient::createChanResponse(const caHdr &, const pvCreateReturn &) { return S_casApp_noSupport; } @@ -213,6 +215,10 @@ caStatus casCoreClient::monitorResponse(casChannelI *, const caHdr &, { return S_casApp_noSupport; } +caStatus casCoreClient::accessRightsResponse(casChannelI *) +{ + return S_casApp_noSupport; +} // // casCoreClient::installChannel() diff --git a/src/cas/generic/casCoreClientIL.h b/src/cas/generic/casCoreClientIL.h index e6c5b062b..19a330b3c 100644 --- a/src/cas/generic/casCoreClientIL.h +++ b/src/cas/generic/casCoreClientIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.1 1996/11/02 01:01:06 jhill + * installed + * * */ @@ -74,6 +77,7 @@ inline void casCoreClient::removeAsyncIO(casAsyncIOI &ioIn) { this->osiLock(); this->ioInProgList.remove(ioIn); + this->ctx.getServer()->ioBlockedList::signal(); this->osiUnlock(); } diff --git a/src/cas/generic/casCtx.cc b/src/cas/generic/casCtx.cc index 48660c59b..73d9d18fb 100644 --- a/src/cas/generic/casCtx.cc +++ b/src/cas/generic/casCtx.cc @@ -1,19 +1,19 @@ -#include +#include "server.h" // // casCtx::show() // void casCtx::show (unsigned level) const { - printf ("casCtx at %x\n", (unsigned) this); - if (level >= 1u) { - printf ("\tpMsg = %x\n", (unsigned) &this->msg); - printf ("\tpData = %x\n", (unsigned) pData); - printf ("\tpCAS = %x\n", (unsigned) pCAS); - printf ("\tpClient = %x\n", (unsigned) pClient); - printf ("\tpChannel = %x\n", (unsigned) pChannel); - printf ("\tpPV = %x\n", (unsigned) pPV); + printf ("casCtx at %lx\n", (unsigned long) this); + if (level >= 3u) { + printf ("\tpMsg = %lx\n", (unsigned long) &this->msg); + printf ("\tpData = %lx\n", (unsigned long) pData); + printf ("\tpCAS = %lx\n", (unsigned long) pCAS); + printf ("\tpClient = %lx\n", (unsigned long) pClient); + printf ("\tpChannel = %lx\n", (unsigned long) pChannel); + printf ("\tpPV = %lx\n", (unsigned long) pPV); } } diff --git a/src/cas/generic/casCtxIL.h b/src/cas/generic/casCtxIL.h index 7b360b092..99b61259c 100644 --- a/src/cas/generic/casCtxIL.h +++ b/src/cas/generic/casCtxIL.h @@ -7,7 +7,7 @@ // inline casCtx::casCtx() : pData(NULL), pCAS(NULL), pClient(NULL), - pChannel(NULL), pPV(NULL) + pChannel(NULL), pPV(NULL), nAsyncIO(0u) { memset(&this->msg, 0, sizeof(this->msg)); } diff --git a/src/cas/generic/casDGClient.cc b/src/cas/generic/casDGClient.cc index 57a8c315c..8b8f31fc7 100644 --- a/src/cas/generic/casDGClient.cc +++ b/src/cas/generic/casDGClient.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.8 1997/01/10 21:17:53 jhill + * code around gnu g++ inline bug when -O isnt used + * * Revision 1.7 1996/11/02 00:54:08 jhill * many improvements * @@ -53,14 +56,14 @@ * */ -#include -#include // caServerI inline func -#include // casClient inline func -#include // dgOutBuf inline func -#include // dgInBuf inline func -#include // casCtx inline func -#include // casCoreClient inline func -#include +#include "server.h" +#include "caServerIIL.h" // caServerI inline func +#include "casClientIL.h" // casClient inline func +#include "dgOutBufIL.h" // dgOutBuf inline func +#include "dgInBufIL.h" // dgInBuf inline func +#include "casCtxIL.h" // casCtx inline func +#include "casCoreClientIL.h" // casCoreClient inline func +#include "gddApps.h" // // CA Server Datagram (DG) Client @@ -124,32 +127,68 @@ caStatus casDGClient::searchAction() void *dp = this->ctx.getData(); const char *pChanName = (const char *) dp; caStatus status; + pvExistReturn pver; if (this->ctx.getServer()->getDebugLevel()>2u) { printf("client is searching for \"%s\"\n", pChanName); } + // + // verify that we have sufficent memory for a PV and a + // monitor prior to calling PV exist test so that when + // the server runs out of memory we dont reply to + // search requests, and therefore dont thrash through + // caServer::pvExistTest() and casCreatePV::createPV() + // +#ifdef vxWorks +# error code needs to be implemented here when we port +# error to memory limited environment +#endif + // // ask the server tool if this PV exists // - pvExistReturn retVal = + this->asyncIOFlag = 0u; + casPVExistReturn retVal = this->ctx.getServer()->pvExistTest(this->ctx, pChanName); - if (retVal.getStatus() == S_casApp_asyncCompletion) { - status = S_cas_success; - } - else if (retVal.getStatus()==S_cas_ioBlocked) { - // - // If too many exist test IO operations are in progress - // then we will just ignore this request (and wait for - // the client to try again later) - // - status = S_cas_success; - } - else { - status = this->searchResponse(*mp, retVal); + if (retVal.getStatus()!=S_cas_success) { + return retVal.getStatus(); } - return S_cas_success; + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + pver = retVal.getAppStat(); + if (this->asyncIOFlag) { + pver = pverAsyncCompletion; + } + else if (pver == pverAsyncCompletion) { + pver = pverDoesNotExistHere; + errMessage(S_cas_badParameter, + "- expected asynch IO creation from caServer::pvExistTest()"); + } + + // + // otherwise we assume sync IO operation was initiated + // + switch (pver) { + case pverDoesNotExistHere: + case pverAsyncCompletion: + status = S_cas_success; + break; + + case pverExistsHere: + status = this->searchResponse(*mp, retVal.getAppStat()); + break; + + default: + status = S_cas_badParameter; + break; + } + + return status; } @@ -157,18 +196,22 @@ caStatus casDGClient::searchAction() // caStatus casDGClient::searchResponse() // caStatus casDGClient::searchResponse(const caHdr &msg, - const pvExistReturn &retVal) + const pvExistReturn retVal) { caStatus status; caHdr *search_reply; unsigned short *pMinorVersion; - this->ctx.getServer()->pvExistTestCompletion(); - // // normal search failure is ignored // - if (retVal.getStatus()==S_casApp_pvNotFound) { + if (retVal==pverDoesNotExistHere) { + return S_cas_success; + } + + if (retVal!=pverExistsHere) { + fprintf(stderr, +"async exist completion with invalid return code \"pverAsynchCompletion\"?\n"); return S_cas_success; } @@ -199,29 +242,6 @@ caStatus casDGClient::searchResponse(const caHdr &msg, return status; } - // - // check for bad parameters - // - if (retVal.getStatus()) { - errMessage(retVal.getStatus(),NULL); - return S_cas_success; - } - if (retVal.getString()) { - if (retVal.getString()[0]=='\0') { - errMessage(S_cas_badParameter, - "PV name descr is empty"); - return S_cas_success; - } - if (this->ctx.getServer()->getDebugLevel()>2u) { - printf("Search request matched for PV=\"%s\"\n", - retVal.getString()); - } - } - else { - errMessage(S_cas_badParameter, "PV name descr is nill"); - return S_cas_success; - } - /* * obtain space for the reply message */ @@ -381,7 +401,7 @@ void casDGClient::processDG(casDGIntfIO &inMsgIO, casDGIntfIO &outMsgIO) // casDGClient::asyncSearchResp() // caStatus casDGClient::asyncSearchResponse(casDGIntfIO &outMsgIO, const caAddr &outAddr, - const caHdr &msg, const pvExistReturn &retVal) + const caHdr &msg, const pvExistReturn retVal) { caStatus stat; diff --git a/src/cas/generic/casEventMask.cc b/src/cas/generic/casEventMask.cc index 37a969e6e..cf9e51ce2 100644 --- a/src/cas/generic/casEventMask.cc +++ b/src/cas/generic/casEventMask.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.5 1996/12/11 01:01:56 jhill + * casEventMaskEntry constr does res tbl add + * * Revision 1.4 1996/12/06 22:32:11 jhill * force virtual destructor * @@ -45,11 +48,11 @@ */ -#include +#include "epicsAssert.h" #include #include -#include +#include "server.h" #ifdef TEST main () @@ -132,8 +135,12 @@ inline casEventMask casEventRegistry::maskAllocator() // casEventMask casEventRegistry::registerEvent(const char *pName) { + // + // NOTE: pName outlives id here + // (so the refString option is ok) + // + stringId id (pName, stringId::refString); casEventMaskEntry *pEntry; - stringId id (pName); casEventMask mask; if (!this->hasBeenInitialized) { @@ -171,7 +178,7 @@ casEventMask casEventRegistry::registerEvent(const char *pName) // // casEventMask::show() // -void casEventMask::show(unsigned level) +void casEventMask::show(unsigned level) const { if (level>0u) { printf ("casEventMask = %x\n", this->mask); @@ -186,7 +193,7 @@ casEventMask::casEventMask (casEventRegistry ®, const char *pName) // // casEventRegistry::show() // -void casEventRegistry::show(unsigned level) +void casEventRegistry::show(unsigned level) const { if (!this->hasBeenInitialized) { printf ("casEventRegistry: not initialized\n"); @@ -209,6 +216,7 @@ casEventMaskEntry::casEventMaskEntry( { int stat; + assert(this->resourceName()!=NULL); stat = this->reg.add(*this); assert(stat==0); } @@ -226,10 +234,18 @@ casEventMaskEntry::~casEventMaskEntry() this->reg.remove (*this); } +// +// casEventMaskEntry::destroy() +// +void casEventMaskEntry::destroy() +{ + delete this; +} + // // casEventMaskEntry::show() // -void casEventMaskEntry::show (unsigned level) +void casEventMaskEntry::show (unsigned level) const { this->casEventMask::show(level); this->stringId::show(level); diff --git a/src/cas/generic/casEventMask.h b/src/cas/generic/casEventMask.h index e47d74cbd..c0f1fbc1c 100644 --- a/src/cas/generic/casEventMask.h +++ b/src/cas/generic/casEventMask.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.4 1996/12/11 01:02:35 jhill + * removed casEventMaskEntry def + * * Revision 1.3 1996/12/06 22:32:10 jhill * force virtual destructor * @@ -72,7 +75,7 @@ public: this->clear(); } - void show (unsigned level); + void show (unsigned level) const; int eventsSelected() { diff --git a/src/cas/generic/casEventSys.cc b/src/cas/generic/casEventSys.cc index 871ad1c8a..649f42d6c 100644 --- a/src/cas/generic/casEventSys.cc +++ b/src/cas/generic/casEventSys.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.4 1996/11/02 00:54:12 jhill + * many improvements + * * Revision 1.3 1996/09/16 18:24:01 jhill * vxWorks port changes * @@ -49,8 +52,8 @@ /* * EPICS */ -#include -#include // casMonitor inline func +#include "server.h" +#include "casEventSysIL.h" // casMonitor inline func // // casEventSys::show() @@ -167,6 +170,19 @@ casProcCond casEventSys::process() this->mutex.osiUnlock(); + // + // allows the derived class to be informed that it + // needs to delete itself via the event system + // + // this gets the server out of nasty situations + // where the client needs to be deleted but + // the caller may be using the client's "this" + // pointer. + // + if (this->destroyPending) { + cond = casProcDisconnect; + } + return cond; } diff --git a/src/cas/generic/casEventSysIL.h b/src/cas/generic/casEventSysIL.h index ac5bd8e20..b1bde25d0 100644 --- a/src/cas/generic/casEventSysIL.h +++ b/src/cas/generic/casEventSysIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.3 1996/11/02 00:54:13 jhill + * many improvements + * * Revision 1.2 1996/09/16 18:24:02 jhill * vxWorks port changes * @@ -54,7 +57,8 @@ inline casEventSys::casEventSys (casCoreClient &coreClientIn) : coreClient(coreClientIn), numEventBlocks(0u), maxLogEntries(individualEventEntries), - eventsOff(aitFalse) + eventsOff(aitFalse), + destroyPending(aitFalse) { } @@ -115,6 +119,18 @@ void casEventSys::setEventsOff() this->eventsOff = aitTrue; } +// +// casEventSys::setDestroyPending() +// +void casEventSys::setDestroyPending() +{ + this->destroyPending = aitTrue; + // + // wakes up the event queue consumer + // + this->coreClient.eventSignal(); +} + // // casEventSys::insertEventQueue() // diff --git a/src/cas/generic/casInternal.h b/src/cas/generic/casInternal.h index f8f396833..5fd7fccae 100644 --- a/src/cas/generic/casInternal.h +++ b/src/cas/generic/casInternal.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.10 1997/01/10 21:17:55 jhill + * code around gnu g++ inline bug when -O isnt used + * * Revision 1.9 1996/12/06 22:33:49 jhill * virtual ~casPVI(), ~casPVListChan(), ~casChannelI() * @@ -96,7 +99,8 @@ class casRes : public uintRes public: virtual ~casRes(); virtual casResType resourceType() const = 0; - virtual void show (unsigned level) = 0; + virtual void show (unsigned level) const = 0; + virtual void destroy() = 0; private: }; @@ -136,7 +140,7 @@ class casMonitor; class casMonEvent : public casEvent { public: // - // only used when this part of another structure + // only used when this is part of another structure // (and we need to postpone true construction) // inline casMonEvent (); @@ -145,8 +149,9 @@ public: // // ~casMonEvent () + // (not inline because this is virtual in the base class) // - inline ~casMonEvent (); + ~casMonEvent (); caStatus cbFunc(casEventSys &); @@ -180,7 +185,7 @@ public: void post(const casEventMask &select, gdd &value); - virtual void show (unsigned level); + virtual void show (unsigned level) const; virtual caStatus callBack(gdd &value)=0; caResId getClientId() const @@ -260,6 +265,7 @@ class casAsyncIO; class casAsyncReadIO; class casAsyncWriteIO; class casAsyncPVExistIO; +class casAsyncPVCreateIO; class casAsyncIOI : public casEvent, public tsDLNode { public: @@ -279,7 +285,7 @@ public: void destroyIfReadOP(); - caServer *getCAS(); + caServer *getCAS() const; inline void destroy(); @@ -294,6 +300,7 @@ private: unsigned posted:1; unsigned ioComplete:1; unsigned serverDelete:1; + unsigned duplicate:1; // // casEvent virtual call back function // (called when IO completion event reaches top of event queue) @@ -411,7 +418,7 @@ public: // // place notification of IO completion on the event queue // - caStatus postIOCompletion(const pvExistReturn &retVal); + caStatus postIOCompletion(const pvExistReturn retVal); caStatus cbFuncAsyncIO(); casAsyncIO &getAsyncIO(); @@ -422,16 +429,43 @@ private: const casOpaqueAddr dgOutAddr; }; +// +// casAsyncPVCIOI +// +// (server internal asynchronous read IO class) +// +class casAsyncPVCIOI : public casAsyncIOI { +public: + casAsyncPVCIOI(const casCtx &ctx, casAsyncPVCreateIO &ioIn); + virtual ~casAsyncPVCIOI(); + + // + // place notification of IO completion on the event queue + // + caStatus postIOCompletion(const pvCreateReturn &retVal); + + caStatus cbFuncAsyncIO(); + casAsyncIO &getAsyncIO(); +private: + caHdr const msg; + pvCreateReturn retVal; +}; + class casChannel; class casPVI; -class casChannelI : public tsDLNode, public casRes { +// +// casChannelI +// +// this derives from casEvent so that access rights +// events can be posted +// +class casChannelI : public tsDLNode, public casRes, + public casEvent { public: casChannelI (const casCtx &ctx, casChannel &chanAdapter); virtual ~casChannelI(); - void show (unsigned level); - casCoreClient &getClient() const { return this->client; @@ -475,20 +509,27 @@ public: inline void postEvent (const casEventMask &select, gdd &event); - casResType resourceType() const - { - return casChanT; - } + virtual casResType resourceType() const; - inline void lock(); - inline void unlock(); + virtual void show (unsigned level) const; + + virtual void destroy(); + + inline void lock() const; + inline void unlock() const; inline void clientDestroy(); - inline casChannel * operator -> (); + inline casChannel * operator -> () const; void clearOutstandingReads(); + // + // access rights event call back + // + caStatus cbFunc(casEventSys &); + + inline void postAccessRightsEvent(); protected: tsDLList monitorList; tsDLList ioInProgList; @@ -497,6 +538,7 @@ protected: casChannel &chan; caResId const cid; // client id unsigned clientDestroyPending:1; + unsigned accessRightsEvPending:1; }; // @@ -514,23 +556,22 @@ class casCtx; class casChannel; class casPV; +// +// casPVI +// class casPVI : - public stringId, // server PV name table installation - public tsSLNode, // server PV name table installation + public tsSLNode, // server resource table installation + public casRes, // server resource table installation public ioBlockedList // list of clients io blocked on this pv { public: - // - // The PV name here must be the canonical and unique name - // for the PV in this system - // - casPVI (caServerI &cas, const char * const pNameIn, casPV &pvAdapter); + casPVI (caServer &cas, casPV &pvAdapter); virtual ~casPVI(); // // for use by the server library // - inline caServerI &getCAS(); + inline caServerI &getCAS() const; // // CA only does 1D arrays for now (and the new server @@ -547,7 +588,6 @@ public: // // only for use by casAsyncIOI // - inline void registerIO(); inline void unregisterIO(); // @@ -565,22 +605,25 @@ public: // inline void deleteSignal(); - void show(unsigned level); - inline void postEvent (const casEventMask &select, gdd &event); inline casPV *interfaceObjectPointer() const; - caServer *getExtServer(); + caServer *getExtServer() const; // // bestDBRType() // inline caStatus bestDBRType (unsigned &dbrType); - inline aitBool okToBeginNewIO() const; - inline casPV * operator -> () const; + + virtual casResType resourceType() const; + + virtual void show(unsigned level) const; + + virtual void destroy(); + private: tsDLList chanList; caServerI &cas; @@ -589,50 +632,7 @@ private: unsigned nIOAttached; unsigned destroyInProgress:1; - inline void lock(); - inline void unlock(); + inline void lock() const; + inline void unlock() const; }; -// -// inline functions associated with the return arg from -// caServer::pvExistTest() -// -inline pvExistReturn::pvExistReturn(caStatus status, - char* pCanonicalNameStr) : - stat(status), str(pCanonicalNameStr) -{ -} -inline pvExistReturn::pvExistReturn(caStatus status, - const char* pCanonicalNameStr) : - stat(status), str(pCanonicalNameStr) -{ -} -inline pvExistReturn::pvExistReturn(pvExistReturn &init) : - stat(init.stat), str(init.str) -{ -} -inline pvExistReturn::pvExistReturn(const pvExistReturn &init) : - stat(init.stat), str(init.str) -{ -} -inline const caStatus pvExistReturn::getStatus() const -{ - return this->stat; -} -inline const char* pvExistReturn::getString() const -{ - return str.string(); -} -inline pvExistReturn& pvExistReturn::operator=(pvExistReturn &rhs) -{ - this->stat = rhs.stat; - this->str = rhs.str; - return *this; -} -inline pvExistReturn& pvExistReturn::operator=(const pvExistReturn &rhs) -{ - this->stat = rhs.stat; - this->str = rhs.str; - return *this; -} - diff --git a/src/cas/generic/casMonEvent.cc b/src/cas/generic/casMonEvent.cc index 351625d7b..2688efa9f 100644 --- a/src/cas/generic/casMonEvent.cc +++ b/src/cas/generic/casMonEvent.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.3 1996/11/02 00:54:16 jhill + * many improvements + * * Revision 1.2 1996/06/26 21:18:55 jhill * now matches gdd api revisions * @@ -39,10 +42,10 @@ */ -#include -#include // casEventSys in line func -#include // casMonEvent in line func -#include // casCtx in line func +#include "server.h" +#include "casEventSysIL.h" // casEventSys in line func +#include "casMonEventIL.h" // casMonEvent in line func +#include "casCtxIL.h" // casCtx in line func // // casMonEvent::cbFunc() @@ -91,4 +94,12 @@ void casMonEvent::assign (casMonitor &monitor, gdd *pValueIn) this->id = monitor.casRes::getId(); } +// +// ~casMonEvent () +// (this is not in line because it is virtual in the base) +// +casMonEvent::~casMonEvent () +{ + this->clear(); +} diff --git a/src/cas/generic/casMonEventIL.h b/src/cas/generic/casMonEventIL.h index 5b2600709..d4eff2cb3 100644 --- a/src/cas/generic/casMonEventIL.h +++ b/src/cas/generic/casMonEventIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.2 1996/11/02 00:54:17 jhill + * many improvements + * * Revision 1.1.1.1 1996/06/20 00:28:16 jhill * ca server installation * @@ -39,12 +42,6 @@ #ifndef casMonEventIL_h #define casMonEventIL_h -// -// All functions here moved to casMonEvent.cc until -// I can determine what is causing them to end up -// undefined -// - // // casMonEvent::casMonEvent() // @@ -109,14 +106,6 @@ inline void casMonEvent::clear() this->id = 0u; } -// -// ~casMonEvent () -// -inline casMonEvent::~casMonEvent () -{ - this->clear(); -} - // // casMonEvent::getValue() // diff --git a/src/cas/generic/casMonitor.cc b/src/cas/generic/casMonitor.cc index 14b9d3df7..1643815b3 100644 --- a/src/cas/generic/casMonitor.cc +++ b/src/cas/generic/casMonitor.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.6 1996/11/02 00:54:18 jhill + * many improvements + * * Revision 1.5 1996/09/16 18:24:03 jhill * vxWorks port changes * @@ -48,11 +51,11 @@ */ -#include -#include // casChannelI inline func -#include // casEventSys inline func -#include // casMonEvent inline func -#include // casCtx inline func +#include "server.h" +#include "casChannelIIL.h" // casChannelI inline func +#include "casEventSysIL.h" // casEventSys inline func +#include "casMonEventIL.h" // casMonEvent inline func +#include "casCtxIL.h" // casCtx inline func // @@ -269,12 +272,13 @@ caStatus casMonitor::executeEvent(casMonEvent *pEV) // // casMonitor::show(unsigned level) // -void casMonitor::show(unsigned level) +void casMonitor::show(unsigned level) const { - if (level>0u) { + if (level>1u) { printf( "\tmonitor type=%u count=%lu client id=%u enabled=%u OVF=%u nPend=%u\n", dbrType, nElem, clientId, enabled, ovf, nPend); + this->mask.show(level); } } diff --git a/src/cas/generic/casMsgIO.cc b/src/cas/generic/casMsgIO.cc index 6966a8002..30ce06f40 100644 --- a/src/cas/generic/casMsgIO.cc +++ b/src/cas/generic/casMsgIO.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.3 1996/11/02 00:54:19 jhill + * many improvements + * * Revision 1.2 1996/09/16 18:24:04 jhill * vxWorks port changes * @@ -41,7 +44,7 @@ -#include +#include"server.h" class casMsgIO { public: diff --git a/src/cas/generic/casOpaqueAddr.cc b/src/cas/generic/casOpaqueAddr.cc index 1af1811fa..d227d1d79 100644 --- a/src/cas/generic/casOpaqueAddr.cc +++ b/src/cas/generic/casOpaqueAddr.cc @@ -1,5 +1,5 @@ -#include +#include "casdef.h" // // this needs to be here (and not in casOpaqueAddrIL.h) if we diff --git a/src/cas/generic/casPV.cc b/src/cas/generic/casPV.cc index 11926db6f..2bdf78d6d 100644 --- a/src/cas/generic/casPV.cc +++ b/src/cas/generic/casPV.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.5 1996/12/06 22:34:22 jhill + * add maxDimension() and maxBound() + * * Revision 1.4 1996/11/02 00:54:21 jhill * many improvements * @@ -51,14 +54,13 @@ // 1) Always verify that pPVI isnt nill prior to using it // -#include -#include // casPVI inline func -#include // casCtx inline func +#include "server.h" +#include "casPVIIL.h" // casPVI inline func +#include "casCtxIL.h" // casCtx inline func -casPV::casPV (const casCtx &ctx, const char * const pPVName) : - casPVI (*ctx.getServer(), pPVName, *this) +casPV::casPV (caServer &casIn) : + casPVI (casIn, *this) { - } casPV::~casPV() @@ -68,24 +70,14 @@ casPV::~casPV() // // casPV::show() // -void casPV::show(unsigned level) +void casPV::show(unsigned level) const { if (level>2u) { - printf ("casPV: Max simultaneous async io = %d\n", - this->maxSimultAsyncOps()); printf ("casPV: Best external type = %d\n", this->bestExternalType()); } } -// -// casPV::maxSimultAsyncOps() -// -unsigned casPV::maxSimultAsyncOps() const -{ - return 1u; -} - // // casPV::interestRegister() // @@ -192,7 +184,7 @@ void casPV::postEvent (const casEventMask &select, gdd &event) // for virtual casPV::destroy() // *************** // -caServer *casPV::getCAS() +caServer *casPV::getCAS() const { return this->casPVI::getExtServer(); } diff --git a/src/cas/generic/casPVI.cc b/src/cas/generic/casPVI.cc index de6ac07c6..289e83bf1 100644 --- a/src/cas/generic/casPVI.cc +++ b/src/cas/generic/casPVI.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.7 1996/12/12 18:55:38 jhill + * fixed client initiated pv delete calls interestDelete() VF bug + * * Revision 1.6 1996/12/06 22:35:06 jhill * added destroyInProgress flag * @@ -59,18 +62,25 @@ // // casPVI::casPVI() // -casPVI::casPVI(caServerI &casIn, const char * const pNameIn, - casPV &pvAdapterIn) : - stringId(pNameIn), - cas(casIn), +casPVI::casPVI(caServer &casIn, casPV &pvAdapterIn) : + cas(*casIn.pCAS), pv(pvAdapterIn), nMonAttached(0u), nIOAttached(0u), destroyInProgress(FALSE) { + // + // we would like to throw an exception for this one + // (but this is not portable) + // assert(&this->cas); + + // + // this will always be true + // assert(&this->pv); - this->cas.installPV(*this); + + this->cas.installItem(*this); } @@ -79,29 +89,29 @@ casPVI::casPVI(caServerI &casIn, const char * const pNameIn, // casPVI::~casPVI() { - casPVListChan *pChan; - casPVListChan *pNextChan; - this->lock(); + this->cas.removeItem(*this); + assert(!this->destroyInProgress); this->destroyInProgress = TRUE; // // delete any attached channels // - tsDLFwdIter iter(this->chanList); - pChan = iter.next(); - while (pChan) { + tsDLIterBD iter(this->chanList.first()); + const tsDLIterBD eol; + tsDLIterBD tmp; + while (iter!=eol) { // // deleting the channel removes it from the list // - pNextChan = iter.next(); - (*pChan)->destroy(); - pChan = pNextChan; - } - this->cas.removePV(*this); + tmp = iter; + ++tmp; + (*iter)->destroy(); + iter = tmp; + } this->unlock(); @@ -122,12 +132,14 @@ casPVI::~casPVI() // // casPVI::show() // -void casPVI::show(unsigned level) +void casPVI::show(unsigned level) const { this->lock(); - printf("\"%s\"\n", this->resourceName()); - + if (level>1u) { + printf ("CA Server PV: nChanAttached=%u nMonAttached=%u nIOAttached=%u\n", + this->chanList.count(), this->nMonAttached, this->nIOAttached); + } (*this)->show(level); this->unlock(); @@ -174,9 +186,25 @@ void casPVI::unregisterEvent() // (not inline because details of caServerI must not // leak into server tool) // -caServer *casPVI::getExtServer() +caServer *casPVI::getExtServer() const { return this->cas.getAdapter(); } +// +// casPVI::destroy() +// +// call the destroy in the server tool +// +void casPVI::destroy() +{ + this->pv.destroy(); +} + +// casPVI::resourceType() +// +casResType casPVI::resourceType() const +{ + return casPVT; +} diff --git a/src/cas/generic/casPVIIL.h b/src/cas/generic/casPVIIL.h index 1d0aefa5b..4545273f5 100644 --- a/src/cas/generic/casPVIIL.h +++ b/src/cas/generic/casPVIIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.9 1997/01/09 22:22:30 jhill + * MSC cannot use the default constructor + * * Revision 1.8 1996/12/06 22:36:17 jhill * use destroyInProgress flag now functional nativeCount() * @@ -68,7 +71,7 @@ // // casPVI::getCAS() // -inline caServerI &casPVI::getCAS() +inline caServerI &casPVI::getCAS() const { return this->cas; } @@ -95,7 +98,7 @@ casPV * casPVI::operator -> () const // // casPVI::lock() // -inline void casPVI::lock() +inline void casPVI::lock() const { // // NOTE: @@ -109,7 +112,7 @@ inline void casPVI::lock() // // casPVI::unlock() // -inline void casPVI::unlock() +inline void casPVI::unlock() const { this->cas.osiUnlock(); } @@ -134,39 +137,11 @@ inline void casPVI::removeChannel(casPVListChan &chan) this->unlock(); } -// -// okToBeginNewIO() -// -inline aitBool casPVI::okToBeginNewIO() const -{ - if (this->nIOAttached >= (*this)->maxSimultAsyncOps()) { - return aitFalse; - } - else { - return aitTrue; - } -} - -// -// casPVI::registerIO() -// -inline void casPVI::registerIO() -{ - this->lock(); - casVerify (this->nIOAttached < (*this)->maxSimultAsyncOps()); - this->nIOAttached++; - this->unlock(); -} - // // casPVI::unregisterIO() // inline void casPVI::unregisterIO() { - this->lock(); - assert(this->nIOAttached>0u); - this->nIOAttached--; - this->unlock(); this->ioBlockedList::signal(); } @@ -216,7 +191,7 @@ inline caStatus casPVI::bestDBRType (unsigned &dbrType) return S_cas_success; } -#include // inline func for casChannelI +#include "casChannelIIL.h" // inline func for casChannelI // // functions that use casChannelIIL.h below here @@ -227,8 +202,6 @@ inline caStatus casPVI::bestDBRType (unsigned &dbrType) // inline void casPVI::postEvent (const casEventMask &select, gdd &event) { - casPVListChan *pChan; - if (this->nMonAttached==0u) { return; } @@ -240,9 +213,11 @@ inline void casPVI::postEvent (const casEventMask &select, gdd &event) event.markConstant(); this->lock(); - tsDLFwdIter iter(this->chanList); - while ( (pChan = iter.next()) ) { - pChan->postEvent(select, event); + tsDLIterBD iter(this->chanList.first()); + const tsDLIterBD eol; + while ( iter != eol ) { + iter->postEvent(select, event); + ++iter; } this->unlock(); } diff --git a/src/cas/generic/casPVListChan.cc b/src/cas/generic/casPVListChan.cc index 0f5d7de27..bf68427bc 100644 --- a/src/cas/generic/casPVListChan.cc +++ b/src/cas/generic/casPVListChan.cc @@ -30,6 +30,9 @@ * * History * $Log$ + * Revision 1.2 1997/01/10 21:17:58 jhill + * code around gnu g++ inline bug when -O isnt used + * * Revision 1.1 1996/12/06 22:36:18 jhill * use destroyInProgress flag now functional nativeCount() * @@ -37,8 +40,8 @@ * */ -#include -#include +#include "server.h" +#include "casPVIIL.h" // // this needs to be here (and not in dgInBufIL.h) if we diff --git a/src/cas/generic/casPVListChanIL.h b/src/cas/generic/casPVListChanIL.h index 161d46b0f..166d10917 100644 --- a/src/cas/generic/casPVListChanIL.h +++ b/src/cas/generic/casPVListChanIL.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.4 1997/01/10 21:17:59 jhill + * code around gnu g++ inline bug when -O isnt used + * * Revision 1.3 1997/01/09 22:29:17 jhill * installed hostBuild branch * @@ -45,7 +48,7 @@ #ifndef casPVListChanIL_h #define casPVListChanIL_h -#include +#include "casPVIIL.h" // // empty for now since casPVListChan::casPVListChan() diff --git a/src/cas/generic/casStrmClient.cc b/src/cas/generic/casStrmClient.cc index a39882515..1ca9d3d4f 100644 --- a/src/cas/generic/casStrmClient.cc +++ b/src/cas/generic/casStrmClient.cc @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.14 1996/12/12 18:56:27 jhill + * doc + * * Revision 1.13 1996/12/11 01:03:52 jhill * removed redundant bad client attach detect * @@ -111,58 +114,13 @@ caStatus casStrmClient::verifyRequest (casChannelI *&pChan) // // element count out of range ? // - if (mp->m_count > pChan->getPVI().nativeCount()) { + if (mp->m_count > pChan->getPVI().nativeCount() || mp->m_count==0u) { return this->sendErr(mp, ECA_BADCOUNT, NULL); } - // - // If too many IO operations are in progress against this pv - // then we will need to wait until later to initiate this - // request. - // - if (pChan->getPVI().okToBeginNewIO()!=aitTrue) { - pChan->getPVI().addItemToIOBLockedList(*this); - return S_cas_ioBlocked; - } - else { - return S_cas_validRequest; - } + return S_cas_validRequest; } - -// -// casStrmClient::createChannel() -// -inline caStatus casStrmClient::createChannel (const char *pName) -{ - caStatus status; - - /* - * Verify that the server still has this channel - * and that we have it's correct official name. - * I assume that we may have found this server by - * contacting a name resolution service so we need to - * verify that the specified channel still exists here. - */ - pvExistReturn retVal = - this->ctx.getServer()->pvExistTest(this->ctx, pName); - if (retVal.getStatus() == S_casApp_asyncCompletion) { - status = S_cas_success; - } - else if (retVal.getStatus() == S_cas_ioBlocked) { - this->ctx.getServer()->addItemToIOBLockedList(*this); - status = S_cas_ioBlocked; - } - else { - // - // All use of the string in retVal below ends up getting - // copied (not referenced) so we are ok if they wrap - // pName through into retVal - // - status = this->createChanResponse (*this->ctx.getMsg(), retVal); - } - return status; -} @@ -222,9 +180,6 @@ caStatus casStrmClient::init() // casStrmClient::~casStrmClient() { - casChannelI *pChan; - casChannelI *pNextChan; - this->osiLock(); // @@ -243,15 +198,17 @@ casStrmClient::~casStrmClient() // // delete all channel attached // - tsDLFwdIter iter(this->chanList); - pChan = iter.next(); - while (pChan) { + tsDLIterBD iter(this->chanList.first()); + const tsDLIterBD eol; + tsDLIterBD tmp; + while (iter!=eol) { // // destroying the channel removes it from the list // - pNextChan = iter(); - pChan->clientDestroy(); - pChan = pNextChan; + tmp = iter; + ++tmp; + iter->clientDestroy(); + iter = tmp; } this->osiUnlock(); @@ -322,12 +279,15 @@ caStatus casStrmClient::readAction () pDesc = NULL; status = this->read(pDesc); - if (status==S_casApp_success && pDesc) { + if (status==S_casApp_success) { status = this->readResponse(pChan, *mp, pDesc, S_cas_success); } else if (status == S_casApp_asyncCompletion) { status = S_cas_success; } + else if (status == S_casApp_postponeAsyncIO) { + pChan->getPVI().addItemToIOBLockedList(*this); + } else { status = this->sendErrWithEpicsStatus(mp, status, ECA_GETFAIL); } @@ -429,9 +389,15 @@ caStatus casStrmClient::readNotifyAction () pDesc = NULL; status = this->read(pDesc); - if (status == S_casApp_asyncCompletion) { + if (status == S_casApp_success) { + status = this->readNotifyResponse(pChan, *mp, pDesc, status); + } + else if (status == S_casApp_asyncCompletion) { status = S_cas_success; } + else if (status == S_casApp_postponeAsyncIO) { + pChan->getPVI().addItemToIOBLockedList(*this); + } else { status = this->readNotifyResponse(pChan, *mp, pDesc, status); } @@ -540,20 +506,13 @@ caStatus casStrmClient::monitorResponse (casChannelI *pChan, const caHdr &msg, gdd *pDesc, const caStatus completionStatus) { caStatus completionStatusCopy = completionStatus; + gdd *pDBRDD = NULL; caHdr *pReply; unsigned size; caStatus status; int strcnt; - gdd *pDBRDD; gddStatus gdds; - // - // verify read access - // - if (!(*pChan)->readAccess()) { - completionStatusCopy = S_cas_noRead; - } - size = dbr_size_n (msg.m_type, msg.m_count); status = this->allocMsg(size, &pReply); if (status) { @@ -564,7 +523,7 @@ caStatus casStrmClient::monitorResponse (casChannelI *pChan, // instead // status = sendErr(&msg, ECA_TOLARGE, - "unable to xmit enevt info"); + "unable to xmit event"); } return status; } @@ -575,18 +534,22 @@ caStatus casStrmClient::monitorResponse (casChannelI *pChan, *pReply = msg; pReply->m_postsize = size; + // + // verify read access + // + if (!(*pChan)->readAccess()) { + completionStatusCopy = S_cas_noRead; + } + // // cid field abused to store the status here // if (completionStatusCopy == S_cas_success) { if (pDesc) { - status = createDBRDD(msg.m_type, msg.m_count, pDBRDD); - if (status != S_cas_success) { - pDBRDD = NULL; - completionStatusCopy = status; - } - else { + completionStatusCopy = createDBRDD(msg.m_type, + msg.m_count, pDBRDD); + if (completionStatusCopy==S_cas_success) { gdds = gddApplicationTypeTable:: app_table.smartCopy(pDBRDD, pDesc); if (gdds) { @@ -627,10 +590,14 @@ caStatus casStrmClient::monitorResponse (casChannelI *pChan, } } else { - errMessage(completionStatusCopy, "monitor response"); + errMessage(completionStatusCopy, "- in monitor response"); + if (completionStatusCopy== S_cas_noRead) { pReply->m_cid = ECA_NORDACCESS; } + else if (completionStatusCopy==S_cas_noMemory) { + pReply->m_cid = ECA_ALLOCMEM; + } else { pReply->m_cid = ECA_GETFAIL; } @@ -688,11 +655,11 @@ caStatus casStrmClient::writeAction() // initiate the write operation // status = this->write(); - if (status==S_casApp_success) { + if (status==S_casApp_success || status == S_casApp_asyncCompletion) { status = S_cas_success; } - else if (status == S_casApp_asyncCompletion) { - status = S_cas_success; + else if (status==S_casApp_postponeAsyncIO) { + pChan->getPVI().addItemToIOBLockedList(*this); } else { status = this->sendErrWithEpicsStatus(mp, status, ECA_PUTFAIL); @@ -760,6 +727,9 @@ caStatus casStrmClient::writeNotifyAction() if (status == S_casApp_asyncCompletion) { status = S_cas_success; } + else if (status==S_casApp_postponeAsyncIO) { + pChan->getPVI().addItemToIOBLockedList(*this); + } else { status = casStrmClient::writeNotifyResponse(pChan, *mp, status); @@ -811,7 +781,6 @@ caStatus casStrmClient::hostNameAction() { const caHdr *mp = this->ctx.getMsg(); char *pName = (char *) this->ctx.getData(); - casChannelI *pciu; unsigned size; char *pMalloc; @@ -837,9 +806,11 @@ caStatus casStrmClient::hostNameAction() } this->pHostName = pMalloc; - tsDLFwdIter iter(this->chanList); - while ( (pciu = iter.next()) ) { - (*pciu)->setOwner(this->pUserName, this->pHostName); + tsDLIterBD iter(this->chanList.first()); + const tsDLIterBD eol; + while ( iter!=eol ) { + (*iter)->setOwner(this->pUserName, this->pHostName); + ++iter; } this->osiUnlock(); @@ -855,7 +826,6 @@ caStatus casStrmClient::clientNameAction() { const caHdr *mp = this->ctx.getMsg(); char *pName = (char *) this->ctx.getData(); - casChannelI *pciu; unsigned size; char *pMalloc; @@ -882,9 +852,11 @@ caStatus casStrmClient::clientNameAction() } this->pUserName = pMalloc; - tsDLFwdIter iter(this->chanList); - while ( (pciu = iter.next()) ) { - (*pciu)->setOwner(this->pUserName, this->pHostName); + tsDLIterBD iter(this->chanList.first()); + const tsDLIterBD eol; + while ( iter!=eol ) { + (*iter)->setOwner(this->pUserName, this->pHostName); + ++iter; } this->osiUnlock(); @@ -900,8 +872,8 @@ caStatus casStrmClient::claimChannelAction() { const caHdr *mp = this->ctx.getMsg(); char *pName = (char *) this->ctx.getData(); - int status; unsigned nameLength; + caStatus status; /* * The available field is used (abused) @@ -922,7 +894,7 @@ caStatus casStrmClient::claimChannelAction() // new API was added to the server (they must // now use clients at EPICS 3.12 or higher) // - status = this->sendErr(mp, ECA_DEFUNCT, + this->sendErr(mp, ECA_DEFUNCT, "R3.11 connect sequence from old client was ignored"); return S_cas_badProtocol; // disconnect client } @@ -941,13 +913,146 @@ caStatus casStrmClient::claimChannelAction() return S_cas_badProtocol; // disconnect client } - status = this->createChannel (pName); - if (status == S_casApp_asyncCompletion) { - // - // asynchronous completion - // - status = S_cas_success; + // + // prevent problems such as the PV being deleted before the + // channel references it + // + this->osiLock(); + this->asyncIOFlag = 0u; + const pvCreateReturn pvcr = + (*this->ctx.getServer())->createPV (this->ctx, pName); + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + if (this->asyncIOFlag) { + status = S_cas_success; } + else if (pvcr.getStatus() == S_casApp_asyncCompletion) { + status = this->createChanResponse(*mp, + pvCreateReturn(S_cas_badParameter)); + errMessage(S_cas_badParameter, + "- expected asynch IO creation from caServer::createPV()"); + } + else if (pvcr.getStatus() == S_casApp_postponeAsyncIO) { + status = S_casApp_postponeAsyncIO; + this->ctx.getServer()->addItemToIOBLockedList(*this); + } + else { + status = this->createChanResponse(*mp, pvcr); + } + this->osiUnlock(); + return status; +} + +// +// casStrmClient::createChanResponse() +// +// LOCK must be applied +// +caStatus casStrmClient::createChanResponse(const caHdr &hdr, const pvCreateReturn &pvcr) +{ + casPVI *pPV; + casChannel *pChan; + casChannelI *pChanI; + caHdr *claim_reply; + caHdr *dummy; + unsigned dbrType; + caStatus status; + + if (pvcr.getStatus() != S_cas_success) { + return this->channelCreateFailed(&hdr, pvcr.getStatus()); + } + + pPV = pvcr.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); + } + + // + // NOTE: + // We are allocating enough space for both the claim + // response and the access response so that we know for + // certain that they will both be sent together. + // + status = this->allocMsg (sizeof(caHdr), &dummy); + if (status) { + return status; + } + + // + // create server tool XXX derived from casChannel + // + this->ctx.setPV(pPV); + pChan = (*pPV)->createChannel(this->ctx, + this->pUserName, this->pHostName); + if (!pChan) { + pvcr.getPV()->deleteSignal(); + return this->channelCreateFailed(&hdr, S_cas_noMemory); + } + + this->osiUnlock(); + + pChanI = (casChannelI *) pChan; + + // + // NOTE: + // 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) { + errMessage(status, "incompplete channel create?"); + pChanI->clientDestroy(); + return this->channelCreateFailed(&hdr, status); + } + + status = pPV->bestDBRType(dbrType); + if (status) { + errMessage(status, "best external dbr type fetch failed"); + pChanI->clientDestroy(); + return this->channelCreateFailed(&hdr, status); + } + + // + // NOTE: + // We are allocated enough space for both the claim + // response and the access response so that we know for + // certain that they will both be sent together. + // Nevertheles, some (old) clients do not receive + // an access rights response so we allocate again + // here to be certain that we are at the correct place in + // the protocol buffer. + // + // The 3rd arg indicates - dont lock the buffer again + // + status = this->allocMsg (0u, &claim_reply, FALSE); + // + // Not sending the access rights response and the claim + // response is a severe error which is avoided by + // the first (oversize) allocMsg + // + assert (status==S_cas_success); + + *claim_reply = nill_msg; + claim_reply->m_cmmd = CA_PROTO_CLAIM_CIU; + claim_reply->m_type = dbrType; + claim_reply->m_count = pPV->nativeCount(); + claim_reply->m_cid = hdr.m_cid; + claim_reply->m_available = pChanI->getSID(); + + // + // Unlock the buffer (and convert it to network format + // + this->commitMsg(); + return status; } @@ -965,7 +1070,15 @@ caStatus createStatus) caStatus status; caHdr *reply; - errMessage (createStatus, "- Server unable to create a new PV"); + if (createStatus == S_casApp_asyncCompletion) { + errMessage(S_cas_badParameter, + "- no asynchronous IO create in createPV() ?"); + errMessage(S_cas_badParameter, + "- or S_casApp_asyncCompletion was async IO competion code ?"); + } + else { + errMessage (createStatus, "- Server unable to create a new PV"); + } if (CA_V46(CA_PROTOCOL_VERSION,this->minor_version_number)) { status = allocMsg (0u, &reply); @@ -1012,7 +1125,7 @@ caStatus casStrmClient::disconnectChan(caResId id) } else { ca_printf( -"Disconnecting old client when server tool deleted its channel or PV\n"); +"Disconnecting old client because of internal channel or PV delete\n"); createStatus = S_cas_disconnect; } @@ -1026,8 +1139,6 @@ caStatus casStrmClient::disconnectChan(caResId id) // caStatus casStrmClient::eventsOnAction () { - casChannelI *pciu; - this->setEventsOn(); // @@ -1035,9 +1146,11 @@ caStatus casStrmClient::eventsOnAction () // should be a queue of modified events // this->osiLock(); - tsDLFwdIter iter(this->chanList); - while ( (pciu = iter.next()) ) { - pciu->postAllModifiedEvents(); + tsDLIterBD iter(this->chanList.first()); + const tsDLIterBD eol; + while ( iter!=eol ) { + iter->postAllModifiedEvents(); + ++iter; } this->osiUnlock(); @@ -1069,15 +1182,9 @@ caStatus casStrmClient::eventAddAction () caStatus status; casEventMask mask; - pciu = this->resIdToChannel(mp->m_cid); - if (!pciu) { - logBadId(mp, (void *) pMonInfo); - return S_cas_internal; - } - - if (mp->m_count==0u) { - this->sendErr(mp, ECA_BADCOUNT, "event add request"); - return S_cas_success; + status = casStrmClient::verifyRequest (pciu); + if (status != S_cas_validRequest) { + return status; } // @@ -1102,21 +1209,38 @@ caStatus casStrmClient::eventAddAction () return S_cas_success; } - pMonitor = new casClientMon(*pciu, mp->m_available, - mp->m_count, mp->m_type, mask, *this); - if (!pMonitor) { - this->sendErr(mp, ECA_ALLOCMEM, NULL); - return S_cas_internal; - } - // - // always send immediate monitor response at event add + // Attempt to read the first monitored value prior to creating + // the monitor object so that if the server tool chooses + // to postpone asynchronous IO we can safely restart this + // request later. // pDD = NULL; status = this->read(pDD); - if (status == S_casApp_asyncCompletion) { + // + // always send immediate monitor response at event add + // + if (status == S_casApp_success) { + status = this->monitorResponse (pciu, *mp, pDD, status); + } + else if (status == S_casApp_asyncCompletion) { status = S_cas_success; } + else if (status == S_casApp_postponeAsyncIO) { + // + // try again later + // + pciu->getPVI().addItemToIOBLockedList(*this); + } + else if (status == S_casApp_noMemory) { + // + // If we cant send the first monitor value because + // there isnt pool space for a gdd then delete + // (disconnect) the channel + // + (*pciu)->destroy(); + return S_cas_success; + } else { status = this->monitorResponse (pciu, *mp, pDD, status); } @@ -1127,6 +1251,21 @@ caStatus casStrmClient::eventAddAction () assert(gddStatus==0); } + if (status==S_cas_success) { + + pMonitor = new casClientMon(*pciu, mp->m_available, + mp->m_count, mp->m_type, mask, *this); + if (!pMonitor) { + this->sendErr(mp, ECA_ALLOCMEM, NULL); + // + // If we cant allocate space for a monitor then + // delete (disconnect) the channel + // + (*pciu)->destroy(); + status = S_cas_success; + } + } + return status; } @@ -1283,7 +1422,6 @@ caStatus casStrmClient::readSyncAction() const caHdr *mp = this->ctx.getMsg(); int status; caHdr *reply; - casChannelI *pChan; // // This messages indicates that the client @@ -1292,9 +1430,11 @@ caStatus casStrmClient::readSyncAction() // a read. // this->osiLock(); - tsDLFwdIter iter(this->chanList); - while ( (pChan = iter.next()) ) { - pChan->clearOutstandingReads(); + tsDLIterBD iter(this->chanList.first()); + const tsDLIterBD eol; + while ( iter!=eol ) { + iter->clearOutstandingReads(); + ++iter; } this->osiUnlock(); @@ -1381,6 +1521,11 @@ caStatus casStrmClient::write() return status; } + // + // clear async IO flag + // + this->asyncIOFlag = 0u; + // // DBR_STRING is stored outside the DD so it // lumped in with arrays @@ -1392,6 +1537,25 @@ caStatus casStrmClient::write() status = this->writeScalarData(); } + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + if (this->asyncIOFlag) { + if (status!=S_casApp_asyncCompletion) { + fprintf(stderr, +"Application returned %d from casPV::write() - 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::write()"); + } + (*pPV)->endTransaction(); return status; @@ -1544,7 +1708,9 @@ caStatus casStrmClient::read(gdd *&pDescRet) return status; } - assert(pDescRet); + if (!pDescRet) { + return S_cas_noMemory; + } // // the PV state must not be modified during a transaction @@ -1554,17 +1720,38 @@ caStatus casStrmClient::read(gdd *&pDescRet) return status; } + // + // clear the async IO flag + // + this->asyncIOFlag = 0u; + // // call the server tool's virtual function // status = (*this->ctx.getPV())->read(this->ctx, *pDescRet); + + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + 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()"); + } + if (status) { pDescRet->unreference(); pDescRet = NULL; - if (status!=S_casApp_asyncCompletion) { - errMessage(status, - " - response from server tool\n"); - } } (*this->ctx.getPV())->endTransaction(); @@ -1695,161 +1882,6 @@ const char *casStrmClient::hostName() const return this->pHostName?this->pHostName:"?"; } - -// -// casStrmClient::createChanResponse() -// -caStatus casStrmClient::createChanResponse(const caHdr &msg, - const pvExistReturn &retVal) -{ - casChannel *pChan; - casChannelI *pChanI; - casPVI *pPV; - caHdr *claim_reply; - caHdr *dummy; - unsigned dbrType; - caStatus status; - - this->ctx.getServer()->pvExistTestCompletion(); - - if (retVal.getStatus()) { - return this->channelCreateFailed(&msg, retVal.getStatus()); - } - - if (!retVal.getString()) { - return this->channelCreateFailed(&msg, S_cas_badParameter); - } - - if (retVal.getString()[0]=='\0') { - return this->channelCreateFailed(&msg, S_cas_badParameter); - } - - // - // NOTE: - // We are allocating enough space for both the claim - // response and the access response so that we know for - // certain that they will both be sent together. - // - status = this->allocMsg (sizeof(caHdr), &dummy); - if (status) { - return status; - } - - // - // prevent problems such as the PV being deleted before the - // channel references it - // - this->osiLock(); - - pPV = this->ctx.getServer()->createPV(retVal.getString()); - if (!pPV) { - this->osiUnlock(); - return this->channelCreateFailed(&msg, S_cas_noMemory); - } - - // - // create server tool XXX derived from casChannel - // - this->ctx.setPV(pPV); - pChan = (*pPV)->createChannel(this->ctx, - this->pUserName, this->pHostName); - if (!pChan) { - this->osiUnlock(); - pPV->deleteSignal(); - return this->channelCreateFailed(&msg, S_cas_noMemory); - } - - this->osiUnlock(); - - pChanI = (casChannelI *) pChan; - - // - // NOTE: - // 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) { - errMessage(status, "incompplete channel create?"); - pChanI->clientDestroy(); - return this->channelCreateFailed(&msg, status); - } - - status = pPV->bestDBRType(dbrType); - if (status) { - errMessage(status, "best external dbr type fetch failed"); - pChanI->clientDestroy(); - return this->channelCreateFailed(&msg, status); - } - - // - // NOTE: - // We are allocated enough space for both the claim - // response and the access response so that we know for - // certain that they will both be sent together. - // Nevertheles, some (old) clients do not receive - // an access rights response so we allocate again - // here to be certain that we are at the correct place in - // the protocol buffer. - // - status = this->allocMsg (sizeof(caHdr), &claim_reply, - FALSE /* dont lock the out buffer again*/); - // - // Not sending the access rights response and the claim - // response is a severe error which is avoided by - // the first (oversize) allocMsg - // - assert (status==S_cas_success); - - *claim_reply = nill_msg; - claim_reply->m_cmmd = CA_PROTO_CLAIM_CIU; - claim_reply->m_type = dbrType; - claim_reply->m_count = pPV->nativeCount(); - claim_reply->m_cid = msg.m_cid; - claim_reply->m_available = pChanI->getSID(); - - // - // Unlock the buffer (and convert it to network format - // - this->commitMsg(); - - return status; -} - -// -// caServerI::createPV () -// -casPVI *caServerI::createPV (const char *pName) -{ - casPVI *pPVI; - - // - // create resource id - // (this does a malloc to obtain space for the string) - // - stringId id (pName); - - this->osiLock (); - - pPVI = this->stringResTbl.lookup (id); - if (!pPVI) { - casPV *pPV; - pPV = (*this)->createPV (this->ctx, pName); - if (pPV) { - pPVI = (casPVI *) pPV; - } - } - - // - // lock shouldnt be released until we finish creating and - // installing the PV - // - this->osiUnlock (); - - return pPVI; -} - // // caServerI::roomForNewChannel() // diff --git a/src/cas/generic/casdef.h b/src/cas/generic/casdef.h index 06f1ebe4b..847a7dd20 100644 --- a/src/cas/generic/casdef.h +++ b/src/cas/generic/casdef.h @@ -30,6 +30,9 @@ * Modification Log: * ----------------- * $Log$ + * Revision 1.11 1997/01/09 22:24:46 jhill + * eliminate MSVC++ warning resulting from passing *this to a base + * * Revision 1.10 1996/12/06 22:36:26 jhill * use destroyInProgress flag now functional nativeCount() * @@ -72,9 +75,6 @@ * .08 Need a mechanism by which an error detail string can be returned * to the server from a server app (in addition to the normal * error constant) - * .10 Need a new env var in which the app specifies a set of interfaces - * to use - or perhaps just a list of networks that we will accept - * clients from. * .12 Should the server have an interface so that all PV names * can be obtained (even ones created after init)? This * would be used to implement update of directory services and @@ -134,7 +134,6 @@ typedef aitUint32 caStatus; #define S_cas_badElementCount (M_cas | 6) /*Bad element count*/ #define S_cas_noConvert (M_cas | 7) /*No conversion between src & dest types*/ #define S_cas_badWriteType (M_cas | 8) /*Src type inappropriate for write*/ -#define S_cas_ioBlocked (M_cas | 9) /*Blocked for io completion*/ #define S_cas_partialMessage (M_cas | 10) /*Partial message*/ #define S_cas_noContext (M_cas | 11) /*Context parameter is required*/ #define S_cas_disconnect (M_cas | 12) /*Lost connection to server*/ @@ -167,40 +166,40 @@ typedef aitUint32 caStatus; #define S_casApp_pvNotFound (M_casApp | 2) /*PV not found*/ #define S_casApp_badPVId (M_casApp | 3) /*Unknown PV identifier*/ #define S_casApp_noSupport (M_casApp | 4) /*No application support for op*/ -#define S_casApp_asyncCompletion (M_casApp | 5) /*Operation will complete asynchronously*/ +#define S_casApp_asyncCompletion (M_casApp | 5) /*will complete asynchronously*/ #define S_casApp_badDimension (M_casApp | 6) /*bad matrix size in request*/ #define S_casApp_canceledAsyncIO (M_casApp | 7) /*asynchronous io canceled*/ #define S_casApp_outOfBounds (M_casApp | 8) /*operation was out of bounds*/ #define S_casApp_undefined (M_casApp | 9) /*undefined value*/ - +#define S_casApp_postponeAsyncIO (M_casApp | 10) /*postpone asynchronous IO*/ // -// pvExistReturn -// Only for use with caServer::pvExistTest() +// pv exist test return // -class pvExistReturn { +// If the server tool does not wish to start another simultaneous +// asynchronous IO operation or if there is not enough memory +// to do so return pverDoesNotExistHere (and the client will +// retry the request later). +// +enum pvExistReturn {pverExistsHere, pverDoesNotExistHere, + pverAsyncCompletion}; + +class casPV; + +class pvCreateReturn { public: - // - // for use by the server tool when creating a return value - // - inline pvExistReturn(caStatus status=S_casApp_pvNotFound, - const char* pCanonicalNameStr=0); - inline pvExistReturn(caStatus status, char* pCanonicalNameStr); - // - // for use by the server lib when extracting the result - // - inline const caStatus getStatus() const; - inline const char *getString() const; - // - // these make certain that copy and assignment are correct - // - inline pvExistReturn(pvExistReturn &init); - inline pvExistReturn(const pvExistReturn &init); - inline pvExistReturn& operator=(pvExistReturn &rhs); - inline pvExistReturn& operator=(const pvExistReturn &rhs); -private: - caStatus stat; - aitString str; + pvCreateReturn() + { this->pPV = NULL; this->stat = S_cas_badParameter; } + pvCreateReturn(caStatus statIn) + { this->pPV = NULL; this->stat = statIn; } + pvCreateReturn(casPV &pv) + { this->pPV = &pv; this->stat = S_casApp_success; } + const caStatus getStatus() const { return this->stat; } + casPV *getPV() const { return this->pPV; } + +private: + casPV *pPV; + caStatus stat; }; #include "casEventMask.h" // EPICS event select class @@ -221,10 +220,9 @@ private: // We do not use private inheritance here in order // to avoid os/io dependent -I during server tool compile // - caServerI *pCAS; + caServerI *pCAS; public: - caServer (unsigned pvMaxNameLength, unsigned pvCountEstimate=0x3ff, - unsigned maxSimultaneousIO=1u); + caServer (unsigned pvCountEstimate=1024u); virtual ~caServer(); //caStatus enableClients (); @@ -238,46 +236,64 @@ public: // // show() // - virtual void show (unsigned level); + virtual void show (unsigned level) const; // // pvExistTest() // + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). + // // The server tool is encouraged to accept multiple PV name - // aliases for the same PV here. However, one unique canonical name - // must be selected by the server tool and returned to the - // server lib for each PV. The server will use this canonical - // name to prevent internal duplication of data structures for - // process variables that have multiple aliases. + // aliases for the same PV here. // - // o returns S_casApp_success and a valid canonical name string - // when the PV is in this server tool + // example return from this procedure: + // return pverExistsHere; // server has PV + // return pverDoesNotExistHere; // server does know of this PV + // return pverAsynchCompletion; // deferred result + // return pverNoMemoryForAsyncOP; // unable to defer result // - // o returns S_casApp_pvNotFound if the PV does not exist in - // the server tool - // - // Examples: - // caServerXXX::pvExistTest(const casCtx &ctx, const char *pPVName) - // { - // return pvExistReturn(S_casApp_success, pPVName); // common - // return pvExistReturn(S_casApp_pvNotFound); // no PV by that name - // - // const char *pConstName = "myPVName"; - // char pName[9] = "myPVName"; - // return pvExistReturn(S_casApp_success, pConstName); // efficient - // return pvExistReturn(S_casApp_asyncCompletion); // not now - // } + // Return S_casApp_postponeAsyncIO if too many simultaneous + // asynchronous IO operations are pending aginst the server. + // The server library will retry the request whenever an + // asynchronous IO operation (create or exist) completes + // against the server. // virtual pvExistReturn pvExistTest (const casCtx &ctx, - const char *pPVName)=0; + const char *pPVAliasName) = 0; // - // createPV() is called each time that a PV is attached to - // by a client for the first time. The server tool must create - // a casPV object (or a derived class) each time that this - // routine is called - // - virtual casPV *createPV (const casCtx &ctx, const char *pPVName)=0; + // createPV() is called _every_ time that a PV is attached to + // by a client. The name supplied here may be a PV canonical + // (base) name or it may instead be a PV alias name. + // + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). + // + // IMPORTANT: + // o It is the responsability of the server tool + // to detect attempts by the server lib to create a 2nd PV with + // the same name as an existing PV. It is also the responsability + // of the server tool to detect attempts by the server lib to + // create a 2nd PV with a name that is an alias of an existing PV. + // In these situations the server tool should avoid PV duplication + // by returning a pointer to an existing PV (and not create a new + // PV). + // + // example return from this procedure: + // return pvCreateReturn(*pPV); // success + // return pvCreateReturn(S_casApp_pvNotFound); // no PV by that name here + // return pvCreateReturn(S_casApp_noMemory); // no resource to create pv + // return pvCreateReturn(S_casApp_asyncCompletion); // deferred completion + // + // Return S_casApp_postponeAsyncIO if too many simultaneous + // asynchronous IO operations are pending aginst the server. + // The server library will retry the request whenever an + // asynchronous IO operation (create or exist) completes + // against the server. + // + virtual pvCreateReturn createPV (const casCtx &ctx, + const char *pPVAliasName) = 0; // // common event masks @@ -286,6 +302,12 @@ public: const casEventMask valueEventMask; // DBE_VALUE const casEventMask logEventMask; // DBE_LOG const casEventMask alarmEventMask; // DBE_ALARM + + // + // this is only used by casPVI::casPVI() too convert from + // caServer to a caServerI + // + friend class casPVI; }; // @@ -294,19 +316,26 @@ public: // Deletion Responsibility // -------- -------------- // o the server lib will not call "delete" directly for any -// casPV created by the server tool because we dont know -// that "new" was called to create the object +// casPV because we dont know that "new" was called to create +// the object // o The server tool is responsible for reclaiming storage for any // casPV it creates. The destroy() virtual function will // assist the server tool with this responsibility. The // virtual function casPV::destroy() does a "delete this". +// o The virtual function "destroy()" is called by the server lib +// each time that the last client attachment to the PV object is removed. // o The destructor for this object will cancel any // client attachment to this PV (and reclaim any resources // allocated by the server library on its behalf) // +// NOTE: if the server tool precreates the PV during initialization +// then it may decide to provide a "destroy()" implementation in the +// derived class which is a noop. +// class casPV : private casPVI { public: - casPV (const casCtx &ctx, const char * const pPVName); + casPV (caServer &cas); + virtual ~casPV (); // @@ -314,13 +343,7 @@ public: // caServer::show() is called and the level is high // enough // - virtual void show (unsigned level); - - // - // The maximum number of simultaneous asynchronous IO operations - // allowed for this PV - // - virtual unsigned maxSimultAsyncOps () const; + virtual void show (unsigned level) const; // // Called by the server libary each time that it wishes to @@ -356,21 +379,33 @@ public: // // read // - // asychronous completion iis allowed + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). // // RULE: if this completes asynchronously and the server tool references // its data into the prototype descriptor passed in the args to read() // then this data must _not_ be modified while the reference count // on the prototype is greater than zero. // + // Return S_casApp_postponeAsyncIO if too many simultaneous + // asynchronous IO operations are pending aginst the PV. + // The server library will retry the request whenever an + // asynchronous IO operation (read or write) completes + // against the PV. + // virtual caStatus read (const casCtx &ctx, gdd &prototype); // // write // - // asychronous completion iis allowed - // (ie the server tool is allowed to cache the data and actually - // complete the write operation at some time in the future) + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). + // + // Return S_casApp_postponeAsyncIO if too many simultaneous + // asynchronous IO operations are pending aginst the PV. + // The server library will retry the request whenever an + // asynchronous IO operation (read or write) completes + // against the PV. // virtual caStatus write (const casCtx &ctx, gdd &value); @@ -455,7 +490,11 @@ public: // // peek at the pv name // - //inline char *getName() const + // NOTE if there are several aliases for the same PV + // this routine should return the canonical (base) + // name for the PV + // + virtual const char *getName() const = 0; // // Find the server associated with this PV @@ -466,7 +505,13 @@ public: // for virtual casPV::destroy() // *************** // - caServer *getCAS(); + caServer *getCAS() const; + + // + // only used when caStrmClient converts between + // casPV * and casPVI * + // + friend class casStrmClient; }; // @@ -497,16 +542,6 @@ public: virtual void setOwner(const char * const pUserName, const char * const pHostName); - // - // called when the first client begins to monitor the PV - // - virtual caStatus interestRegister(); - - // - // called when the last client stops monitoring the PV - // - virtual void interestDelete(); - // // the following are encouraged to change during an channel's // lifetime @@ -522,7 +557,7 @@ public: // caServer::show() is called and the level is high // enough // - virtual void show(unsigned level); + virtual void show(unsigned level) const; // // destroy() is called when @@ -535,10 +570,10 @@ public: virtual void destroy(); // - // server tool calls this to indicate change of channel state - // (ie access rights changed) + // server tool calls this to indicate change in access + // rights has occurred // - void postEvent (const casEventMask &select, gdd &event); + void postAccessRightsEvent(); // // Find the PV associated with this channel @@ -550,6 +585,12 @@ public: // *************** // casPV *getPV(); + + // + // only used when casStrmClient converts between + // casChannel * and casChannelI * + // + friend class casStrmClient; }; // @@ -560,12 +601,13 @@ public: // Virtual Function Asynchronous IO Class // ----------------- --------------------- // caServer::pvExistTest() casAsyncPVExistIO +// caServer::createPV() casAsyncCreatePVIO // casPV::read() casAsyncReadIO // casPV::write() casAsyncWriteIO // // To initiate asynchronous completion create a corresponding -// asynchronous IO object from the table above from within -// one of the above virtual functions and return the status code +// asynchronous IO object from within one of the virtual +// functions shown in the table above and return the status code // S_casApp_asyncCompletion. Use the member function // "postIOCompletion()" to inform the server library that the // requested operation has completed. @@ -661,7 +703,7 @@ public: // into a server // *************** // - caServer *getCAS() + caServer *getCAS() const { return this->casAsyncRdIOI::getCAS(); } @@ -712,7 +754,7 @@ public: // into a server // *************** // - caServer *getCAS() + caServer *getCAS() const { return this->casAsyncWtIOI::getCAS(); } @@ -727,9 +769,6 @@ public: // // casAsyncPVExistIO() // - // Any DD ptr supplied here is used if postIOCompletion() - // is called with a nill DD pointer - // casAsyncPVExistIO(const casCtx &ctx) : casAsyncExIOI(ctx, *this) {} @@ -743,7 +782,7 @@ public: // (this function does not delete the casAsyncIO object). // Only the first call to this function has any effect. // - caStatus postIOCompletion(const pvExistReturn &retValIn) + caStatus postIOCompletion(const pvExistReturn retValIn) { return this->casAsyncExIOI::postIOCompletion (retValIn); } @@ -755,11 +794,51 @@ public: // into a server // *************** // - caServer *getCAS() + caServer *getCAS() const { return this->casAsyncExIOI::getCAS(); } }; +// +// casAsyncPVCreateIO +// - for use with caServer::createPV() +// +class casAsyncPVCreateIO : public casAsyncIO, private casAsyncPVCIOI { +public: + // + // casAsyncPVCreateIO() + // + casAsyncPVCreateIO(const casCtx &ctx) : + casAsyncPVCIOI(ctx, *this) {} + + // + // force virtual destructor + // + virtual ~casAsyncPVCreateIO(); + + // + // place notification of IO completion on the event queue + // (this function does not delete the casAsyncIO object). + // Only the first call to this function has any effect. + // + caStatus postIOCompletion(const pvCreateReturn &retValIn) + { + return this->casAsyncPVCIOI::postIOCompletion (retValIn); + } + + // + // Find the server associated with this async IO + // ****WARNING**** + // this returns NULL if the async io isnt currently installed + // into a server + // *************** + // + caServer *getCAS() const + { + return this->casAsyncPVCreateIO::getCAS(); + } +}; + #endif // ifdef includecasdefh (this must be the last line in this file) diff --git a/src/cas/generic/dgInBuf.cc b/src/cas/generic/dgInBuf.cc index 4b748c2d2..596df00cb 100644 --- a/src/cas/generic/dgInBuf.cc +++ b/src/cas/generic/dgInBuf.cc @@ -1,7 +1,7 @@ -#include -#include -#include +#include "server.h" +#include "inBufIL.h" +#include "casOpaqueAddrIL.h" // // this needs to be here (and not in dgInBufIL.h) if we diff --git a/src/cas/generic/dgInBufIL.h b/src/cas/generic/dgInBufIL.h index bdb02a70a..6832e6540 100644 --- a/src/cas/generic/dgInBufIL.h +++ b/src/cas/generic/dgInBufIL.h @@ -2,8 +2,8 @@ #ifndef dgInBufILh #define dgInBufILh -#include -#include +#include "casOpaqueAddrIL.h" +#include "inBufIL.h" // // dgInBuf::clear() diff --git a/src/cas/generic/dgOutBuf.cc b/src/cas/generic/dgOutBuf.cc index 793145110..a4b8794ab 100644 --- a/src/cas/generic/dgOutBuf.cc +++ b/src/cas/generic/dgOutBuf.cc @@ -1,7 +1,7 @@ -#include -#include -#include +#include "server.h" +#include "casOpaqueAddrIL.h" +#include "outBufIL.h" // // this needs to be here (and not in dgInBufIL.h) if we diff --git a/src/cas/generic/dgOutBufIL.h b/src/cas/generic/dgOutBufIL.h index de790c38f..1de0d166b 100644 --- a/src/cas/generic/dgOutBufIL.h +++ b/src/cas/generic/dgOutBufIL.h @@ -2,8 +2,8 @@ #ifndef dgOutBufILh #define dgOutBufILh -#include -#include +#include "casOpaqueAddrIL.h" +#include "outBufIL.h" // // All of the functions in this file moved to dgOutBuf.cc because diff --git a/src/cas/generic/server.h b/src/cas/generic/server.h index 7b08e9d66..b9ee86b92 100644 --- a/src/cas/generic/server.h +++ b/src/cas/generic/server.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.15 1997/01/10 21:18:05 jhill + * code around gnu g++ inline bug when -O isnt used + * * Revision 1.14 1996/12/11 00:57:56 jhill * moved casEventMaskEntry here * @@ -133,12 +136,16 @@ enum casProcCond {casProcOk, casProcDisconnect}; /* * maximum peak log entries for each event block (registartion) * (events cached into the last queue entry if over flow occurs) + * (if this exceeds 256 then the casMonitor::nPend must + * be assigned a new data type) */ #define individualEventEntries 16u /* * maximum average log entries for each event block (registartion) * (events cached into the last queue entry if over flow occurs) + * (if this exceeds 256 then the casMonitor::nPend must + * be assigned a new data type) */ #define averageEventEntries 4u @@ -159,7 +166,9 @@ public: inline caStatus init(); - ~casEventSys(); + virtual ~casEventSys(); + + virtual void destroy()=0; void show(unsigned level) const; casProcCond process(); @@ -185,6 +194,8 @@ public: inline void setEventsOff(); + inline void setDestroyPending(); + private: tsDLList eventLogQue; osiMutex mutex; @@ -192,6 +203,7 @@ private: unsigned numEventBlocks; // N event blocks installed unsigned maxLogEntries; // max log entries unsigned char eventsOff; + unsigned char destroyPending; }; // @@ -206,15 +218,14 @@ public: caStatus callBack(gdd &value); - casResType resourceType() const - { - return casClientMonT; - } + virtual casResType resourceType() const; caResId getId() const { return this->casRes::getId(); } + + virtual void destroy(); private: }; @@ -249,6 +260,7 @@ public: inline void setChannel(casChannelI *p); void show (unsigned level) const; + private: caHdr msg; // ca message header void *pData; // pointer to data following header @@ -256,6 +268,7 @@ private: casCoreClient *pClient; casChannelI *pChannel; casPVI *pPV; + unsigned nAsyncIO; // checks for improper use of async io }; enum casFillCondition{ @@ -426,6 +439,12 @@ private: // class casCoreClient : public osiMutex, public ioBlocked, public casEventSys { + +// +// allows casAsyncIOI constructor to check for asynch IO duplicates +// +friend casAsyncIOI::casAsyncIOI(casCoreClient &clientIn, casAsyncIO &ioExternalIn); + public: casCoreClient(caServerI &serverInternal); caStatus init(); @@ -451,14 +470,15 @@ public: virtual caStatus monitorResponse(casChannelI *, const caHdr &, gdd *, const caStatus); + virtual caStatus accessRightsResponse(casChannelI *); + // // one virtual function for each CA request type that has // asynchronous completion // virtual caStatus asyncSearchResponse(casDGIntfIO &outMsgIO, - const caAddr &outAddr, const caHdr &, const pvExistReturn &); - virtual caStatus createChanResponse(const caHdr &, - const pvExistReturn &); + const caAddr &outAddr, const caHdr &, const pvExistReturn); + virtual caStatus createChanResponse(const caHdr &, const pvCreateReturn &); virtual caStatus readResponse(casChannelI *, const caHdr &, gdd *, const caStatus); virtual caStatus readNotifyResponse(casChannelI *, const caHdr &, @@ -476,6 +496,7 @@ public: virtual casDGIntfIO* fetchOutIntf(); protected: casCtx ctx; + unsigned char asyncIOFlag; private: tsDLList ioInProgList; @@ -613,7 +634,7 @@ public: // one function for each CA request type that has // asynchronous completion // - virtual caStatus createChanResponse(const caHdr &, const pvExistReturn &); + virtual caStatus createChanResponse(const caHdr &, const pvCreateReturn &); caStatus readResponse(casChannelI *pChan, const caHdr &msg, gdd *pDesc, const caStatus status); caStatus readNotifyResponse(casChannelI *pChan, const caHdr &msg, @@ -753,11 +774,11 @@ private: // caStatus searchFailResponse(const caHdr *pMsg); - caStatus searchResponse(const caHdr &, const pvExistReturn &); + caStatus searchResponse(const caHdr &, const pvExistReturn); caStatus asyncSearchResponse(casDGIntfIO &outMsgIO, const caAddr &outAddr, const caHdr &msg, - const pvExistReturn &retVal); + const pvExistReturn); caAddr fetchRespAddr(); casDGIntfIO* fetchOutIntf(); @@ -781,7 +802,9 @@ public: casEventMaskEntry (casEventRegistry ®In, casEventMask maskIn, const char *pName); virtual ~casEventMaskEntry(); - void show (unsigned level); + void show (unsigned level) const; + + virtual void destroy(); private: casEventRegistry ® }; @@ -804,7 +827,7 @@ public: casEventMask registerEvent (const char *pName); - void show (unsigned level); + void show (unsigned level) const; private: osiMutex &mutex; @@ -819,6 +842,27 @@ private: class casClientMon; +// +// casPVExistReturn +// +// special return code for the server internal version of pvExistTest() +// +class casPVExistReturn { +public: + casPVExistReturn(pvExistReturn appIn) + {app=appIn; stat = S_cas_success; } + casPVExistReturn(caStatus statIn) + {app=pverDoesNotExistHere; stat = statIn; } + caStatus getStatus() const {return stat;} + pvExistReturn getAppStat() const {return app;} +private: + pvExistReturn app; + caStatus stat; +}; + +// +// caServerI +// class caServerI : public osiMutex, // osiMutex must be first because it is used // by ioBlockedList and casEventRegistry @@ -828,8 +872,7 @@ class caServerI : private uintResTable, public casEventRegistry { public: - caServerI(caServer &tool, unsigned pvMaxNameLength, - unsigned pvCountEstimate, unsigned maxSimultaneousIO); + caServerI(caServer &tool, unsigned pvCountEstimate); caStatus init(); //constructor does not return status ~caServerI(); @@ -854,18 +897,6 @@ public: void removeClient(casStrmClient *pClient); - unsigned getMaxSimultaneousIO() const {return this->maxSimultaneousIO;} - - // - // install a PV into the server - // - inline void installPV(casPVI &pv); - - // - // remove PV from the server - // - inline void removePV(casPVI &pv); - // // is there space for a new channel // @@ -878,20 +909,14 @@ public: unsigned getDebugLevel() const { return debugLevel; } inline void setDebugLevel(unsigned debugLevelIn); - inline pvExistReturn pvExistTest (const casCtx &ctx, const char *pPVName); - inline void pvExistTestCompletion(); - inline aitBool pvExistTestPossible(); - - casPVI *createPV(const char *pName); + inline casPVExistReturn pvExistTest (const casCtx &ctx, const char *pPVName); osiTime getBeaconPeriod() const { return this->beaconPeriod; } - void show(unsigned level); + void show(unsigned level) const; inline casRes *lookupRes(const caResId &idIn, casResType type); - inline unsigned getPVMaxNameLength() const; - inline caServer *getAdapter(); inline void installItem(casRes &res); @@ -913,27 +938,16 @@ private: void advanceBeaconPeriod(); casDGOS dgClient; - casCtx ctx; + //casCtx ctx; tsDLList clientList; tsDLList intfList; - resTable stringResTbl; osiTime beaconPeriod; caServer &adapter; - unsigned pvCount; unsigned debugLevel; - unsigned nExistTestInProg; - // max number of IO ops pending simultaneously - // (for operations that are not directed at a particular PV) - const unsigned pvMaxNameLength; - - // the estimated number of proces variables default = ??? + // the estimated number of proces variables default = 1024u const unsigned pvCountEstimate; - // the maximum number of characters in a pv name - // default = none - required initialization parameter - const unsigned maxSimultaneousIO; - unsigned char haveBeenInitialized; }; diff --git a/src/cas/generic/st/caServerOS.cc b/src/cas/generic/st/caServerOS.cc index dad710f98..be64af0c9 100644 --- a/src/cas/generic/st/caServerOS.cc +++ b/src/cas/generic/st/caServerOS.cc @@ -6,6 +6,9 @@ * * * $Log$ + * Revision 1.1 1996/11/02 01:01:27 jhill + * installed + * * Revision 1.2 1996/08/05 19:28:49 jhill * space became tab * @@ -19,7 +22,7 @@ // // CA server // -#include +#include "server.h" // // casBeaconTimer diff --git a/src/cas/generic/st/casDGIntfOS.cc b/src/cas/generic/st/casDGIntfOS.cc index 8073efd52..aab1903a5 100644 --- a/src/cas/generic/st/casDGIntfOS.cc +++ b/src/cas/generic/st/casDGIntfOS.cc @@ -10,8 +10,8 @@ // // CA server // -#include -#include // IO Depen in line func +#include "server.h" +#include "casIODIL.h" // IO Depen in line func // // casDGReadReg diff --git a/src/cas/generic/st/casDGOS.cc b/src/cas/generic/st/casDGOS.cc index b04c0fd48..220812a19 100644 --- a/src/cas/generic/st/casDGOS.cc +++ b/src/cas/generic/st/casDGOS.cc @@ -6,6 +6,9 @@ * * * $Log$ + * Revision 1.1 1996/11/02 01:01:29 jhill + * installed + * * Revision 1.3 1996/09/16 18:27:50 jhill * vxWorks port changes * @@ -21,8 +24,8 @@ // // CA server // -#include -#include +#include "server.h" +#include "casClientIL.h" class casDGEvWakeup : public osiTimer { public: @@ -71,12 +74,20 @@ void casDGEvWakeup::expire() cond = this->os.eventSysProcess(); if (cond != casProcOk) { // - // if "this" is being used above this - // routine on the stack then problems - // will result if we delete "this" here + // ok to delete the client here + // because casStreamEvWakeup::expire() + // is called by the timer queue system + // and therefore we are not being + // called from a client member function + // higher up on the stack // - // delete &this->os; - ca_printf("DG event sys process failed\n"); + this->os.destroy(); + + // + // must not touch the "this" pointer + // from this point on however + // + return; } } diff --git a/src/cas/generic/st/casIntfOS.cc b/src/cas/generic/st/casIntfOS.cc index 604bdcbcd..9114c068d 100644 --- a/src/cas/generic/st/casIntfOS.cc +++ b/src/cas/generic/st/casIntfOS.cc @@ -11,7 +11,7 @@ // // CA server // -#include +#include "server.h" // // casServerReg diff --git a/src/cas/generic/st/casOSD.h b/src/cas/generic/st/casOSD.h index 939ad9e3d..49723b00d 100644 --- a/src/cas/generic/st/casOSD.h +++ b/src/cas/generic/st/casOSD.h @@ -7,6 +7,9 @@ // Some BSD calls have crept in here // // $Log$ +// Revision 1.1 1996/11/02 01:01:32 jhill +// installed +// // Revision 1.3 1996/09/04 22:04:07 jhill // moved netdb.h include here // @@ -21,8 +24,8 @@ #ifndef includeCASOSDH #define includeCASOSDH -#include -#include +#include "osiTimer.h" +#include "fdManager.h" class caServerI; class caServerOS; diff --git a/src/cas/generic/st/casStreamOS.cc b/src/cas/generic/st/casStreamOS.cc index d7e325762..649ad49fc 100644 --- a/src/cas/generic/st/casStreamOS.cc +++ b/src/cas/generic/st/casStreamOS.cc @@ -4,6 +4,9 @@ // // // $Log$ +// Revision 1.4 1996/12/12 21:24:17 jhill +// moved casStreamOS *pStrmOS decl down +// // Revision 1.3 1996/12/12 19:02:36 jhill // fixed send does not get armed after complete flush bug // @@ -26,10 +29,10 @@ // // CA server // -#include -#include // casClient inline func -#include // inBuf inline func -#include // outBuf inline func +#include "server.h" +#include "casClientIL.h" // casClient inline func +#include "inBufIL.h" // inBuf inline func +#include "outBufIL.h" // outBuf inline func // // casStreamReadReg @@ -174,12 +177,20 @@ void casStreamEvWakeup::expire() cond = this->os.casEventSys::process(); if (cond != casProcOk) { // - // if "this" is being used above this - // routine on the stack then problems - // will result if we delete "this" here + // ok to delete the client here + // because casStreamEvWakeup::expire() + // is called by the timer queue system + // and therefore we are not being + // called from a client member function + // higher up on the stack // - // delete &this->os; - ca_printf("strm event sys process failed\n"); + this->os.destroy(); + + // + // must not touch the "this" pointer + // from this point on however + // + return; } } @@ -506,21 +517,22 @@ void casStreamWriteReg::callBack() // attempt to flush the output buffer // flushCond = os.flush(); - switch (flushCond) { - case casFlushCompleted: - case casFlushPartial: + if ( flushCond==casFlushCompleted || + flushCond==casFlushPartial) { if (os.sendBlocked) { os.sendBlocked = FALSE; } - break; - case casFlushNone: - break; - case casFlushDisconnect: + } + else if (flushCond==casFlushDisconnect) { return; - break; - default: + } +#if defined(DEBUG) + else if (flushCond==casFlushNone) { + } + else { assert(0); } +#endif // // If we are unable to flush out all of the events @@ -531,7 +543,20 @@ void casStreamWriteReg::callBack() // procCond = this->os.casEventSys::process(); if (procCond != casProcOk) { - ca_printf("strm event sys process failed\n"); + // + // ok to delete the client here + // because casStreamWriteReg::callBack() + // is called by the fdManager system + // and therefore we are not being + // called from a client member function + // higher up on the stack + // + this->os.destroy(); + // + // must not touch "this" pointer + // after the destroy however + // + return; } # if defined(DEBUG) @@ -592,20 +617,20 @@ casProcCond casStreamOS::processInput() # endif status = this->processMsg(); - switch (status) { - case S_cas_sendBlocked: - case S_cas_partialMessage: - case S_cas_ioBlocked: - case S_cas_success: + if ( status==S_cas_success || + status==S_cas_sendBlocked || + status==S_casApp_postponeAsyncIO || + status==S_cas_partialMessage) { + if (this->inBuf::bytesAvailable()==0u) { this->armSend (); } this->armRecv(); return casProcOk; - break; - default: + } + else { errMessage (status, - "unexpected problem with client's input - forcing disconnect"); + "unexpected problem with client's input - forcing disconnect"); return casProcDisconnect; } } diff --git a/src/cas/generic/st/ioBlocked.cc b/src/cas/generic/st/ioBlocked.cc index 015ff7c27..9948af837 100644 --- a/src/cas/generic/st/ioBlocked.cc +++ b/src/cas/generic/st/ioBlocked.cc @@ -7,12 +7,15 @@ // (for single threaded version of the server) // // $Log$ +// Revision 1.1 1996/11/02 01:01:34 jhill +// installed +// // #include -#include -#include +#include "casdef.h" +#include "osiMutexNOOP.h" // @@ -35,7 +38,9 @@ ioBlocked::~ioBlocked() // void ioBlocked::ioBlockedSignal() { - fprintf(stderr, "in virtual base ioBlocked::ioBlockedSignal() ?\n"); + // + // NOOP + // } // diff --git a/src/cas/generic/st/osiMutexCAS.h b/src/cas/generic/st/osiMutexCAS.h index 430afd5e8..d0e1aa67f 100644 --- a/src/cas/generic/st/osiMutexCAS.h +++ b/src/cas/generic/st/osiMutexCAS.h @@ -2,5 +2,5 @@ // // single threaded code NOOPs the mutex class // -#include +#include "osiMutexNOOP.h" diff --git a/src/cas/generic/templInst.cc b/src/cas/generic/templInst.cc index e2dcb61ab..82d1b48c2 100644 --- a/src/cas/generic/templInst.cc +++ b/src/cas/generic/templInst.cc @@ -10,9 +10,10 @@ #include "resourceLib.cc" // -// Sun C++ 4.1 still appears to be lacking support in this area +// if the compiler supports explicit instantiation of +// template member functions // -#if !defined(__SUNPRO_CC) +#if defined(EXPL_TEMPL) // // From Stroustrups's "The C++ Programming Language" // Appendix A: r.14.9 @@ -21,7 +22,6 @@ // functions into "templInst.o" // template class resTable ; - template class resTable ; template class resTable ; #endif diff --git a/src/cas/io/bsdSocket/caServerIO.cc b/src/cas/io/bsdSocket/caServerIO.cc index ab093ce5c..fac1a8bf0 100644 --- a/src/cas/io/bsdSocket/caServerIO.cc +++ b/src/cas/io/bsdSocket/caServerIO.cc @@ -5,6 +5,9 @@ // // // $Log$ +// Revision 1.3 1996/11/02 00:54:42 jhill +// many improvements +// // Revision 1.2 1996/06/21 02:12:40 jhill // SOLARIS port // @@ -15,10 +18,10 @@ #include -#include -#include +#include "server.h" +#include "sigPipeIgnore.h" -static char *getToken(char **ppString); +static char *getToken(const char **ppString, char *pBuf, unsigned bufSIze); int caServerIO::staticInitialized; @@ -49,8 +52,8 @@ inline void caServerIO::staticInit() // caStatus caServerIO::init(caServerI &cas) { - ENV_PARAM buf; - char *pStr; + char buf[64u]; + const char *pStr; char *pToken; caStatus stat; unsigned short port; @@ -65,21 +68,30 @@ caStatus caServerIO::init(caServerI &cas) // clients to find the server). If this also isnt available // then use a hard coded default - CA_SERVER_PORT. // - if (envParamIsEmpty(&EPICS_CAS_SERVER_PORT)) { - port = caFetchPortConfig(&EPICS_CA_SERVER_PORT, CA_SERVER_PORT); + if (envGetConfigParamPtr(&EPICS_CAS_SERVER_PORT)) { + port = caFetchPortConfig(&EPICS_CAS_SERVER_PORT, CA_SERVER_PORT); } else { - port = caFetchPortConfig(&EPICS_CAS_SERVER_PORT, CA_SERVER_PORT); + port = caFetchPortConfig(&EPICS_CA_SERVER_PORT, CA_SERVER_PORT); } memset((char *)&addr,0,sizeof(addr)); addr.sa.sa_family = AF_INET; addr.in.sin_port = ntohs (port); - pStr = envGetConfigParam(&EPICS_CA_AUTO_ADDR_LIST, - sizeof(buf.dflt), buf.dflt); - if (strstr(pStr,"no")||strstr(pStr,"NO")) { - autoBeaconAddr = FALSE; + pStr = envGetConfigParam(&EPICS_CA_AUTO_ADDR_LIST, sizeof(buf), buf); + if (pStr) { + if (strstr(pStr,"no")||strstr(pStr,"NO")) { + autoBeaconAddr = FALSE; + } + else if (strstr(pStr,"yes")||strstr(pStr,"YES")) { + autoBeaconAddr = TRUE; + } + else { + fprintf(stderr, + "CAS: EPICS_CA_AUTO_ADDR_LIST = \"%s\"? Assuming \"YES\"\n", pStr); + autoBeaconAddr = TRUE; + } } else { autoBeaconAddr = TRUE; @@ -89,12 +101,11 @@ caStatus caServerIO::init(caServerI &cas) // bind to the the interfaces specified - otherwise wildcard // with INADDR_ANY and allow clients to attach from any interface // - pStr = envGetConfigParam(&EPICS_CAS_INTF_ADDR_LIST, - sizeof(buf.dflt), buf.dflt); + pStr = envGetConfigParamPtr(&EPICS_CAS_INTF_ADDR_LIST); if (pStr) { int configAddrOnceFlag = TRUE; stat = S_cas_noInterface; - while ( (pToken = getToken(&pStr)) ) { + while ( (pToken = getToken(&pStr, buf, sizeof(buf))) ) { addr.in.sin_addr.s_addr = inet_addr(pToken); if (addr.in.sin_addr.s_addr == ~0ul) { ca_printf( @@ -136,35 +147,32 @@ void caServerIO::show (unsigned /* level */) const // // getToken() // -static char *getToken(char **ppString) +static char *getToken(const char **ppString, char *pBuf, unsigned bufSIze) { - char *pToken; - char *pStr; + const char *pToken; + unsigned i; pToken = *ppString; while(isspace(*pToken)&&*pToken){ pToken++; } - pStr = pToken; - while(!isspace(*pStr)&&*pStr){ - pStr++; + for (i=0u; i -#include // in line func for dgInBuf +#include "server.h" +#include "dgInBufIL.h" // in line func for dgInBuf // // casDGIO::clientHostName() diff --git a/src/cas/io/bsdSocket/casDGIntfIO.cc b/src/cas/io/bsdSocket/casDGIntfIO.cc index 825dd2720..d6c7c57cc 100644 --- a/src/cas/io/bsdSocket/casDGIntfIO.cc +++ b/src/cas/io/bsdSocket/casDGIntfIO.cc @@ -34,7 +34,7 @@ // // -#include +#include "server.h" // // casDGIntfIO::casDGIntfIO() @@ -89,6 +89,28 @@ caStatus casDGIntfIO::init(const caAddr &addr, unsigned connectWithThisPortIn, return S_cas_internal; } + { + /* + * + * this allows for faster connects by queuing + * additional incomming UDP search frames + * + * this allocates a 32k buffer + * (uses a power of two) + */ + int size = 1u<<15u; + status = setsockopt( + this->sock, + SOL_SOCKET, + SO_RCVBUF, + (char *)&size, + sizeof(size)); + if (status<0) { + errMessage(S_cas_internal, + "CAS: unable to set cast socket size\n"); + } + } + /* * release the port in case we exit early. Also if * on a kernel with MULTICAST mods then we can have @@ -109,12 +131,12 @@ caStatus casDGIntfIO::init(const caAddr &addr, unsigned connectWithThisPortIn, // // Fetch port configuration from EPICS environment variables // - if (envParamIsEmpty(&EPICS_CAS_SERVER_PORT)) { - serverPort = caFetchPortConfig(&EPICS_CA_SERVER_PORT, + if (envGetConfigParamPtr(&EPICS_CAS_SERVER_PORT)) { + serverPort = caFetchPortConfig(&EPICS_CAS_SERVER_PORT, CA_SERVER_PORT); } else { - serverPort = caFetchPortConfig(&EPICS_CAS_SERVER_PORT, + serverPort = caFetchPortConfig(&EPICS_CA_SERVER_PORT, CA_SERVER_PORT); } beaconPort = caFetchPortConfig(&EPICS_CA_REPEATER_PORT, @@ -168,14 +190,14 @@ caStatus casDGIntfIO::init(const caAddr &addr, unsigned connectWithThisPortIn, * by default use EPICS_CA_ADDR_LIST for the * beacon address list */ - ENV_PARAM *pParam = &EPICS_CA_ADDR_LIST; + const ENV_PARAM *pParam; - if (envParamIsEmpty(&EPICS_CAS_INTF_ADDR_LIST) && - envParamIsEmpty(&EPICS_CAS_BEACON_ADDR_LIST)) { - pParam = &EPICS_CA_ADDR_LIST; + if (envGetConfigParamPtr(&EPICS_CAS_INTF_ADDR_LIST) || + envGetConfigParamPtr(&EPICS_CAS_BEACON_ADDR_LIST)) { + pParam = &EPICS_CAS_BEACON_ADDR_LIST; } else { - pParam = &EPICS_CAS_BEACON_ADDR_LIST; + pParam = &EPICS_CA_ADDR_LIST; } /* diff --git a/src/cas/io/bsdSocket/casIntfIO.cc b/src/cas/io/bsdSocket/casIntfIO.cc index edfa65fbd..75b8f2ef2 100644 --- a/src/cas/io/bsdSocket/casIntfIO.cc +++ b/src/cas/io/bsdSocket/casIntfIO.cc @@ -6,9 +6,12 @@ // // // $Log$ +// Revision 1.1 1996/11/02 01:01:41 jhill +// installed +// // -#include +#include "server.h" // // 5 appears to be a TCP/IP built in maximum diff --git a/src/cas/io/bsdSocket/casStreamIO.cc b/src/cas/io/bsdSocket/casStreamIO.cc index b881fb10b..6aa6bbae1 100644 --- a/src/cas/io/bsdSocket/casStreamIO.cc +++ b/src/cas/io/bsdSocket/casStreamIO.cc @@ -5,6 +5,9 @@ // // // $Log$ +// Revision 1.8 1997/01/10 00:00:01 jhill +// close() => socket_close() +// // Revision 1.7.2.1 1996/11/25 16:33:00 jhill // close() => socket_close() // @@ -31,7 +34,7 @@ // // -#include +#include "server.h" // diff --git a/src/cas/io/bsdSocket/sigPipeIgnore.c b/src/cas/io/bsdSocket/sigPipeIgnore.c index 45597e075..1082cb55c 100644 --- a/src/cas/io/bsdSocket/sigPipeIgnore.c +++ b/src/cas/io/bsdSocket/sigPipeIgnore.c @@ -11,19 +11,21 @@ #include #include -#include +#include "sigPipeIgnore.h" typedef void (*pSigFunc) (); static pSigFunc pReplacedFunc; -#ifdef WIN32 +#ifndef UNIX // all os except UNIX void installSigPipeIgnore (void) { } -#else // WIN32 +#else // it is UNIX + +static void localInstallSigPipeIgnore (void); /* * ignoreSigPipe () @@ -33,6 +35,11 @@ static void ignoreSigPipe (int param) if (pReplacedFunc) { (*pReplacedFunc) (param); } + /* + * some versios of unix reset to SIG_DFL + * each time that the signal occurs + */ + localInstallSigPipeIgnore (); } /* @@ -45,15 +52,38 @@ void installSigPipeIgnore (void) if (init) { return; } - - pReplacedFunc = signal (SIGPIPE, ignoreSigPipe); - if (pReplacedFunc == SIG_ERR) { - char *pFmt = "replace of SIGPIPE failed beacuse\n"; - fprintf (stderr, pFmt, __FILE__, strerror(errno)); - } - + localInstallSigPipeIgnore(); init = 1; } -#endif // WIN32 +/* + * localInstallSigPipeIgnore () + * + * dont allow disconnect to terminate process + * when running in UNIX environment + * + * allow error to be returned to sendto() + * instead of handling disconnect at interrupt + */ +static void localInstallSigPipeIgnore (void) +{ + pSigFunc sigRet; + + sigRet = signal (SIGPIPE, ignoreSigPipe); + if (sigRet==SIG_ERR) { + fprintf (stderr, "%s replace of SIGPIPE failed beacuse %s\n", + __FILE__, strerror(errno)); + } + else if (sigRet!=SIG_DFL && sigRet!=SIG_IGN) { + pReplacedFunc = sigRet; + } + /* + * no infinite loops + */ + if (pReplacedFunc==ignoreSigPipe) { + pReplacedFunc = NULL; + } +} + +#endif // UNIX diff --git a/src/cxxTemplates/Makefile.Host b/src/cxxTemplates/Makefile.Host index b4dab2478..dcd95d5b3 100644 --- a/src/cxxTemplates/Makefile.Host +++ b/src/cxxTemplates/Makefile.Host @@ -5,6 +5,8 @@ INC += resourceLib.h INC += resourceLib.cc INC += tsDLList.h INC += tsSLList.h +INC += tsBTree.h +INC += minmax.h include $(TOP)/config/RULES.Host diff --git a/src/cxxTemplates/README b/src/cxxTemplates/README index 4018aeb1b..566497b6a 100644 --- a/src/cxxTemplates/README +++ b/src/cxxTemplates/README @@ -6,4 +6,51 @@ resourceLib.h - hash table template the test subdir contains examples +Since I am using templates the linked lists are type safe +(no casting of pointers ala ellList and dllList). +Also, the node class in embedded in the item on the +list (more efficent use of pool). + +The file resourceLib.h provides a core hashing library +"resTable " where "itemClass" objects +are stored in the hash table and "idClass" is the data type +of the key for the hash table. The identifier class provides +the hash alg. I have provided simple string "stringId" and +unsigned integer "uintId" key types in resourceLib.h. It +is easy to implement a new key class. + +There are examples under cxxTemplate/test. The list/hashing +templates all depend on a particular inheritance hierarchy. +If the inheritance hierarchy is wrong nothing will compile. +For instance, in tsDLList.h the template data type "T" +must derive from tsDLNode. Likewise, in tsSLList.h +"T" must derive from tsSLNode. Likewise, in resourceLib.h +class "T" (the type stored in the hash table) must derive +from class "ID" (the hash table key type) and also derive from +tsSLNode. + +So far, the only confusion I have run into with templates has been: + +1) strange compiler messages - unrelated to cause of course - +when I get the class declaration order wrong (so that the +compiler has trouble instantiating the template). + +2) sun pro/dec/att compilers use a template database and +gnu/msvc++ compilers use explicit template instantiation. +Therefore blocks of code of this sort are required: + +#include "resourceLib.h" // template def +#include "resourceLib.cc" // template functions (that are not inline) +#if defined (EXPL_TEMPL) + // + // From Stroustrups's "The C++ Programming Language" + // Appendix A: r.14.9 + // + // This explicitly instantiates the template class's member + // functions into "templInst.o" + // + template class resTable; + template class resTable; +#endif + diff --git a/src/cxxTemplates/minmax.h b/src/cxxTemplates/minmax.h new file mode 100644 index 000000000..772416607 --- /dev/null +++ b/src/cxxTemplates/minmax.h @@ -0,0 +1,22 @@ + +// +// simple inline template functions to replace the min() and max() +// macros +// + +// +// ??? g++ 2.7.2 -Winline is unable to in line these tiny functions ??? +// + +template +inline const T &max(const T &a, const T &b) +{ + return (a>b) ? a : b; +} + +template +inline const T &min(const T &a, const T &b) +{ + return (a::destroyAllEntries() tsSLList *pList = this->pTable; while (pList<&this->pTable[this->hashIdMask+1]) { - tsSLIter iter(*pList); T *pItem; T *pNextItem; - pItem = iter(); - while (pItem) { - pNextItem = iter(); - delete pItem; - pItem = pNextItem; + { + tsSLIter iter(*pList); + pItem = iter(); + while (pItem) { + pNextItem = iter(); + pItem->destroy(); + pItem = pNextItem; + } } + // // Check to see if a defective class is // installed that does not remove itself // from the table when it is destroyed. // - iter.reset(); - while ( (pItem=iter()) ) { - fprintf(stderr, + { + tsSLIterRm iter(*pList); + while ( (pItem=iter()) ) { + fprintf(stderr, "Warning: Defective class still in resTable after it was destroyed\n"); - // - // remove defective class - // - iter.remove(); - this->nInUse--; + // + // remove defective class + // + iter.remove(); + this->nInUse--; + } } + pList++; } } @@ -116,7 +125,7 @@ void resTable::destroyAllEntries() // resTable::show // template -void resTable::show (unsigned level) +void resTable::show (unsigned level) const { tsSLList *pList; double X; @@ -160,5 +169,57 @@ void resTable::show (unsigned level) } } +// +// resTable::traverse +// +// pCB is a pointer to a member function +// +template +void resTable::traverse (void (T::*pCB)()) +{ + tsSLList *pList; + + pList = this->pTable; + while (pList < &this->pTable[this->hashIdMask+1]) { + tsSLIter iter(*pList); + T *pItem; + + while ( (pItem = iter()) ) { + (pItem->*pCB) (); + } + pList++; + } +} + +// +// this needs to be instanciated only once (normally in libCom) +// +#ifdef INSTANCIATE_RES_LIB_STATIC +// +// The hash algorithm is a modification of the algorithm described in +// Fast Hashing of Variable Length Text Strings, Peter K. Pearson, +// Communications of the ACM, June 1990 +// The modifications were designed by Marty Kraimer +// +unsigned char stringId::T[256] = { + 39,159,180,252, 71, 6, 13,164,232, 35,226,155, 98,120,154, 69, +157, 24,137, 29,147, 78,121, 85,112, 8,248,130, 55,117,190,160, +176,131,228, 64,211,106, 38, 27,140, 30, 88,210,227,104, 84, 77, + 75,107,169,138,195,184, 70, 90, 61,166, 7,244,165,108,219, 51, + 9,139,209, 40, 31,202, 58,179,116, 33,207,146, 76, 60,242,124, +254,197, 80,167,153,145,129,233,132, 48,246, 86,156,177, 36,187, + 45, 1, 96, 18, 19, 62,185,234, 99, 16,218, 95,128,224,123,253, + 42,109, 4,247, 72, 5,151,136, 0,152,148,127,204,133, 17, 14, +182,217, 54,199,119,174, 82, 57,215, 41,114,208,206,110,239, 23, +189, 15, 3, 22,188, 79,113,172, 28, 2,222, 21,251,225,237,105, +102, 32, 56,181,126, 83,230, 53,158, 52, 59,213,118,100, 67,142, +220,170,144,115,205, 26,125,168,249, 66,175, 97,255, 92,229, 91, +214,236,178,243, 46, 44,201,250,135,186,150,221,163,216,162, 43, + 11,101, 34, 37,194, 25, 50, 12, 87,198,173,240,193,171,143,231, +111,141,191,103, 74,245,223, 20,161,235,122, 63, 89,149, 73,238, +134, 68, 93,183,241, 81,196, 49,192, 65,212, 94,203, 10,200, 47 +}; +#endif // INSTANCIATE_RES_LIB_STATIC + #endif // INCresourceLibcc diff --git a/src/cxxTemplates/resourceLib.h b/src/cxxTemplates/resourceLib.h index 9c0f7641d..beddc585b 100644 --- a/src/cxxTemplates/resourceLib.h +++ b/src/cxxTemplates/resourceLib.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.7 1996/12/06 22:26:36 jhill + * added auto cleanup of installed classes to destroy + * * Revision 1.6 1996/11/02 01:07:17 jhill * many improvements * @@ -69,7 +72,7 @@ typedef unsigned resTableIndex; #define resTableIndexBitWidth (sizeof(resTableIndex)*CHAR_BIT) // -// class T must derive class ID +// class T must derive from class ID and also from class tsSLNode // // NOTE: Classes installed into this table should have // a virtual destructor so that the delete in ~resTable() will @@ -78,8 +81,6 @@ typedef unsigned resTableIndex; template class resTable { public: - enum resTableDelFlag {rtdfDelete, rtdfNoDelete}; - resTable() : pTable(0), hashIdMask(0), hashIdNBits(0), nInUse(0) {} @@ -94,10 +95,19 @@ public: } } + // + // destroy all res in the table + // void destroyAllEntries(); - void show (unsigned level); + // + // call T::show(level) for each res in the table + // + void show (unsigned level) const; + // + // add a res to the table + // int add (T &res) { // @@ -113,19 +123,34 @@ public: return 0; } + // + // remove a res from the table + // T *remove (const ID &idIn) { tsSLList &list = this->pTable[this->hash(idIn)]; - return this->find(list, idIn, rtdfDelete); + return this->findDelete(list, idIn); } + // + // find an res in the table + // T *lookup (const ID &idIn) { tsSLList &list = this->pTable[this->hash(idIn)]; return this->find(list, idIn); } + // + // Call (pT->*pCB) () for each item in the table + // + // where pT is a pointer to type T and pCB is + // a pointer to a memmber function of T with + // no parameters and returning void + // + void traverse(void (T::*pCB)()); + private: tsSLList *pTable; unsigned hashIdMask; @@ -147,8 +172,7 @@ private: // iterator points to the item found upon return // (or NULL if nothing matching was found) // - T *find (tsSLList &list, const ID &idIn, - resTableDelFlag df=rtdfNoDelete) + T *find (tsSLList &list, const ID &idIn) { tsSLIter iter(list); T *pItem; @@ -157,10 +181,33 @@ private: while ( (pItem = iter()) ) { pId = pItem; if (*pId == idIn) { - if (df==rtdfDelete) { - iter.remove(); - this->nInUse--; - } + break; + } + } + return pItem; + } + + // + // findDelete + // searches from where the iterator points to the + // end of the list for idIn + // + // iterator points to the item found upon return + // (or NULL if nothing matching was found) + // + // removes the item if it finds it + // + T *findDelete (tsSLList &list, const ID &idIn) + { + tsSLIterRm iter(list); + T *pItem; + ID *pId; + + while ( (pItem = iter()) ) { + pId = pItem; + if (*pId == idIn) { + iter.remove(); + this->nInUse--; break; } } @@ -211,16 +258,6 @@ protected: unsigned id; }; -// -// resource with unsigned chronological identifier -// -template -class uintRes : public uintId, public tsSLNode { -friend class uintResTable; -public: - uintRes(unsigned idIn=UINT_MAX) : uintId(idIn) {} -}; - // // special resource table which uses // unsigned integer keys allocated in chronological sequence @@ -232,24 +269,39 @@ class uintResTable : public resTable { public: uintResTable() : allocId(1u) {} // hashing is faster close to zero - // - // NOTE: This detects (and avoids) the case where - // the PV id wraps around and we attempt to have two - // resources with the same id. - // - void installItem(ITEM &item) - { - int resTblStatus; - do { - item.uintId::id = this->allocId++; - resTblStatus = this->add(item); - } - while (resTblStatus); - } + inline void installItem(ITEM &item); private: unsigned allocId; }; +// +// resource with unsigned chronological identifier +// +template +class uintRes : public uintId, public tsSLNode { +friend class uintResTable; +public: + uintRes(unsigned idIn=UINT_MAX) : uintId(idIn) {} +}; + +// +// uintRes::installItem() +// +// NOTE: This detects (and avoids) the case where +// the PV id wraps around and we attempt to have two +// resources with the same id. +// +template +inline void uintResTable::installItem(ITEM &item) +{ + int resTblStatus; + do { + item.uintRes::id = allocId++; + resTblStatus = this->add(item); + } + while (resTblStatus); +} + // // pointer identifier // @@ -292,40 +344,89 @@ private: // // character string identifier // +// NOTE: to be robust in situations where the new() +// in the constructor might fail a careful consumer +// of this class should check to see if the +// stringId::resourceName() below +// returns a valid (non--NULL) string pointer. +// Eventually an exception will be thrown if +// new fails (when this is portable). +// class stringId { public: - stringId (char const * const idIn) : - pStr(new char [strlen(idIn)+1u]) + enum allocationType {copyString, refString}; + + // + // allocCopyString() + // + static inline char * allocCopyString(const char * const pStr) { - if (this->pStr!=NULL) { - strcpy(this->pStr, idIn); + char *pNewStr = new char [strlen(pStr)+1u]; + if (pNewStr) { + strcpy (pNewStr, pStr); } + return pNewStr; } + // + // stringId() constructor + // + // Use typeIn==refString only if the string passed in will exist + // and remain constant during the entire lifespan of the stringId + // object. + // + stringId (char const * const idIn, allocationType typeIn=copyString) : + pStr(typeIn==copyString?allocCopyString(idIn):idIn), + allocType(typeIn) {} + ~ stringId() { - if (this->pStr!=NULL) { - delete [] this->pStr; + if (this->allocType==copyString) { + if (this->pStr!=NULL) { + delete [] (char *) this->pStr; + } } } - + + // + // The hash algorithm is a modification of the algorithm described in + // Fast Hashing of Variable Length Text Strings, Peter K. Pearson, + // Communications of the ACM, June 1990 + // The modifications were designed by Marty Kraimer + // resTableIndex resourceHash(unsigned nBitsId) const { - resTableIndex hashid; - unsigned i; - if (this->pStr==NULL) { return 0u; } - hashid = 0u; - for (i=0u; this->pStr[i]; i++) { - hashid += this->pStr[i] * (i+1u); + unsigned h0 = 0u; + unsigned h1 = 0u; + unsigned c; + unsigned i; + for (i=0u; (c = this->pStr[i]); i++) { + // + // odd + // + if (i&1u) { + h1 = this->T[h1 ^ c]; + } + // + // even + // + else { + h0 = this->T[h0 ^ c]; + } } - hashid = hashid % (1u<=8u) { + h1 = h1 << (nBitsId-8u); + } + return h1 ^ h0; } int operator == (const stringId &idIn) @@ -338,22 +439,27 @@ public: } } + // + // return the pointer to the string + // (also used to test to see if "new()" + // failed in the constructor + // const char * resourceName() { return this->pStr; } - void show (unsigned) + void show (unsigned level) const { - printf ("resource id = %s\n", this->pStr); + if (level>2u) { + printf ("resource id = %s\n", this->pStr); + } } private: - char * const pStr; + const char * const pStr; + allocationType const allocType; + static unsigned char T[256]; }; -#if defined(__SUNPRO_CC) || defined(EXPAND_TEMPLATES_HERE) -# include "resourceLib.cc" -#endif - #endif // INCresourceLibh diff --git a/src/cxxTemplates/test/Makefile.Host b/src/cxxTemplates/test/Makefile.Host index c544c1031..9b4111099 100644 --- a/src/cxxTemplates/test/Makefile.Host +++ b/src/cxxTemplates/test/Makefile.Host @@ -2,8 +2,10 @@ TOP = ../../../.. include $(TOP)/config/CONFIG_BASE +CXXCMPLR = STRICT + TESTPROD := resourceLibTest tsDLListBench tsDLListTest tsDLListTest \ - tsSLListBench tsSLListTest + tsSLListBench tsSLListTest minmaxTest include $(TOP)/config/RULES.Host diff --git a/src/cxxTemplates/test/minmaxTest.cc b/src/cxxTemplates/test/minmaxTest.cc new file mode 100644 index 000000000..69671e270 --- /dev/null +++ b/src/cxxTemplates/test/minmaxTest.cc @@ -0,0 +1,28 @@ + +#include + +#include "minmax.h" + +main () +{ + float f1 = 3.3; + float f2 = 3.4; + float f3; + + f3 = min(f1,f2); + assert(f3==f1); + + f3 = max(f1,f2); + assert(f3==f2); + + int i1 = 3; + int i2 = 4; + int i3; + + i3 = min(i1,i2); + assert(i3==i1); + + i3 = max(i1,i2); + assert(i3==i2); +} + diff --git a/src/cxxTemplates/test/resourceLibTest.cc b/src/cxxTemplates/test/resourceLibTest.cc index 05cad10e6..6adc14f49 100644 --- a/src/cxxTemplates/test/resourceLibTest.cc +++ b/src/cxxTemplates/test/resourceLibTest.cc @@ -5,6 +5,7 @@ #include #include "resourceLib.h" +#define INSTANCIATE_RES_LIB_STATIC #include "resourceLib.cc" #ifdef SUNOS4 @@ -21,6 +22,10 @@ public: { printf("fred %s\n", pName); } + void destroy() + { + // always on stack so noop + } private: const char * const pName; }; @@ -28,12 +33,28 @@ private: class jane : public stringId, public tsSLNode { public: jane (const char *pNameIn) : stringId(pNameIn) {} + + void testTraverse(); + + void destroy() + { + // always on stack so noop + } }; // -// Sun C++ 4.1 still appears to be lacking support in this area +// jane::testTraverse() // -#if !defined(__SUNPRO_CC) +void jane::testTraverse() +{ + printf("Traverse Test\n"); + this->show(10); +} + +// +// explicitly instantiates on compilers that support this +// +#if defined(EXPL_TEMPL) // // From Stroustrups's "The C++ Programming Language" // Appendix A: r.14.9 @@ -55,14 +76,14 @@ main() resTable strTbl; fred fred1("fred1",0x1000a432); fred fred2("fred2",0x0000a432); - jane jane1("jane1"); - jane jane2("jane2"); + jane jane1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + jane jane2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); fred *pFred; jane *pJane; uintId uintId1(0x1000a432); uintId uintId2(0x0000a432); - stringId strId1("jane1"); - stringId strId2("jane2"); + stringId strId1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + stringId strId2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); int status; status = intTbl.init(8); @@ -157,6 +178,7 @@ main() printf("It took %15.10f u sec per string hash lookup\n", duration); strTbl.show(10u); + strTbl.traverse(&jane::testTraverse); strTbl.remove(strId1); strTbl.remove(strId2); diff --git a/src/cxxTemplates/test/tsDLListBench.cc b/src/cxxTemplates/test/tsDLListBench.cc index a37dd938b..45ddf878a 100644 --- a/src/cxxTemplates/test/tsDLListBench.cc +++ b/src/cxxTemplates/test/tsDLListBench.cc @@ -40,7 +40,6 @@ main () } clk = clock(); - iter = list; while ( (pFred = iter()) ) { pFred->inc(); } @@ -52,7 +51,6 @@ main () pFred = new fred(); clk = clock(); - iter = list; for (i=0; iinc(); } diff --git a/src/cxxTemplates/test/tsDLListTest.cc b/src/cxxTemplates/test/tsDLListTest.cc index cb98997ca..30d630c28 100644 --- a/src/cxxTemplates/test/tsDLListTest.cc +++ b/src/cxxTemplates/test/tsDLListTest.cc @@ -70,18 +70,18 @@ main () pJane->show(); } - iter = list; + iter.reset(); while ( (pFredBack = iter()) ) { pFredBack->show(); } - iter = list; + iter.reset(); while ( (pFredBack = iter()) ) { iter.remove(); } assert(list.count()==0); - janeFwdIter = janeList; + janeFwdIter.reset(); while ( (pFredBack = janeFwdIter()) ) { janeFwdIter.remove(); } @@ -91,7 +91,7 @@ main () janeList.add(*pJane); pJane = new jane("JB"); janeList.add(*pJane); - janeBwdIter = janeList; + janeBwdIter.reset(); while ( (pFredBack = janeBwdIter()) ) { janeBwdIter.remove(); } diff --git a/src/cxxTemplates/test/tsSLListBench.cc b/src/cxxTemplates/test/tsSLListBench.cc index be095170e..eacf340c0 100644 --- a/src/cxxTemplates/test/tsSLListBench.cc +++ b/src/cxxTemplates/test/tsSLListBench.cc @@ -32,7 +32,6 @@ private: main () { tsSLList list; - tsSLIter iter(list); fred *pFred; unsigned i; clock_t clk; @@ -45,9 +44,11 @@ main () } clk = clock(); - iter = list; - while ( (pFred = iter()) ) { - pFred->inc(); + { + tsSLIter iter(list); + while ( (pFred = iter()) ) { + pFred->inc(); + } } diff = clock() - clk; delay = diff; @@ -57,9 +58,11 @@ main () pFred = new fred(); clk = clock(); - iter = list; - for (i=0; iinc(); + { + tsSLIter iter(list); + for (i=0; iinc(); + } } diff = clock() - clk; delay = diff; diff --git a/src/cxxTemplates/test/tsSLListTest.cc b/src/cxxTemplates/test/tsSLListTest.cc index 96a89e6c2..f7ea11d03 100644 --- a/src/cxxTemplates/test/tsSLListTest.cc +++ b/src/cxxTemplates/test/tsSLListTest.cc @@ -22,12 +22,10 @@ private: main () { tsSLList list; - tsSLIter iter(list); fred *pFred; fred *pFredII; fred *pFredBack; tsSLList janeList; - tsSLIter janeIter(janeList); jane *pJane; pFred = new fred("A"); @@ -35,8 +33,11 @@ main () list.add(*pFred); list.add(*pFredII); - pFredBack = iter(); - assert(pFredBack == pFredII); + { + tsSLIter iter(list); + pFredBack = iter(); + assert(pFredBack == pFredII); + } list.remove(*pFredII); // removes *pFred !! list.add(*pFred); pFredBack = list.get(); @@ -45,9 +46,11 @@ main () assert (pFredBack == pFredII); list.add(*pFredII); list.add(*pFred); - iter.reset(); - while ( (pFredBack = iter()) ) { - iter.remove(); + { + tsSLIterRm iter(list); + while ( (pFredBack = iter()) ) { + iter.remove(); + } } pFredBack = list.get(); assert (pFredBack == 0); @@ -56,8 +59,11 @@ main () list.add(* new fred("C")); list.add(* new fred("D")); - while ( (pFredBack = iter()) ) { - pFredBack->show(); + { + tsSLIter iter(list); + while ( (pFredBack = iter()) ) { + pFredBack->show(); + } } pJane = new jane("JA"); @@ -65,19 +71,31 @@ main () pJane = new jane("JB"); janeList.add(*pJane); - while ( (pJane = janeIter()) ) { - pJane->show(); + { + tsSLIter janeIter(janeList); + while ( (pJane = janeIter()) ) { + pJane->show(); + } } - while ( (pFredBack = iter()) ) { - pFredBack->show(); + { + tsSLIter iter(list); + while ( (pFredBack = iter()) ) { + pFredBack->show(); + } } - while ( (pFredBack = iter()) ) { - iter.remove(); + { + tsSLIterRm iter(list); + while ( (pFredBack = iter()) ) { + iter.remove(); + } } - pFredBack = iter(); - assert(pFredBack==NULL); + { + tsSLIter iter(list); + pFredBack = iter(); + assert(pFredBack==NULL); + } } diff --git a/src/cxxTemplates/tsDLList.h b/src/cxxTemplates/tsDLList.h index 7e1ef672d..02d729878 100644 --- a/src/cxxTemplates/tsDLList.h +++ b/src/cxxTemplates/tsDLList.h @@ -31,6 +31,9 @@ * * History * $Log$ + * Revision 1.6 1997/01/22 21:13:49 jhill + * fixed class decl order for VMS + * * Revision 1.5 1996/11/02 01:07:19 jhill * many improvements * @@ -54,10 +57,12 @@ // // tsDLNode +// NOTE: T must derive from tsDLNode // template class tsDLNode { friend class tsDLList; +friend class tsDLIterBD; friend class tsDLIter; friend class tsDLFwdIter; friend class tsDLBwdIter; @@ -79,6 +84,7 @@ private: // // tsDLList +// NOTE: T must derive from tsDLNode // template class tsDLList { @@ -293,9 +299,10 @@ public: // and the node number (beginning with zero if // it is) // - int find(T &item); + int find(T &item) const; - T *first(void) const { return pFirst; } + T *first(void) const { return this->pFirst; } + T *last(void) const { return this->pLast; } protected: T *getFirst(void) const { return pFirst; } @@ -306,6 +313,98 @@ private: unsigned itemCount; }; +// +// tsDLIterBD +// (a bi-directional iterator in the style of the STL) +// +template +class tsDLIterBD { +public: + tsDLIterBD () : + pEntry(0) {} + + tsDLIterBD (T *pInitialEntry) : + pEntry(pInitialEntry) {} + + tsDLIterBD (class tsDLIterBD ©In) : + pEntry(copyIn.pEntry) {} + + tsDLIterBD & operator = (T *pNewEntry) + { + this->pEntry = pNewEntry; + return *this; + } + + tsDLIterBD &operator = (const tsDLIterBD ©In) + { + this->pEntry = copyIn.pEntry; + return *this; + } + + int operator == (const tsDLIterBD &rhs) const + { + return (this->pEntry == rhs.pEntry); + } + + int operator != (const tsDLIterBD &rhs) const + { + return (this->pEntry != rhs.pEntry); + } + + T & operator * () const + { + return *this->pEntry; + } + + T * operator -> () const + { + return this->pEntry; + } + + operator T* () const + { + return this->pEntry; + } + + // + // prefix ++ + // + T *operator ++ () + { + return this->pEntry = this->pEntry->tsDLNode::pNext; + } + + // + // postfix ++ + // + T *operator ++ (int) + { + T *pE = this->pEntry; + this->pEntry = this->pEntry->tsDLNode::pNext; + return pE; + } + + // + // prefix -- + // + T *operator -- () + { + return this->pEntry = pEntry->tsDLNode::pPrev; + } + + // + // postfix -- + // + T *operator -- (int) + { + T *pE = this->pEntry; + this->pEntry = pEntry->tsDLNode::pPrev; + return pE; + } +private: + T *pEntry; +}; + // // tsDLIter // @@ -320,7 +419,7 @@ private: template class tsDLIter { public: - tsDLIter (const tsDLList &listIn) : + tsDLIter (const tsDLList & listIn) : pCurrent(0), pList(&listIn) {} void reset () @@ -328,28 +427,28 @@ public: this->pCurrent = 0; } - void reset (tsDLList &listIn) - { - this->reset(); - this->pList = &listIn; - } + void reset (tsDLList &listIn) + { + this->reset(); + this->pList = &listIn; + } - void operator = (tsDLList &listIn) - { - this->reset(listIn); - } + void operator = (tsDLList &listIn) + { + this->reset(listIn); + } T * next () { - T *pCur = this->pCurrent; - if (pCur==0) { - pCur = this->pList->pFirst; - } - else { - pCur = pCur->tsDLNode::pNext; - } - this->pCurrent = pCur; - return pCur; + T *pCur = this->pCurrent; + if (pCur==0) { + pCur = this->pList->pFirst; + } + else { + pCur = pCur->tsDLNode::pNext; + } + this->pCurrent = pCur; + return pCur; } T * prev () @@ -383,8 +482,8 @@ public: } protected: - T *pCurrent; - const tsDLList *pList; + T *pCurrent; + const tsDLList *pList; }; // @@ -412,14 +511,14 @@ public: { this->tsDLIter::reset(); } - void reset (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } - void operator = (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } + void reset (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } + void operator = (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } T * operator () () { return this->tsDLIter::next(); @@ -448,9 +547,10 @@ public: if (pCur) { // - // strip const + // strip const (we didnt declare the + // list const in the constructor) // - tsDLList *pMutableList = + tsDLList * pMutableList = (tsDLList *) this->pList; // @@ -487,18 +587,14 @@ public: tsDLBwdIter(tsDLList &listIn) : tsDLIter(listIn) {} - void reset () - { - this->tsDLIter::reset(); - } - void reset (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } - void operator = (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } + void reset (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } + void operator = (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } T * operator () () { return this->tsDLIter::prev(); @@ -528,9 +624,10 @@ public: if (pCur) { // - // strip const + // strip const (we didnt declare the + // list const in the constructor) // - tsDLList *pMutableList = + tsDLList * pMutableList = (tsDLList *) this->pList; // @@ -555,7 +652,7 @@ public: // it is) // template -inline int tsDLList::find(T &item) +inline int tsDLList::find(T &item) const { tsDLFwdIter iter(*this); tsDLNode *pItem; diff --git a/src/cxxTemplates/tsSLList.h b/src/cxxTemplates/tsSLList.h index c0545a115..60cdc00b5 100644 --- a/src/cxxTemplates/tsSLList.h +++ b/src/cxxTemplates/tsSLList.h @@ -31,6 +31,9 @@ * * History * $Log$ + * Revision 1.6 1997/01/22 21:14:21 jhill + * fixed class decl order for VMS + * * Revision 1.5 1996/11/02 01:07:20 jhill * many improvements * @@ -49,17 +52,15 @@ * */ -#ifndef assert // allows epicsAssert.h -#include -#endif - // // tsSLNode<> +// NOTE: T must derive from tsSLNode // template class tsSLNode { friend class tsSLList; friend class tsSLIter; +friend class tsSLIterRm; public: tsSLNode() : pNext(0) {} @@ -76,6 +77,7 @@ private: // // tsSLList<> +// NOTE: T must derive from tsSLNode // template class tsSLList : public tsSLNode { @@ -138,6 +140,9 @@ public: // // tsSLIter // +// A simple fast single link linked list iterator +// (which must not be called again after it returns NULL) +// // Notes: // 1) No direct access to pCurrent is provided since // this might allow for confusion when an item @@ -146,40 +151,22 @@ public: // template class tsSLIter { +friend class tsSLIterRm; public: - tsSLIter(tsSLList &listIn) : - pCurrent(0), pPrevious(0), pList(&listIn) {} - - void reset() - { - this->pCurrent = 0; - this->pPrevious = 0; - } - - void reset (tsSLList &listIn) - { - this->pList = &listIn; - this->reset(); - } - - void operator = (tsSLList &listIn) - { - this->reset(listIn); - } + tsSLIter(const tsSLList &listIn) : pCurrent(&listIn) {} // // move iterator forward // + // **** NOTE **** + // This may be called continuously until it returns + // NULL. Attempts to call this again after it has + // returned NULL will fail + // T * next () { T *pNewCur; - if (this->pCurrent) { - this->pPrevious = this->pCurrent; - } - else { - this->pPrevious = this->pList; - } - this->pCurrent = pNewCur = this->pPrevious->pNext; + this->pCurrent = pNewCur = this->pCurrent->pNext; return pNewCur; } @@ -191,6 +178,48 @@ public: return this->next(); } +private: + const tsSLNode *pCurrent; +}; + +// +// tsSLIterRm +// (A tsSLIter that allows removing a node) +// +// adds remove method (and does not construct +// with const list) +// +// Notes: +// 1) No direct access to pCurrent is provided since +// this might allow for confusion when an item +// is removed (and pCurrent ends up pointing at +// an item that has been seen before) +// +// +template +class tsSLIterRm : private tsSLIter { +public: + tsSLIterRm(tsSLList &listIn) : + tsSLIter(listIn), pPrevious(0) {} + + // + // move iterator forward + // + T * next () + { + // strip const + this->pPrevious = (tsSLNode *) this->pCurrent; + return tsSLIter::next(); + } + + // + // move iterator forward + // + T * operator () () + { + return this->tsSLIterRm::next(); + } + // // remove current node // (and move current to be the previos item - @@ -203,20 +232,15 @@ public: // This may be called once for each cycle of the // iterator. Attempts to call this twice without // moving the iterator forward inbetween the two - // calls will assert fail + // calls will fail // void remove () { - if (this->pCurrent) { - assert(this->pPrevious); - this->pPrevious->pNext = this->pCurrent->pNext; - this->pCurrent = this->pPrevious; - this->pPrevious = 0; - } + this->pPrevious->pNext = this->pCurrent->pNext; + this->pCurrent = this->pPrevious; + this->pPrevious = 0; } private: - tsSLNode *pCurrent; - tsSLNode *pPrevious; - tsSLList *pList; + tsSLNode *pPrevious; }; diff --git a/src/libCom/cxxTemplates/README b/src/libCom/cxxTemplates/README index 4018aeb1b..566497b6a 100644 --- a/src/libCom/cxxTemplates/README +++ b/src/libCom/cxxTemplates/README @@ -6,4 +6,51 @@ resourceLib.h - hash table template the test subdir contains examples +Since I am using templates the linked lists are type safe +(no casting of pointers ala ellList and dllList). +Also, the node class in embedded in the item on the +list (more efficent use of pool). + +The file resourceLib.h provides a core hashing library +"resTable " where "itemClass" objects +are stored in the hash table and "idClass" is the data type +of the key for the hash table. The identifier class provides +the hash alg. I have provided simple string "stringId" and +unsigned integer "uintId" key types in resourceLib.h. It +is easy to implement a new key class. + +There are examples under cxxTemplate/test. The list/hashing +templates all depend on a particular inheritance hierarchy. +If the inheritance hierarchy is wrong nothing will compile. +For instance, in tsDLList.h the template data type "T" +must derive from tsDLNode. Likewise, in tsSLList.h +"T" must derive from tsSLNode. Likewise, in resourceLib.h +class "T" (the type stored in the hash table) must derive +from class "ID" (the hash table key type) and also derive from +tsSLNode. + +So far, the only confusion I have run into with templates has been: + +1) strange compiler messages - unrelated to cause of course - +when I get the class declaration order wrong (so that the +compiler has trouble instantiating the template). + +2) sun pro/dec/att compilers use a template database and +gnu/msvc++ compilers use explicit template instantiation. +Therefore blocks of code of this sort are required: + +#include "resourceLib.h" // template def +#include "resourceLib.cc" // template functions (that are not inline) +#if defined (EXPL_TEMPL) + // + // From Stroustrups's "The C++ Programming Language" + // Appendix A: r.14.9 + // + // This explicitly instantiates the template class's member + // functions into "templInst.o" + // + template class resTable; + template class resTable; +#endif + diff --git a/src/libCom/cxxTemplates/resourceLib.h b/src/libCom/cxxTemplates/resourceLib.h index 9c0f7641d..beddc585b 100644 --- a/src/libCom/cxxTemplates/resourceLib.h +++ b/src/libCom/cxxTemplates/resourceLib.h @@ -29,6 +29,9 @@ * * History * $Log$ + * Revision 1.7 1996/12/06 22:26:36 jhill + * added auto cleanup of installed classes to destroy + * * Revision 1.6 1996/11/02 01:07:17 jhill * many improvements * @@ -69,7 +72,7 @@ typedef unsigned resTableIndex; #define resTableIndexBitWidth (sizeof(resTableIndex)*CHAR_BIT) // -// class T must derive class ID +// class T must derive from class ID and also from class tsSLNode // // NOTE: Classes installed into this table should have // a virtual destructor so that the delete in ~resTable() will @@ -78,8 +81,6 @@ typedef unsigned resTableIndex; template class resTable { public: - enum resTableDelFlag {rtdfDelete, rtdfNoDelete}; - resTable() : pTable(0), hashIdMask(0), hashIdNBits(0), nInUse(0) {} @@ -94,10 +95,19 @@ public: } } + // + // destroy all res in the table + // void destroyAllEntries(); - void show (unsigned level); + // + // call T::show(level) for each res in the table + // + void show (unsigned level) const; + // + // add a res to the table + // int add (T &res) { // @@ -113,19 +123,34 @@ public: return 0; } + // + // remove a res from the table + // T *remove (const ID &idIn) { tsSLList &list = this->pTable[this->hash(idIn)]; - return this->find(list, idIn, rtdfDelete); + return this->findDelete(list, idIn); } + // + // find an res in the table + // T *lookup (const ID &idIn) { tsSLList &list = this->pTable[this->hash(idIn)]; return this->find(list, idIn); } + // + // Call (pT->*pCB) () for each item in the table + // + // where pT is a pointer to type T and pCB is + // a pointer to a memmber function of T with + // no parameters and returning void + // + void traverse(void (T::*pCB)()); + private: tsSLList *pTable; unsigned hashIdMask; @@ -147,8 +172,7 @@ private: // iterator points to the item found upon return // (or NULL if nothing matching was found) // - T *find (tsSLList &list, const ID &idIn, - resTableDelFlag df=rtdfNoDelete) + T *find (tsSLList &list, const ID &idIn) { tsSLIter iter(list); T *pItem; @@ -157,10 +181,33 @@ private: while ( (pItem = iter()) ) { pId = pItem; if (*pId == idIn) { - if (df==rtdfDelete) { - iter.remove(); - this->nInUse--; - } + break; + } + } + return pItem; + } + + // + // findDelete + // searches from where the iterator points to the + // end of the list for idIn + // + // iterator points to the item found upon return + // (or NULL if nothing matching was found) + // + // removes the item if it finds it + // + T *findDelete (tsSLList &list, const ID &idIn) + { + tsSLIterRm iter(list); + T *pItem; + ID *pId; + + while ( (pItem = iter()) ) { + pId = pItem; + if (*pId == idIn) { + iter.remove(); + this->nInUse--; break; } } @@ -211,16 +258,6 @@ protected: unsigned id; }; -// -// resource with unsigned chronological identifier -// -template -class uintRes : public uintId, public tsSLNode { -friend class uintResTable; -public: - uintRes(unsigned idIn=UINT_MAX) : uintId(idIn) {} -}; - // // special resource table which uses // unsigned integer keys allocated in chronological sequence @@ -232,24 +269,39 @@ class uintResTable : public resTable { public: uintResTable() : allocId(1u) {} // hashing is faster close to zero - // - // NOTE: This detects (and avoids) the case where - // the PV id wraps around and we attempt to have two - // resources with the same id. - // - void installItem(ITEM &item) - { - int resTblStatus; - do { - item.uintId::id = this->allocId++; - resTblStatus = this->add(item); - } - while (resTblStatus); - } + inline void installItem(ITEM &item); private: unsigned allocId; }; +// +// resource with unsigned chronological identifier +// +template +class uintRes : public uintId, public tsSLNode { +friend class uintResTable; +public: + uintRes(unsigned idIn=UINT_MAX) : uintId(idIn) {} +}; + +// +// uintRes::installItem() +// +// NOTE: This detects (and avoids) the case where +// the PV id wraps around and we attempt to have two +// resources with the same id. +// +template +inline void uintResTable::installItem(ITEM &item) +{ + int resTblStatus; + do { + item.uintRes::id = allocId++; + resTblStatus = this->add(item); + } + while (resTblStatus); +} + // // pointer identifier // @@ -292,40 +344,89 @@ private: // // character string identifier // +// NOTE: to be robust in situations where the new() +// in the constructor might fail a careful consumer +// of this class should check to see if the +// stringId::resourceName() below +// returns a valid (non--NULL) string pointer. +// Eventually an exception will be thrown if +// new fails (when this is portable). +// class stringId { public: - stringId (char const * const idIn) : - pStr(new char [strlen(idIn)+1u]) + enum allocationType {copyString, refString}; + + // + // allocCopyString() + // + static inline char * allocCopyString(const char * const pStr) { - if (this->pStr!=NULL) { - strcpy(this->pStr, idIn); + char *pNewStr = new char [strlen(pStr)+1u]; + if (pNewStr) { + strcpy (pNewStr, pStr); } + return pNewStr; } + // + // stringId() constructor + // + // Use typeIn==refString only if the string passed in will exist + // and remain constant during the entire lifespan of the stringId + // object. + // + stringId (char const * const idIn, allocationType typeIn=copyString) : + pStr(typeIn==copyString?allocCopyString(idIn):idIn), + allocType(typeIn) {} + ~ stringId() { - if (this->pStr!=NULL) { - delete [] this->pStr; + if (this->allocType==copyString) { + if (this->pStr!=NULL) { + delete [] (char *) this->pStr; + } } } - + + // + // The hash algorithm is a modification of the algorithm described in + // Fast Hashing of Variable Length Text Strings, Peter K. Pearson, + // Communications of the ACM, June 1990 + // The modifications were designed by Marty Kraimer + // resTableIndex resourceHash(unsigned nBitsId) const { - resTableIndex hashid; - unsigned i; - if (this->pStr==NULL) { return 0u; } - hashid = 0u; - for (i=0u; this->pStr[i]; i++) { - hashid += this->pStr[i] * (i+1u); + unsigned h0 = 0u; + unsigned h1 = 0u; + unsigned c; + unsigned i; + for (i=0u; (c = this->pStr[i]); i++) { + // + // odd + // + if (i&1u) { + h1 = this->T[h1 ^ c]; + } + // + // even + // + else { + h0 = this->T[h0 ^ c]; + } } - hashid = hashid % (1u<=8u) { + h1 = h1 << (nBitsId-8u); + } + return h1 ^ h0; } int operator == (const stringId &idIn) @@ -338,22 +439,27 @@ public: } } + // + // return the pointer to the string + // (also used to test to see if "new()" + // failed in the constructor + // const char * resourceName() { return this->pStr; } - void show (unsigned) + void show (unsigned level) const { - printf ("resource id = %s\n", this->pStr); + if (level>2u) { + printf ("resource id = %s\n", this->pStr); + } } private: - char * const pStr; + const char * const pStr; + allocationType const allocType; + static unsigned char T[256]; }; -#if defined(__SUNPRO_CC) || defined(EXPAND_TEMPLATES_HERE) -# include "resourceLib.cc" -#endif - #endif // INCresourceLibh diff --git a/src/libCom/cxxTemplates/test/minmaxTest.cc b/src/libCom/cxxTemplates/test/minmaxTest.cc new file mode 100644 index 000000000..69671e270 --- /dev/null +++ b/src/libCom/cxxTemplates/test/minmaxTest.cc @@ -0,0 +1,28 @@ + +#include + +#include "minmax.h" + +main () +{ + float f1 = 3.3; + float f2 = 3.4; + float f3; + + f3 = min(f1,f2); + assert(f3==f1); + + f3 = max(f1,f2); + assert(f3==f2); + + int i1 = 3; + int i2 = 4; + int i3; + + i3 = min(i1,i2); + assert(i3==i1); + + i3 = max(i1,i2); + assert(i3==i2); +} + diff --git a/src/libCom/cxxTemplates/test/resourceLibTest.cc b/src/libCom/cxxTemplates/test/resourceLibTest.cc index 05cad10e6..6adc14f49 100644 --- a/src/libCom/cxxTemplates/test/resourceLibTest.cc +++ b/src/libCom/cxxTemplates/test/resourceLibTest.cc @@ -5,6 +5,7 @@ #include #include "resourceLib.h" +#define INSTANCIATE_RES_LIB_STATIC #include "resourceLib.cc" #ifdef SUNOS4 @@ -21,6 +22,10 @@ public: { printf("fred %s\n", pName); } + void destroy() + { + // always on stack so noop + } private: const char * const pName; }; @@ -28,12 +33,28 @@ private: class jane : public stringId, public tsSLNode { public: jane (const char *pNameIn) : stringId(pNameIn) {} + + void testTraverse(); + + void destroy() + { + // always on stack so noop + } }; // -// Sun C++ 4.1 still appears to be lacking support in this area +// jane::testTraverse() // -#if !defined(__SUNPRO_CC) +void jane::testTraverse() +{ + printf("Traverse Test\n"); + this->show(10); +} + +// +// explicitly instantiates on compilers that support this +// +#if defined(EXPL_TEMPL) // // From Stroustrups's "The C++ Programming Language" // Appendix A: r.14.9 @@ -55,14 +76,14 @@ main() resTable strTbl; fred fred1("fred1",0x1000a432); fred fred2("fred2",0x0000a432); - jane jane1("jane1"); - jane jane2("jane2"); + jane jane1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + jane jane2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); fred *pFred; jane *pJane; uintId uintId1(0x1000a432); uintId uintId2(0x0000a432); - stringId strId1("jane1"); - stringId strId2("jane2"); + stringId strId1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + stringId strId2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); int status; status = intTbl.init(8); @@ -157,6 +178,7 @@ main() printf("It took %15.10f u sec per string hash lookup\n", duration); strTbl.show(10u); + strTbl.traverse(&jane::testTraverse); strTbl.remove(strId1); strTbl.remove(strId2); diff --git a/src/libCom/cxxTemplates/test/tsDLListBench.cc b/src/libCom/cxxTemplates/test/tsDLListBench.cc index a37dd938b..45ddf878a 100644 --- a/src/libCom/cxxTemplates/test/tsDLListBench.cc +++ b/src/libCom/cxxTemplates/test/tsDLListBench.cc @@ -40,7 +40,6 @@ main () } clk = clock(); - iter = list; while ( (pFred = iter()) ) { pFred->inc(); } @@ -52,7 +51,6 @@ main () pFred = new fred(); clk = clock(); - iter = list; for (i=0; iinc(); } diff --git a/src/libCom/cxxTemplates/test/tsDLListTest.cc b/src/libCom/cxxTemplates/test/tsDLListTest.cc index cb98997ca..30d630c28 100644 --- a/src/libCom/cxxTemplates/test/tsDLListTest.cc +++ b/src/libCom/cxxTemplates/test/tsDLListTest.cc @@ -70,18 +70,18 @@ main () pJane->show(); } - iter = list; + iter.reset(); while ( (pFredBack = iter()) ) { pFredBack->show(); } - iter = list; + iter.reset(); while ( (pFredBack = iter()) ) { iter.remove(); } assert(list.count()==0); - janeFwdIter = janeList; + janeFwdIter.reset(); while ( (pFredBack = janeFwdIter()) ) { janeFwdIter.remove(); } @@ -91,7 +91,7 @@ main () janeList.add(*pJane); pJane = new jane("JB"); janeList.add(*pJane); - janeBwdIter = janeList; + janeBwdIter.reset(); while ( (pFredBack = janeBwdIter()) ) { janeBwdIter.remove(); } diff --git a/src/libCom/cxxTemplates/test/tsSLListBench.cc b/src/libCom/cxxTemplates/test/tsSLListBench.cc index be095170e..eacf340c0 100644 --- a/src/libCom/cxxTemplates/test/tsSLListBench.cc +++ b/src/libCom/cxxTemplates/test/tsSLListBench.cc @@ -32,7 +32,6 @@ private: main () { tsSLList list; - tsSLIter iter(list); fred *pFred; unsigned i; clock_t clk; @@ -45,9 +44,11 @@ main () } clk = clock(); - iter = list; - while ( (pFred = iter()) ) { - pFred->inc(); + { + tsSLIter iter(list); + while ( (pFred = iter()) ) { + pFred->inc(); + } } diff = clock() - clk; delay = diff; @@ -57,9 +58,11 @@ main () pFred = new fred(); clk = clock(); - iter = list; - for (i=0; iinc(); + { + tsSLIter iter(list); + for (i=0; iinc(); + } } diff = clock() - clk; delay = diff; diff --git a/src/libCom/cxxTemplates/test/tsSLListTest.cc b/src/libCom/cxxTemplates/test/tsSLListTest.cc index 96a89e6c2..f7ea11d03 100644 --- a/src/libCom/cxxTemplates/test/tsSLListTest.cc +++ b/src/libCom/cxxTemplates/test/tsSLListTest.cc @@ -22,12 +22,10 @@ private: main () { tsSLList list; - tsSLIter iter(list); fred *pFred; fred *pFredII; fred *pFredBack; tsSLList janeList; - tsSLIter janeIter(janeList); jane *pJane; pFred = new fred("A"); @@ -35,8 +33,11 @@ main () list.add(*pFred); list.add(*pFredII); - pFredBack = iter(); - assert(pFredBack == pFredII); + { + tsSLIter iter(list); + pFredBack = iter(); + assert(pFredBack == pFredII); + } list.remove(*pFredII); // removes *pFred !! list.add(*pFred); pFredBack = list.get(); @@ -45,9 +46,11 @@ main () assert (pFredBack == pFredII); list.add(*pFredII); list.add(*pFred); - iter.reset(); - while ( (pFredBack = iter()) ) { - iter.remove(); + { + tsSLIterRm iter(list); + while ( (pFredBack = iter()) ) { + iter.remove(); + } } pFredBack = list.get(); assert (pFredBack == 0); @@ -56,8 +59,11 @@ main () list.add(* new fred("C")); list.add(* new fred("D")); - while ( (pFredBack = iter()) ) { - pFredBack->show(); + { + tsSLIter iter(list); + while ( (pFredBack = iter()) ) { + pFredBack->show(); + } } pJane = new jane("JA"); @@ -65,19 +71,31 @@ main () pJane = new jane("JB"); janeList.add(*pJane); - while ( (pJane = janeIter()) ) { - pJane->show(); + { + tsSLIter janeIter(janeList); + while ( (pJane = janeIter()) ) { + pJane->show(); + } } - while ( (pFredBack = iter()) ) { - pFredBack->show(); + { + tsSLIter iter(list); + while ( (pFredBack = iter()) ) { + pFredBack->show(); + } } - while ( (pFredBack = iter()) ) { - iter.remove(); + { + tsSLIterRm iter(list); + while ( (pFredBack = iter()) ) { + iter.remove(); + } } - pFredBack = iter(); - assert(pFredBack==NULL); + { + tsSLIter iter(list); + pFredBack = iter(); + assert(pFredBack==NULL); + } } diff --git a/src/libCom/cxxTemplates/tsDLList.h b/src/libCom/cxxTemplates/tsDLList.h index 7e1ef672d..02d729878 100644 --- a/src/libCom/cxxTemplates/tsDLList.h +++ b/src/libCom/cxxTemplates/tsDLList.h @@ -31,6 +31,9 @@ * * History * $Log$ + * Revision 1.6 1997/01/22 21:13:49 jhill + * fixed class decl order for VMS + * * Revision 1.5 1996/11/02 01:07:19 jhill * many improvements * @@ -54,10 +57,12 @@ // // tsDLNode +// NOTE: T must derive from tsDLNode // template class tsDLNode { friend class tsDLList; +friend class tsDLIterBD; friend class tsDLIter; friend class tsDLFwdIter; friend class tsDLBwdIter; @@ -79,6 +84,7 @@ private: // // tsDLList +// NOTE: T must derive from tsDLNode // template class tsDLList { @@ -293,9 +299,10 @@ public: // and the node number (beginning with zero if // it is) // - int find(T &item); + int find(T &item) const; - T *first(void) const { return pFirst; } + T *first(void) const { return this->pFirst; } + T *last(void) const { return this->pLast; } protected: T *getFirst(void) const { return pFirst; } @@ -306,6 +313,98 @@ private: unsigned itemCount; }; +// +// tsDLIterBD +// (a bi-directional iterator in the style of the STL) +// +template +class tsDLIterBD { +public: + tsDLIterBD () : + pEntry(0) {} + + tsDLIterBD (T *pInitialEntry) : + pEntry(pInitialEntry) {} + + tsDLIterBD (class tsDLIterBD ©In) : + pEntry(copyIn.pEntry) {} + + tsDLIterBD & operator = (T *pNewEntry) + { + this->pEntry = pNewEntry; + return *this; + } + + tsDLIterBD &operator = (const tsDLIterBD ©In) + { + this->pEntry = copyIn.pEntry; + return *this; + } + + int operator == (const tsDLIterBD &rhs) const + { + return (this->pEntry == rhs.pEntry); + } + + int operator != (const tsDLIterBD &rhs) const + { + return (this->pEntry != rhs.pEntry); + } + + T & operator * () const + { + return *this->pEntry; + } + + T * operator -> () const + { + return this->pEntry; + } + + operator T* () const + { + return this->pEntry; + } + + // + // prefix ++ + // + T *operator ++ () + { + return this->pEntry = this->pEntry->tsDLNode::pNext; + } + + // + // postfix ++ + // + T *operator ++ (int) + { + T *pE = this->pEntry; + this->pEntry = this->pEntry->tsDLNode::pNext; + return pE; + } + + // + // prefix -- + // + T *operator -- () + { + return this->pEntry = pEntry->tsDLNode::pPrev; + } + + // + // postfix -- + // + T *operator -- (int) + { + T *pE = this->pEntry; + this->pEntry = pEntry->tsDLNode::pPrev; + return pE; + } +private: + T *pEntry; +}; + // // tsDLIter // @@ -320,7 +419,7 @@ private: template class tsDLIter { public: - tsDLIter (const tsDLList &listIn) : + tsDLIter (const tsDLList & listIn) : pCurrent(0), pList(&listIn) {} void reset () @@ -328,28 +427,28 @@ public: this->pCurrent = 0; } - void reset (tsDLList &listIn) - { - this->reset(); - this->pList = &listIn; - } + void reset (tsDLList &listIn) + { + this->reset(); + this->pList = &listIn; + } - void operator = (tsDLList &listIn) - { - this->reset(listIn); - } + void operator = (tsDLList &listIn) + { + this->reset(listIn); + } T * next () { - T *pCur = this->pCurrent; - if (pCur==0) { - pCur = this->pList->pFirst; - } - else { - pCur = pCur->tsDLNode::pNext; - } - this->pCurrent = pCur; - return pCur; + T *pCur = this->pCurrent; + if (pCur==0) { + pCur = this->pList->pFirst; + } + else { + pCur = pCur->tsDLNode::pNext; + } + this->pCurrent = pCur; + return pCur; } T * prev () @@ -383,8 +482,8 @@ public: } protected: - T *pCurrent; - const tsDLList *pList; + T *pCurrent; + const tsDLList *pList; }; // @@ -412,14 +511,14 @@ public: { this->tsDLIter::reset(); } - void reset (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } - void operator = (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } + void reset (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } + void operator = (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } T * operator () () { return this->tsDLIter::next(); @@ -448,9 +547,10 @@ public: if (pCur) { // - // strip const + // strip const (we didnt declare the + // list const in the constructor) // - tsDLList *pMutableList = + tsDLList * pMutableList = (tsDLList *) this->pList; // @@ -487,18 +587,14 @@ public: tsDLBwdIter(tsDLList &listIn) : tsDLIter(listIn) {} - void reset () - { - this->tsDLIter::reset(); - } - void reset (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } - void operator = (tsDLList &listIn) - { - this->tsDLIter::reset(listIn); - } + void reset (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } + void operator = (tsDLList &listIn) + { + this->tsDLIter::reset(listIn); + } T * operator () () { return this->tsDLIter::prev(); @@ -528,9 +624,10 @@ public: if (pCur) { // - // strip const + // strip const (we didnt declare the + // list const in the constructor) // - tsDLList *pMutableList = + tsDLList * pMutableList = (tsDLList *) this->pList; // @@ -555,7 +652,7 @@ public: // it is) // template -inline int tsDLList::find(T &item) +inline int tsDLList::find(T &item) const { tsDLFwdIter iter(*this); tsDLNode *pItem; diff --git a/src/libCom/cxxTemplates/tsSLList.h b/src/libCom/cxxTemplates/tsSLList.h index c0545a115..60cdc00b5 100644 --- a/src/libCom/cxxTemplates/tsSLList.h +++ b/src/libCom/cxxTemplates/tsSLList.h @@ -31,6 +31,9 @@ * * History * $Log$ + * Revision 1.6 1997/01/22 21:14:21 jhill + * fixed class decl order for VMS + * * Revision 1.5 1996/11/02 01:07:20 jhill * many improvements * @@ -49,17 +52,15 @@ * */ -#ifndef assert // allows epicsAssert.h -#include -#endif - // // tsSLNode<> +// NOTE: T must derive from tsSLNode // template class tsSLNode { friend class tsSLList; friend class tsSLIter; +friend class tsSLIterRm; public: tsSLNode() : pNext(0) {} @@ -76,6 +77,7 @@ private: // // tsSLList<> +// NOTE: T must derive from tsSLNode // template class tsSLList : public tsSLNode { @@ -138,6 +140,9 @@ public: // // tsSLIter // +// A simple fast single link linked list iterator +// (which must not be called again after it returns NULL) +// // Notes: // 1) No direct access to pCurrent is provided since // this might allow for confusion when an item @@ -146,40 +151,22 @@ public: // template class tsSLIter { +friend class tsSLIterRm; public: - tsSLIter(tsSLList &listIn) : - pCurrent(0), pPrevious(0), pList(&listIn) {} - - void reset() - { - this->pCurrent = 0; - this->pPrevious = 0; - } - - void reset (tsSLList &listIn) - { - this->pList = &listIn; - this->reset(); - } - - void operator = (tsSLList &listIn) - { - this->reset(listIn); - } + tsSLIter(const tsSLList &listIn) : pCurrent(&listIn) {} // // move iterator forward // + // **** NOTE **** + // This may be called continuously until it returns + // NULL. Attempts to call this again after it has + // returned NULL will fail + // T * next () { T *pNewCur; - if (this->pCurrent) { - this->pPrevious = this->pCurrent; - } - else { - this->pPrevious = this->pList; - } - this->pCurrent = pNewCur = this->pPrevious->pNext; + this->pCurrent = pNewCur = this->pCurrent->pNext; return pNewCur; } @@ -191,6 +178,48 @@ public: return this->next(); } +private: + const tsSLNode *pCurrent; +}; + +// +// tsSLIterRm +// (A tsSLIter that allows removing a node) +// +// adds remove method (and does not construct +// with const list) +// +// Notes: +// 1) No direct access to pCurrent is provided since +// this might allow for confusion when an item +// is removed (and pCurrent ends up pointing at +// an item that has been seen before) +// +// +template +class tsSLIterRm : private tsSLIter { +public: + tsSLIterRm(tsSLList &listIn) : + tsSLIter(listIn), pPrevious(0) {} + + // + // move iterator forward + // + T * next () + { + // strip const + this->pPrevious = (tsSLNode *) this->pCurrent; + return tsSLIter::next(); + } + + // + // move iterator forward + // + T * operator () () + { + return this->tsSLIterRm::next(); + } + // // remove current node // (and move current to be the previos item - @@ -203,20 +232,15 @@ public: // This may be called once for each cycle of the // iterator. Attempts to call this twice without // moving the iterator forward inbetween the two - // calls will assert fail + // calls will fail // void remove () { - if (this->pCurrent) { - assert(this->pPrevious); - this->pPrevious->pNext = this->pCurrent->pNext; - this->pCurrent = this->pPrevious; - this->pPrevious = 0; - } + this->pPrevious->pNext = this->pCurrent->pNext; + this->pCurrent = this->pPrevious; + this->pPrevious = 0; } private: - tsSLNode *pCurrent; - tsSLNode *pPrevious; - tsSLList *pList; + tsSLNode *pPrevious; };