diff --git a/ioc/groupsource.cpp b/ioc/groupsource.cpp index 0c0e092..7f22298 100644 --- a/ioc/groupsource.cpp +++ b/ioc/groupsource.cpp @@ -128,9 +128,11 @@ void GroupSource::createRequestAndSubscriptionHandlers(std::unique_ptr(group)); channelControl - ->onSubscribe([this, subscriptionContext](std::unique_ptr&& subscriptionOperation) { + ->onSubscribe([this, &group](std::unique_ptr&& subscriptionOperation) { + // The group subscription must be kept alive + // We accomplish this further on during the binding of the onStart() + auto subscriptionContext(std::make_shared(group)); onSubscribe(subscriptionContext, std::move(subscriptionOperation)); }); } @@ -265,6 +267,7 @@ void GroupSource::onSubscribe(const std::shared_ptr& } // If all goes well, set up handlers for start and stop monitoring events + // The group subscription context is being kept alive because it is being bound into some internal storage by onStart groupSubscriptionCtx->subscriptionControl->onStart([groupSubscriptionCtx](bool isStarting) { onStart(groupSubscriptionCtx, isStarting); }); diff --git a/ioc/singlesource.cpp b/ioc/singlesource.cpp index d17dac6..fcf255c 100644 --- a/ioc/singlesource.cpp +++ b/ioc/singlesource.cpp @@ -130,7 +130,6 @@ void SingleSource::show(std::ostream& outputStream) { */ void SingleSource::createRequestAndSubscriptionHandlers(std::unique_ptr&& channelControl, const std::shared_ptr& dbChannelSharedPtr) { - auto subscriptionContext(std::make_shared(dbChannelSharedPtr)); Value valuePrototype = getValuePrototype(dbChannelSharedPtr); @@ -141,10 +140,13 @@ void SingleSource::createRequestAndSubscriptionHandlers(std::unique_ptrcurrentValue = valuePrototype; channelControl - ->onSubscribe([this, subscriptionContext](std::unique_ptr&& subscriptionOperation) { + ->onSubscribe([this, valuePrototype, dbChannelSharedPtr]( + std::unique_ptr&& subscriptionOperation) { + // The subscription must be kept alive + // We accomplish this further on during the binding of the onStart() + auto subscriptionContext(std::make_shared(dbChannelSharedPtr)); + subscriptionContext->currentValue = valuePrototype; onSubscribe(subscriptionContext, std::move(subscriptionOperation)); }); } @@ -391,7 +393,8 @@ void SingleSource::onSubscribe(const std::shared_ptrsubscriptionControl->onStart([&subscriptionContext](bool isStarting) { + // The subscription context is being kept alive because it is being bound into some internal storage by onStart + subscriptionContext->subscriptionControl->onStart([subscriptionContext](bool isStarting) { onStart(subscriptionContext, isStarting); }); } diff --git a/test/testioc.cpp b/test/testioc.cpp index 9fd5016..3f37565 100644 --- a/test/testioc.cpp +++ b/test/testioc.cpp @@ -279,9 +279,9 @@ static std::initializer_list tests = { auto subscription = subscribe(event, pvName); clientContext.hurryUp(); - testThrows([&subscription, &event]() { - waitForUpdate(subscription, event); - }); + + // Wait for initial update + waitForUpdate(subscription, event); uint64_t expectedValue = 10L; clientContext.put(pvName) @@ -304,9 +304,8 @@ static std::initializer_list tests = { auto subscription = subscribe(event, pvGroupName); clientContext.hurryUp(); - testThrows([&subscription, &event]() { - waitForUpdate(subscription, event); - }); + waitForUpdate(subscription, event); + testDiag("Got Initial Update!"); auto pvName = "test:longExample"; uint64_t expectedValue = 12L; @@ -314,6 +313,7 @@ static std::initializer_list tests = { .set("value", expectedValue) .exec() ->wait(5.0); + testDiag("Issued Put!"); auto subFieldName = "sa[0].long.value"; auto updatedValue = waitForUpdate(subscription, event); @@ -332,9 +332,8 @@ static std::initializer_list tests = { auto subscription = subscribe(event, pvGroupName); clientContext.hurryUp(); - testThrows([&subscription, &event]() { - waitForUpdate(subscription, event); - }); + waitForUpdate(subscription, event); + testDiag("Got Initial Update!"); auto pvName = "test:vectorExampleD2"; shared_array expectedValue({ 3.1, 3.2, 3.3, 3.4, 3.5 }); @@ -345,6 +344,7 @@ static std::initializer_list tests = { }) .exec()->wait(5.0); + testDiag("Issued Put!"); auto subFieldName = "value.B"; auto updatedValue = waitForUpdate(subscription, event); if (updatedValue) { @@ -361,18 +361,18 @@ static std::initializer_list tests = { auto subscription = subscribe(event, pvGroupName); clientContext.hurryUp(); - testThrows([&subscription, &event]() { - waitForUpdate(subscription, event); - }); + waitForUpdate(subscription, event); + testDiag("Got Initial Update!"); auto pvName = "test:longExample"; - uint64_t expectedValue = 12L; + uint64_t expectedValue = 13L; clientContext.put(pvName) .set("value", expectedValue) .exec() ->wait(5.0); + testDiag("Issued Put!"); - auto subFieldName = "sa[0].long.value"; + auto subFieldName = "sa[0].any"; auto updatedValue = waitForUpdate(subscription, event); if (updatedValue) { auto actualValue = updatedValue[subFieldName].as(); @@ -388,9 +388,8 @@ static std::initializer_list tests = { auto subscription = subscribe(event, pvGroupName); clientContext.hurryUp(); - testThrows([&subscription, &event]() { - waitForUpdate(subscription, event); - }); + waitForUpdate(subscription, event); + testDiag("Got Initial Update!"); auto pvName = "test:vectorExampleD2"; shared_array expectedValue({ 3.1, 3.2, 3.3, 3.4, 3.5 }); @@ -400,6 +399,7 @@ static std::initializer_list tests = { return putVal; }) .exec()->wait(5.0); + testDiag("Issued Put!"); auto subFieldName = "value.jB"; auto updatedValue = waitForUpdate(subscription, event); @@ -409,6 +409,38 @@ static std::initializer_list tests = { } subscription->cancel(); }, + []() { + epicsEvent event; + + // Subscribe for changes to Group PV + auto pvGroupName = "test:tableExample"; + auto subscription = subscribe(event, pvGroupName); + + clientContext.hurryUp(); + waitForUpdate(subscription, event); + testDiag("Got Initial Update!"); + + auto pvName = "test:tableExample"; + shared_array expectedA({ 4.1, 4.2, 4.3, 4.4, 4.5 }); + shared_array expectedB({ 5.1, 5.2, 5.3, 5.4, 5.5 }); + clientContext.put(pvName).build([&expectedA, &expectedB](Value&& prototype) -> Value { + auto putVal = prototype.cloneEmpty(); + putVal["value.A"] = expectedA; + putVal["value.B"] = expectedB; + return putVal; + }) + .exec()->wait(5.0); + + testDiag("Issued Put!"); + auto updatedValue = waitForUpdate(subscription, event); + if (updatedValue) { + auto actualAValue = updatedValue["value.A"].as>(); + auto actualBValue = updatedValue["value.B"].as>(); + testArrEq(expectedA, actualAValue); + testArrEq(expectedB, actualBValue); + } + subscription->cancel(); + }, }; /** @@ -418,8 +450,7 @@ static std::initializer_list tests = { */ MAIN(testioc) { auto testNum = 0; - auto nMonitorTests = 5; - testPlan((int)tests.size() + nMonitorTests); + testPlan((int)tests.size() + 1); testSetup(); testdbPrepare(); @@ -460,8 +491,8 @@ static void boxLeft() { */ static std::shared_ptr subscribe(epicsEvent& event, const char* pvName) { return clientContext.monitor(pvName) - .maskConnected(false) - .maskDisconnected(false) + .maskConnected(true) + .maskDisconnected(true) .event([&event, pvName](client::Subscription& subscription) { testDiag("%s update event occurred", pvName); event.signal(); diff --git a/test/testiocg.db b/test/testiocg.db index 6229b03..b3e6b75 100644 --- a/test/testiocg.db +++ b/test/testiocg.db @@ -42,6 +42,7 @@ record(waveform, "$(user):vectorExampleD2") { }) field(INP, {const:[1.1,2.2,3.3,4.4,5.5]}) field(TPRO, "1") + field(FLNK, "$(user):groupExampleSave") } record(longout, "$(user):groupExampleSave") { @@ -96,6 +97,7 @@ record("*", "$(user):longExample") { "sa[0].long":{+channel:"VAL", +putorder:0} } }) + field(FLNK, "$(user):structExampleSave") } record("*", "$(user):enumExample") {