Skip to content
Snippets Groups Projects
Commit b175de8c authored by Martin Christoph Hierholzer's avatar Martin Christoph Hierholzer
Browse files

fixed bug that using poll-type transfers in testable mode could sometimes lead...

fixed bug that using poll-type transfers in testable mode could sometimes lead to stalled tests or assert failures.
parent c0025bd1
No related branches found
No related tags found
No related merge requests found
...@@ -139,9 +139,9 @@ namespace ChimeraTK { ...@@ -139,9 +139,9 @@ namespace ChimeraTK {
} }
} }
/** Obtain the testableModeLock and decrement the counter. */ /** Obtain the testableModeLock if not owned yet, and decrement the counter. */
void obtainLockAndDecrementCounter() { void obtainLockAndDecrementCounter() {
Application::testableModeLock("doReadTransfer "+this->getName()); if(!Application::testableModeTestLock()) Application::testableModeLock("doReadTransfer "+this->getName());
assert(Application::getInstance().testableMode_perVarCounter[_accessor->getUniqueId()] > 0); assert(Application::getInstance().testableMode_perVarCounter[_accessor->getUniqueId()] > 0);
assert(Application::getInstance().testableMode_counter > 0); assert(Application::getInstance().testableMode_counter > 0);
--Application::getInstance().testableMode_counter; --Application::getInstance().testableMode_counter;
...@@ -153,18 +153,24 @@ namespace ChimeraTK { ...@@ -153,18 +153,24 @@ namespace ChimeraTK {
} }
bool doReadTransferNonBlocking() override { bool doReadTransferNonBlocking() override {
return _accessor->doReadTransferNonBlocking(); bool newData = _accessor->doReadTransferNonBlocking();
if(!newData) return false;
obtainLockAndDecrementCounter();
return true;
} }
bool doReadTransferLatest() override { bool doReadTransferLatest() override {
bool retval = _accessor->doReadTransferLatest(); bool newData = _accessor->doReadTransferLatest();
if(!newData) return false;
// the queue has been emptied, so make sure that the testableMode_counter reflects this // the queue has been emptied, so make sure that the testableMode_counter reflects this
assert(Application::testableModeTestLock()); assert(Application::testableModeTestLock());
auto &app = Application::getInstance(); auto &app = Application::getInstance();
assert(Application::getInstance().testableMode_perVarCounter[_accessor->getUniqueId()] > 0);
assert(Application::getInstance().testableMode_counter > 0);
app.testableMode_counter -= app.testableMode_perVarCounter[_accessor->getUniqueId()]; app.testableMode_counter -= app.testableMode_perVarCounter[_accessor->getUniqueId()];
app.testableMode_perVarCounter[_accessor->getUniqueId()] = 0; app.testableMode_perVarCounter[_accessor->getUniqueId()] = 0;
return retval; return true;
} }
TransferFuture& readAsync() override { TransferFuture& readAsync() override {
......
...@@ -66,8 +66,9 @@ namespace ChimeraTK { ...@@ -66,8 +66,9 @@ namespace ChimeraTK {
mtca4u::DeviceException::REGISTER_DOES_NOT_EXIST); mtca4u::DeviceException::REGISTER_DOES_NOT_EXIST);
} }
// decorate with TestDecoratorRegisterAccessor if variable is not poll-type and store it in cache // decorate with TestDecoratorRegisterAccessor if variable is sender and receiver is not poll-type,
if(!Application::getInstance().testableMode_isPollMode[pv->getUniqueId()]) { // and store it in cache
if(pv->isWriteable() && !Application::getInstance().testableMode_isPollMode[pv->getUniqueId()]) {
auto deco = boost::make_shared<TestDecoratorRegisterAccessor<T>>(pv); auto deco = boost::make_shared<TestDecoratorRegisterAccessor<T>>(pv);
Application::getInstance().testableMode_names[pv->getUniqueId()] = "ControlSystem:"+name; Application::getInstance().testableMode_names[pv->getUniqueId()] = "ControlSystem:"+name;
boost::fusion::at_key<T>(scalarMap.table)[name].replace(mtca4u::ScalarRegisterAccessor<T>(deco)); boost::fusion::at_key<T>(scalarMap.table)[name].replace(mtca4u::ScalarRegisterAccessor<T>(deco));
...@@ -96,8 +97,9 @@ namespace ChimeraTK { ...@@ -96,8 +97,9 @@ namespace ChimeraTK {
mtca4u::DeviceException::REGISTER_DOES_NOT_EXIST); mtca4u::DeviceException::REGISTER_DOES_NOT_EXIST);
} }
// decorate with TestDecoratorRegisterAccessor if variable is not poll-type and store it in cache // decorate with TestDecoratorRegisterAccessor if variable is sender and receiver is not poll-type,
if(!Application::getInstance().testableMode_isPollMode[pv->getUniqueId()]) { // and store it in cache
if(pv->isWriteable() && !Application::getInstance().testableMode_isPollMode[pv->getUniqueId()]) {
auto deco = boost::make_shared<TestDecoratorRegisterAccessor<T>>(pv); auto deco = boost::make_shared<TestDecoratorRegisterAccessor<T>>(pv);
Application::getInstance().testableMode_names[pv->getUniqueId()] = "ControlSystem:"+name; Application::getInstance().testableMode_names[pv->getUniqueId()] = "ControlSystem:"+name;
boost::fusion::at_key<T>(arrayMap.table)[name].replace(mtca4u::OneDRegisterAccessor<T>(deco)); boost::fusion::at_key<T>(arrayMap.table)[name].replace(mtca4u::OneDRegisterAccessor<T>(deco));
......
...@@ -139,18 +139,67 @@ struct ReadAnyTestModule : public ctk::ApplicationModule { ...@@ -139,18 +139,67 @@ struct ReadAnyTestModule : public ctk::ApplicationModule {
} }
}; };
/*********************************************************************************************************************/
/* the PollingReadModule is designed to test poll-type transfers (even mixed with push-type) */
template<typename T>
struct PollingReadModule : public ctk::ApplicationModule {
using ctk::ApplicationModule::ApplicationModule;
ctk::ScalarPushInput<T> push{this, "push", "cm", "A push-type input"};
ctk::ScalarPushInput<T> push2{this, "push2", "cm", "A second push-type input"};
ctk::ScalarPollInput<T> poll{this, "poll", "cm", "A poll-type input"};
ctk::ScalarOutput<T> valuePush{this, "valuePush", "cm", "The last value received for 'push'"};
ctk::ScalarOutput<T> valuePoll{this, "valuePoll", "cm", "The last value received for 'poll'"};
ctk::ScalarOutput<int> state{this, "state", "", "State of the test mainLoop"};
void mainLoop() {
while(true) {
push.read();
poll.read();
valuePush = (T)push;
valuePoll = (T)poll;
valuePoll.write();
valuePush.write();
state = 1;
state.write();
push2.read();
push.readNonBlocking();
poll.read();
valuePush = (T)push;
valuePoll = (T)poll;
valuePoll.write();
valuePush.write();
state = 2;
state.write();
push2.read();
push.readLatest();
poll.read();
valuePush = (T)push;
valuePoll = (T)poll;
valuePoll.write();
valuePush.write();
state = 3;
state.write();
}
}
};
/*********************************************************************************************************************/ /*********************************************************************************************************************/
/* dummy application */ /* dummy application */
template<typename T> template<typename T>
struct TestApplication : public ctk::Application { struct TestApplication : public ctk::Application {
TestApplication() : Application("testApplication") { TestApplication() : Application("testApplication") {}
ChimeraTK::ExperimentalFeatures::enable();
}
~TestApplication() { shutdown(); } ~TestApplication() { shutdown(); }
using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc. using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc.
void defineConnections() {} // the setup is done in the tests void defineConnections() {} // setup is done in the tests
ctk::ControlSystemModule cs{""}; ctk::ControlSystemModule cs{""};
ctk::DeviceModule dev{dummySdm,""}; ctk::DeviceModule dev{dummySdm,""};
...@@ -159,6 +208,22 @@ struct TestApplication : public ctk::Application { ...@@ -159,6 +208,22 @@ struct TestApplication : public ctk::Application {
ReadAnyTestModule<T> readAnyTestModule{this,"readAnyTestModule", "Module for testing readAny()"}; ReadAnyTestModule<T> readAnyTestModule{this,"readAnyTestModule", "Module for testing readAny()"};
}; };
/*********************************************************************************************************************/
/* second application */
template<typename T>
struct PollingTestApplication : public ctk::Application {
PollingTestApplication() : Application("testApplication") {}
~PollingTestApplication() { shutdown(); }
void defineConnections() {
pollingReadModule.connectTo(cs);
}
ctk::ControlSystemModule cs{""};
PollingReadModule<T> pollingReadModule{this,"pollingReadModule", "Module for testing poll-type transfers"};
};
/*********************************************************************************************************************/ /*********************************************************************************************************************/
/* test that no TestDecoratorRegisterAccessor is used if the testable mode is not enabled */ /* test that no TestDecoratorRegisterAccessor is used if the testable mode is not enabled */
...@@ -205,6 +270,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( testBlockingRead, T, test_types ) { ...@@ -205,6 +270,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( testBlockingRead, T, test_types ) {
ctk::TestFacility test; ctk::TestFacility test;
auto pvInput = test.getScalar<T>("input"); auto pvInput = test.getScalar<T>("input");
auto pvOutput = test.getScalar<T>("output"); auto pvOutput = test.getScalar<T>("output");
app.debugTestableMode();
test.runApplication(); test.runApplication();
// test blocking read when taking control in the test thread (note: the blocking read is executed in the app module!) // test blocking read when taking control in the test thread (note: the blocking read is executed in the app module!)
...@@ -823,7 +889,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( testConvenienceRead, T, test_types ) { ...@@ -823,7 +889,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( testConvenienceRead, T, test_types ) {
app.readAnyTestModule.connectTo(app.cs["readAny"]); // avoid runtime warning app.readAnyTestModule.connectTo(app.cs["readAny"]); // avoid runtime warning
ctk::TestFacility test; ctk::TestFacility test;
auto pvOutput = test.getScalar<T>("output");
test.runApplication(); test.runApplication();
// test blocking read when taking control in the test thread (note: the blocking read is executed in the app module!) // test blocking read when taking control in the test thread (note: the blocking read is executed in the app module!)
...@@ -842,3 +907,67 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( testConvenienceRead, T, test_types ) { ...@@ -842,3 +907,67 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( testConvenienceRead, T, test_types ) {
} }
} }
/*********************************************************************************************************************/
/* test poll-type transfers mixed with push-type */
BOOST_AUTO_TEST_CASE_TEMPLATE( testPolling, T, test_types ) {
std::cout << "*********************************************************************************************************************" << std::endl;
std::cout << "==> testPolling<" << typeid(T).name() << ">" << std::endl;
PollingTestApplication<T> app;
ctk::TestFacility test;
test.runApplication();
auto pv_push = test.getScalar<T>("push");
auto pv_push2 = test.getScalar<T>("push2");
auto pv_poll = test.getScalar<T>("poll");
auto pv_valuePush = test.getScalar<T>("valuePush");
auto pv_valuePoll = test.getScalar<T>("valuePoll");
auto pv_state = test.getScalar<int>("state");
// write values to 'push' and 'poll' and check result
pv_push = 120;
pv_push.write();
pv_poll = 42;
pv_poll.write();
test.stepApplication();
pv_valuePoll.read();
pv_valuePush.read();
pv_state.read();
BOOST_CHECK_EQUAL((T)pv_valuePoll, 42);
BOOST_CHECK_EQUAL((T)pv_valuePush, 120);
BOOST_CHECK_EQUAL((T)pv_state, 1);
// this time the application gets triggered by push2, push is read non-blockingly (single value only)
pv_push = 22;
pv_push.write();
pv_poll = 44;
pv_poll.write();
pv_poll = 45;
pv_poll.write();
pv_push2.write();
test.stepApplication();
pv_valuePoll.read();
pv_valuePush.read();
pv_state.read();
BOOST_CHECK_EQUAL((T)pv_valuePoll, 45);
BOOST_CHECK_EQUAL((T)pv_valuePush, 22);
BOOST_CHECK_EQUAL((T)pv_state, 2);
// this time the application gets triggered by push2, push is read with readLatest()
pv_push = 24;
pv_push.write();
pv_poll = 46;
pv_poll.write();
pv_push2.write();
test.stepApplication();
pv_valuePoll.read();
pv_valuePush.read();
pv_state.read();
BOOST_CHECK_EQUAL((T)pv_valuePoll, 46);
BOOST_CHECK_EQUAL((T)pv_valuePush, 24);
BOOST_CHECK_EQUAL((T)pv_state, 3);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment