diff --git a/pdbApp/pdb.cpp b/pdbApp/pdb.cpp index c6eab06..783b4d4 100644 --- a/pdbApp/pdb.cpp +++ b/pdbApp/pdb.cpp @@ -12,7 +12,7 @@ namespace pvd = epics::pvData; namespace pva = epics::pvAccess; -int PDBProviderDebug = 0; +int PDBProviderDebug = 3; namespace { struct GroupMemberInfo { @@ -66,14 +66,14 @@ PDBProvider::PDBProvider() const char *next = strchr(pos, '|'), *equal= strchr(pos, '='); - if(!equal || equal>next) + if(!equal || (next && equal>next)) throw std::runtime_error("expected '='"); - info.members.push_back(GroupMemberInfo(recbase + (next ? std::string(equal+1, next-pos) : std::string(equal+1)), + info.members.push_back(GroupMemberInfo(recbase + (next ? std::string(equal+1, next-equal-1) : std::string(equal+1)), std::string(pos, equal-pos))); if(PDBProviderDebug>2) { - fprintf(stderr, " pdbGroup '%s' add %s=%s\n", + fprintf(stderr, " pdbGroup '%s' add '%s'='%s'\n", info.name.c_str(), info.members.back().pvfldname.c_str(), info.members.back().pvname.c_str()); @@ -104,19 +104,26 @@ PDBProvider::PDBProvider() pvd::shared_vector chans(nchans); std::vector records(nchans); + pvd::FieldBuilderPtr builder(pvd::getFieldCreate()->createFieldBuilder()); + for(size_t i=0; iadd(mem.pvfldname, PVIF::dtype(chan)); + pv->attachments[i] = mem.pvfldname; records[i] = dbChannelRecord(chan); chans[i].swap(chan); } + + pv->fielddesc = builder->createStructure(); pv->chan.swap(chans); - pv->locker.reset(dbLockerAlloc(&records[0], records.size(), 0)); - if(!pv->locker.get()) - throw std::runtime_error("Failed to create dbLocker"); + DBManyLock L(&records[0], records.size(), 0); + pv->locker.swap(L); persist_pv_map[info.name] = pv; diff --git a/pdbApp/pdbgroup.cpp b/pdbApp/pdbgroup.cpp index 4ef44d5..f3c2ad2 100644 --- a/pdbApp/pdbgroup.cpp +++ b/pdbApp/pdbgroup.cpp @@ -1,3 +1,5 @@ + +#include #include #include "pdbgroup.h" @@ -6,6 +8,18 @@ namespace pvd = epics::pvData; namespace pva = epics::pvAccess; +size_t PDBGroupPV::ninstances; + +PDBGroupPV::PDBGroupPV() +{ + epics::atomic::increment(ninstances); +} + +PDBGroupPV::~PDBGroupPV() +{ + epics::atomic::decrement(ninstances); +} + pva::Channel::shared_pointer PDBGroupPV::connect(const std::tr1::shared_ptr& prov, const pva::ChannelRequester::shared_pointer& req) @@ -18,8 +32,8 @@ PDBGroupChannel::PDBGroupChannel(const PDBGroupPV::shared_pointer& pv, const std::tr1::shared_ptr& prov, const pva::ChannelRequester::shared_pointer& req) :BaseChannel(pv->name, prov, req, pv->fielddesc) -{ -} + ,pv(pv) +{} void PDBGroupChannel::printInfo(std::ostream& out) { @@ -71,7 +85,7 @@ void PDBGroupGet::get() changed->clear(); if(atomic) { - DBManyLocker L(channel->pv->locker.get()); + DBManyLocker L(channel->pv->locker); for(size_t i=0; iput(*changed, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, NULL); } else { diff --git a/pdbApp/pdbgroup.h b/pdbApp/pdbgroup.h index 6c29518..fbfca47 100644 --- a/pdbApp/pdbgroup.h +++ b/pdbApp/pdbgroup.h @@ -19,10 +19,12 @@ struct PDBGroupPV : public PDBPV, public std::tr1::enable_shared_from_this chan; std::vector attachments; - std::auto_ptr locker; + DBManyLock locker; - PDBGroupPV() {} - virtual ~PDBGroupPV() {} + static size_t ninstances; + + PDBGroupPV(); + virtual ~PDBGroupPV(); virtual epics::pvAccess::Channel::shared_pointer diff --git a/pdbApp/pdbsingle.cpp b/pdbApp/pdbsingle.cpp index b7c4c55..5b8b0a1 100644 --- a/pdbApp/pdbsingle.cpp +++ b/pdbApp/pdbsingle.cpp @@ -1,4 +1,5 @@ #include +#include #include "pdbsingle.h" #include "pdb.h" @@ -6,12 +7,20 @@ namespace pvd = epics::pvData; namespace pva = epics::pvAccess; +size_t PDBSinglePV::ninstances; + PDBSinglePV::PDBSinglePV(DBCH& chan, const PDBProvider::shared_pointer& prov) :provider(prov) { this->chan.swap(chan); fielddesc = PVIF::dtype(this->chan); + epics::atomic::increment(ninstances); +} + +PDBSinglePV::~PDBSinglePV() +{ + epics::atomic::decrement(ninstances); } pva::Channel::shared_pointer diff --git a/pdbApp/pdbsingle.h b/pdbApp/pdbsingle.h index 5830566..12de7e9 100644 --- a/pdbApp/pdbsingle.h +++ b/pdbApp/pdbsingle.h @@ -18,9 +18,11 @@ struct PDBSinglePV : public PDBPV, public std::tr1::enable_shared_from_this& prov, diff --git a/pdbApp/pvif.h b/pdbApp/pvif.h index c053f1b..31b9a25 100644 --- a/pdbApp/pvif.h +++ b/pdbApp/pvif.h @@ -55,7 +55,12 @@ struct pdbRecordIterator { { dbInitEntry(pdbbase, &ent); m_done = dbFirstRecordType(&ent)!=0; - if(!m_done) m_done = dbFirstRecord(&ent)!=0; + while(!m_done) { + if(dbFirstRecord(&ent)==0) + break; + // not instances of this type + m_done = dbNextRecordType(&ent)!=0; + } } ~pdbRecordIterator() { @@ -63,8 +68,17 @@ struct pdbRecordIterator { } bool done() const { return m_done; } bool next() { - m_done = dbNextRecord(&ent)!=0; - if(m_done) m_done = dbNextRecordType(&ent)!=0; + if(!m_done && dbNextRecord(&ent)!=0) + { + // done with this recordType + while(true) { + m_done = dbNextRecordType(&ent)!=0; + if(m_done) break; + if(dbFirstRecord(&ent)==0) + break; + // not instances of this type + } + } return m_done; } dbCommon* record() const { @@ -92,6 +106,20 @@ struct DBScanLocker { dbScanUnlock(prec); } }; +struct DBManyLock +{ + dbLocker *plock; + DBManyLock() :plock(NULL) {} + DBManyLock(dbCommon **precs, size_t nrecs, unsigned flags=0) + :plock(dbLockerAlloc(precs, nrecs, flags)) + { + if(!plock) throw std::invalid_argument("Failed to create locker"); + } + ~DBManyLock() { if(plock) dbLockerFree(plock); } + void swap(DBManyLock& O) { std::swap(plock, O.plock); } + operator dbLocker*() { return plock; } +}; + struct DBManyLocker { dbLocker *plock; diff --git a/testApp/Makefile b/testApp/Makefile index 6dfb86a..7ef7666 100644 --- a/testApp/Makefile +++ b/testApp/Makefile @@ -7,6 +7,7 @@ include $(TOP)/configure/CONFIG USR_CPPFLAGS += -I$(TOP)/p2pApp USR_CPPFLAGS += -I$(TOP)/pdbApp +USR_CPPFLAGS += -I$(TOP)/common TARGETS += $(COMMON_DIR)/p2pTestIoc.dbd p2pTestIoc_DBD += base.dbd diff --git a/testApp/testpdb.cpp b/testApp/testpdb.cpp index b24f437..390f74b 100644 --- a/testApp/testpdb.cpp +++ b/testApp/testpdb.cpp @@ -1,12 +1,15 @@ #include +#include #include #include #include "utilities.h" #include "pdb.h" +#include "pdbgroup.h" +#include "pdbsingle.h" namespace pvd = epics::pvData; namespace pva = epics::pvAccess; @@ -21,8 +24,7 @@ void testFieldEqual(const pvd::PVStructurePtr& val, const char *name, typename P testFail("field '%s' with type %s does not exist", name, typeid(PVD).name()); } else { typename PVD::value_type actual(fval->get()); - std::cout<<"# expect='"<value); if(!greq->value) testAbort("'%s' get w/o data", name); + + get->destroy(); + chan->destroy(); + return greq->value; } -void testSingle(const PDBProvider::shared_pointer& prov) +void testSingleGet(const PDBProvider::shared_pointer& prov) { + testDiag("test single get"); pvd::PVStructurePtr value; value = pvget(prov, "rec1", false); @@ -81,6 +88,26 @@ void testSingle(const PDBProvider::shared_pointer& prov) testFieldEqual(value, "value", 10); } +void testGroupGet(const PDBProvider::shared_pointer& prov) +{ + testDiag("test group get"); + pvd::PVStructurePtr value; + + testDiag("get non-atomic"); + value = pvget(prov, "grp1", false); + testFieldEqual(value, "fld1.value", 3.0); + testFieldEqual(value, "fld2.value", 30); + testFieldEqual(value, "fld3.value", 4.0); + testFieldEqual(value, "fld4.value", 40); + + testDiag("get atomic"); + value = pvget(prov, "grp1", true); + testFieldEqual(value, "fld1.value", 3.0); + testFieldEqual(value, "fld2.value", 30); + testFieldEqual(value, "fld3.value", 4.0); + testFieldEqual(value, "fld4.value", 40); +} + } // namespace extern "C" @@ -98,8 +125,14 @@ MAIN(testpdb) IOC.init(); - PDBProvider::shared_pointer prov(new PDBProvider()); - testSingle(prov); + { + PDBProvider::shared_pointer prov(new PDBProvider()); + testSingleGet(prov); + testGroupGet(prov); + } + testDiag("check to see that all dbChannel are closed before IOC shuts down"); + testEqual(epics::atomic::get(PDBGroupPV::ninstances), 0u); + testEqual(epics::atomic::get(PDBSinglePV::ninstances), 0u); }catch(std::exception& e){ PRINT_EXCEPTION(e); diff --git a/testApp/utilities.cpp b/testApp/utilities.cpp index 2cd0372..77b2690 100644 --- a/testApp/utilities.cpp +++ b/testApp/utilities.cpp @@ -26,7 +26,7 @@ TestChannelRequester::~TestChannelRequester() void TestChannelRequester::channelCreated(const pvd::Status& status, pva::Channel::shared_pointer const & channel) { - testDiag("channelCreated %s", channel->getChannelName().c_str()); + testDiag("channelCreated %s", channel ? channel->getChannelName().c_str() : ""); Guard G(lock); laststate = pva::Channel::CONNECTED; this->status = status; diff --git a/testApp/utilities.h b/testApp/utilities.h index 4881d60..a95be38 100644 --- a/testApp/utilities.h +++ b/testApp/utilities.h @@ -50,6 +50,15 @@ void test_method(const char *kname, const char *mname) // Construct an instance and run one method #define TEST_METHOD(klass, method) test_method(#klass, #method) +template +void testEqualx(const char *nLHS, const char *nRHS, LHS l, RHS r) +{ + std::ostringstream msg; + msg< struct ScalarAccessor {