diff --git a/testApp/testmon.cpp b/testApp/testmon.cpp index 1a36d2a..0c73ab3 100644 --- a/testApp/testmon.cpp +++ b/testApp/testmon.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -62,6 +63,7 @@ struct TestMonitor { testAbort("channel \"test1\" not connected"); test1_x = 1; test1_y = 2; + test1->post(); } ~TestMonitor() @@ -89,7 +91,7 @@ struct TestMonitor { if(elem) mon->release(elem); - testOk1(!!mon->poll()); + testOk1(!mon->poll()); mon->destroy(); } @@ -125,13 +127,15 @@ struct TestMonitor { testOk1(!!elem2.get()); testOk1(elem!=elem2); testOk1(elem && elem->pvStructurePtr->getSubFieldT("x")->get()==1); + testOk1(elem && elem->pvStructurePtr->getSubFieldT("y")->get()==2); testOk1(elem2 && elem2->pvStructurePtr->getSubFieldT("x")->get()==1); + testOk1(elem2 && elem2->pvStructurePtr->getSubFieldT("y")->get()==2); if(elem) mon->release(elem); if(elem2) mon2->release(elem2); - testOk1(!!mon->poll()); - testOk1(!!mon2->poll()); + testOk1(!mon->poll()); + testOk1(!mon2->poll()); testDiag("explicitly push an update"); test1_x = 42; @@ -153,8 +157,8 @@ struct TestMonitor { if(elem) mon->release(elem); if(elem2) mon2->release(elem2); - testOk1(!!mon->poll()); - testOk1(!!mon2->poll()); + testOk1(!mon->poll()); + testOk1(!mon2->poll()); mon->destroy(); mon2->destroy(); @@ -169,5 +173,13 @@ MAIN(testmon) TEST_METHOD(TestMonitor, test_event); TEST_METHOD(TestMonitor, test_share); TestProvider::testCounts(); + int ok = 1; + size_t temp; +#define TESTC(name) temp=epicsAtomicGetSizeT(&name::num_instances); ok &= temp==0; testDiag("num. live " #name " %u", (unsigned)temp) + TESTC(ChannelCacheEntry); + TESTC(MonitorCacheEntry); + TESTC(MonitorUser); +#undef TESTC + testOk(temp, "All instances free'd"); return testDone(); } diff --git a/testApp/testtest.cpp b/testApp/testtest.cpp index fbe12f4..2f0ff6b 100644 --- a/testApp/testtest.cpp +++ b/testApp/testtest.cpp @@ -32,9 +32,12 @@ void testmonitor() ->add("y", pvd::pvInt) ->createStructure())); - ScalarAccessor x(pv->value, "x"); + ScalarAccessor x(pv->value, "x"), + y(pv->value, "y"); x = 42; + y = 15; + pv->post(); testDiag("Create channel"); TestChannelRequester::shared_pointer creq(new TestChannelRequester); @@ -74,6 +77,7 @@ void testmonitor() testOk1(!!elem.get()); testOk1(elem && elem->pvStructurePtr->getSubFieldT("x")->get()==42); + testOk1(elem && elem->pvStructurePtr->getSubFieldT("y")->get()==15); testOk1(elem && elem->changedBitSet->nextSetBit(0)==0); // initial update shows all changed testOk1(elem && elem->changedBitSet->nextSetBit(1)==-1); testOk1(elem && elem->overrunBitSet->isEmpty()); @@ -82,10 +86,11 @@ void testmonitor() testDiag("ensure start() queues only one"); testOk1(!mon->poll()); - testDiag("Change a field"); + testDiag("Change both fields, only push 'x'"); x = 43; + y = 16; pvd::BitSet changed; - changed.set(1); + changed.set(1); // only notify that 'x' changed pv->post(changed); testOk1(mreq->eventCnt==1); @@ -94,6 +99,7 @@ void testmonitor() testOk1(!!elem.get()); testOk1(elem && elem->pvStructurePtr->getSubFieldT("x")->get()==43); + testOk1(elem && elem->pvStructurePtr->getSubFieldT("y")->get()==15); testOk1(elem && elem->changedBitSet->nextSetBit(0)==1); testOk1(elem && elem->changedBitSet->nextSetBit(2)==-1); testOk1(elem && elem->overrunBitSet->isEmpty()); @@ -121,6 +127,7 @@ void testmonitor() testOk1(!!elem.get()); testOk1(elem && elem->pvStructurePtr->getSubFieldT("x")->get()==44); + testOk1(elem && elem->pvStructurePtr->getSubFieldT("y")->get()==15); testOk1(elem && elem->changedBitSet->nextSetBit(0)==1); testOk1(elem && elem->overrunBitSet->isEmpty()); if(elem) mon->release(elem); // overflow element is queued here @@ -129,6 +136,7 @@ void testmonitor() testOk1(!!elem.get()); testOk1(elem && elem->pvStructurePtr->getSubFieldT("x")->get()==45); + testOk1(elem && elem->pvStructurePtr->getSubFieldT("y")->get()==15); testOk1(elem && elem->changedBitSet->nextSetBit(0)==1); testOk1(elem && elem->overrunBitSet->isEmpty()); if(elem) mon->release(elem); @@ -137,6 +145,7 @@ void testmonitor() testOk1(!!elem.get()); testOk1(elem && elem->pvStructurePtr->getSubFieldT("x")->get()==47); + testOk1(elem && elem->pvStructurePtr->getSubFieldT("y")->get()==15); testOk1(elem && elem->changedBitSet->nextSetBit(0)==1); testOk1(elem && elem->overrunBitSet->nextSetBit(0)==1); testOk1(elem && elem->overrunBitSet->nextSetBit(2)==-1); diff --git a/testApp/utilities.cpp b/testApp/utilities.cpp index 5f9c823..9a411d1 100644 --- a/testApp/utilities.cpp +++ b/testApp/utilities.cpp @@ -285,6 +285,7 @@ TestPVMonitor::TestPVMonitor(const TestPVChannel::shared_pointer& ch, pvd::MonitorElementPtr elem(new pvd::MonitorElement(fact->createPVStructure(channel->pv->dtype))); free.push_back(elem); } + overflow.reset(new pvd::MonitorElement(fact->createPVStructure(channel->pv->dtype))); epicsAtomicIncrSizeT(&countTestPVMonitor); } @@ -309,48 +310,51 @@ pvd::Status TestPVMonitor::start() { if(debugdebug) testDiag("TestPVMonitor::start %p", this); - { - Guard G(channel->pv->provider->lock); - if(finalize && buffer.empty()) - return pvd::Status(); - if(running) - return pvd::Status(); - running = true; + Guard G(channel->pv->provider->lock); + if(finalize && buffer.empty()) + return pvd::Status(); - if(this->buffer.empty()) { - needWakeup = true; - if(debugdebug) - testDiag(" need wakeup"); - } + if(running) + return pvd::Status(); + running = true; - if(!this->free.empty()) { - pvd::MonitorElementPtr monitorElement(this->free.front()); + // overflow element does double duty to hold this monitor's copy + overflow->pvStructurePtr->copyUnchecked(*channel->pv->value); - if(changedMask.isEmpty()) { - changedMask.set(0); // initial update has all changed - overflowMask.clear(); - } - - monitorElement->pvStructurePtr->copyUnchecked(*channel->pv->value); - *monitorElement->changedBitSet = changedMask; - *monitorElement->overrunBitSet = overflowMask; - changedMask.clear(); - overflowMask.clear(); - - buffer.push_back(monitorElement); - this->free.pop_front(); - if(debugdebug) - testDiag(" push current"); - - } else { - inoverflow = true; - changedMask.clear(); - changedMask.set(0); - if(debugdebug) - testDiag(" push overflow"); - } + if(this->buffer.empty()) { + needWakeup = true; + if(debugdebug) + testDiag(" need wakeup"); } + + if(!this->free.empty()) { + pvd::MonitorElementPtr monitorElement(this->free.front()); + + if(overflow->changedBitSet->isEmpty()) { + overflow->changedBitSet->set(0); // initial update has all changed + overflow->overrunBitSet->clear(); + } + + monitorElement->pvStructurePtr->copyUnchecked(*overflow->pvStructurePtr); + *monitorElement->changedBitSet = *overflow->changedBitSet; + *monitorElement->overrunBitSet = *overflow->overrunBitSet; + overflow->changedBitSet->clear(); + overflow->overrunBitSet->clear(); + + buffer.push_back(monitorElement); + this->free.pop_front(); + if(debugdebug) + testDiag(" push current"); + + } else { + inoverflow = true; + overflow->changedBitSet->clear(); + overflow->changedBitSet->set(0); + if(debugdebug) + testDiag(" push overflow"); + } + return pvd::Status(); } @@ -385,12 +389,12 @@ void TestPVMonitor::release(pvd::MonitorElementPtr const & monitorElement) assert(!buffer.empty()); assert(this->free.empty()); - monitorElement->pvStructurePtr->copyUnchecked(*channel->pv->value); - *monitorElement->changedBitSet = changedMask; - *monitorElement->overrunBitSet = overflowMask; + monitorElement->pvStructurePtr->copyUnchecked(*overflow->pvStructurePtr); + *monitorElement->changedBitSet = *overflow->changedBitSet; + *monitorElement->overrunBitSet = *overflow->overrunBitSet; - changedMask.clear(); - overflowMask.clear(); + overflow->changedBitSet->clear(); + overflow->overrunBitSet->clear(); buffer.push_back(monitorElement); testDiag("TestPVMonitor::overflow resume %p %p", this, monitorElement.get()); @@ -419,11 +423,19 @@ TestPV::~TestPV() epicsAtomicDecrSizeT(&countTestPV); } +void TestPV::post(bool notify) +{ + pvd::BitSet changed; + changed.set(0); // all + post(changed, notify); +} + void TestPV::post(const pvd::BitSet& changed, bool notify) { if(debugdebug) testDiag("post %s %d", name.c_str(), (int)notify); Guard G(provider->lock); + channels_t::vector_type toupdate(channels.lock_vector()); FOREACH(it, end, toupdate) // channel @@ -435,19 +447,22 @@ void TestPV::post(const pvd::BitSet& changed, bool notify) { TestPVMonitor *mon = it2->get(); - if(mon->inoverflow || mon->free.empty()) { + mon->overflow->pvStructurePtr->copyUnchecked(*value, changed); + + if(mon->free.empty()) { mon->inoverflow = true; - mon->overflowMask.or_and(mon->changedMask, changed); // oflow = prev_changed & new_changed - mon->changedMask |= changed; + mon->overflow->overrunBitSet->or_and(*mon->overflow->changedBitSet, changed); // oflow = prev_changed & new_changed + *mon->overflow->changedBitSet |= changed; if(debugdebug) testDiag("overflow"); } else { + assert(!mon->inoverflow); if(mon->buffer.empty()) mon->needWakeup = true; AUTO_REF(elem, mon->free.front()); - elem->pvStructurePtr->copyUnchecked(*value); + elem->pvStructurePtr->copyUnchecked(*mon->overflow->pvStructurePtr); *elem->changedBitSet = changed; elem->overrunBitSet->clear(); // redundant/paranoia @@ -618,8 +633,5 @@ void TestProvider::testCounts() TESTC(TestPVChannel); TESTC(TestPVMonitor); #undef TESTC - if(ok) - testPass("All instances free'd"); - else - testFail("Some instances leaked"); + testOk(ok, "All instances free'd"); } diff --git a/testApp/utilities.h b/testApp/utilities.h index 6eb7d13..30fce8f 100644 --- a/testApp/utilities.h +++ b/testApp/utilities.h @@ -182,7 +182,7 @@ struct TestPVMonitor : public epics::pvData::Monitor virtual void release(epics::pvData::MonitorElementPtr const & monitorElement); std::deque buffer, free; - epics::pvData::BitSet changedMask, overflowMask; + epics::pvData::MonitorElementPtr overflow; }; struct TestPV @@ -201,6 +201,7 @@ struct TestPV const epics::pvData::StructureConstPtr& dtype); ~TestPV(); + void post(bool notify = true); void post(const epics::pvData::BitSet& changed, bool notify = true); void disconnect();