Newer
Older
Martin Christoph Hierholzer
committed
/*
* TestDecoratorRegisterAccessor.h
*
* Created on: Feb 17, 2017
* Author: Martin Hierholzer
*/
#ifndef CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR
#define CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR
#include <mtca4u/NDRegisterAccessor.h>
Martin Christoph Hierholzer
committed
#include "Application.h"
Martin Christoph Hierholzer
committed
namespace ChimeraTK {
Martin Christoph Hierholzer
committed
template<typename UserType>
class TestDecoratorRegisterAccessor;
/** Altered version of the TransferFuture since we need to deal with the lock in the wait() function. */
template<typename UserType>
class TestDecoratorTransferFuture : public TransferFuture {
public:
Martin Christoph Hierholzer
committed
TestDecoratorTransferFuture() : _originalFuture{nullptr}, _accessor{nullptr} {}
Martin Christoph Hierholzer
committed
virtual ~TestDecoratorTransferFuture() {}
Martin Christoph Hierholzer
committed
void wait() override {
Martin Christoph Hierholzer
committed
boost::this_thread::interruption_point();
Martin Christoph Hierholzer
committed
_originalFuture->wait();
_accessor->obtainLockAndDecrementCounter();
Martin Christoph Hierholzer
committed
_accessor->postRead();
_accessor->hasActiveFuture = false;
}
Martin Christoph Hierholzer
committed
void reset(PlainFutureType plainFuture, mtca4u::TransferElement *transferElement) = delete;
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
void reset(TransferFuture &originalFuture, TestDecoratorRegisterAccessor<UserType> *accessor) {
_originalFuture = &originalFuture;
_accessor = accessor;
TransferFuture::_theFuture = _originalFuture->getBoostFuture();
TransferFuture::_transferElement = accessor;
}
Martin Christoph Hierholzer
committed
Martin Christoph Hierholzer
committed
protected:
// plain pointers are ok, since this class is non-copyable and always owned by the TestDecoratorRegisterAccessor
// (which also holds a shared pointer on the actual accessor, which in turn owns the originalFuture).
Martin Christoph Hierholzer
committed
TransferFuture *_originalFuture;
Martin Christoph Hierholzer
committed
TestDecoratorRegisterAccessor<UserType> *_accessor;
Martin Christoph Hierholzer
committed
};
/*********************************************************getTwoDRegisterAccessor**********************************************************/
Martin Christoph Hierholzer
committed
/** Decorator of the NDRegisterAccessor which facilitates tests of the application */
template<typename UserType>
class TestDecoratorRegisterAccessor : public mtca4u::NDRegisterAccessor<UserType> {
public:
Martin Christoph Hierholzer
committed
TestDecoratorRegisterAccessor(boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> accessor)
Martin Christoph Hierholzer
committed
: mtca4u::NDRegisterAccessor<UserType>(accessor->getName(), accessor->getUnit(), accessor->getDescription()),
_accessor(accessor)
{
Martin Christoph Hierholzer
committed
// set ID to match the decorated accessor
this->_id = _accessor->getId();
variableId = Application::getInstance().idMap[this->_id];
assert(variableId != 0);
try {
// initialise buffers
buffer_2D.resize(_accessor->getNumberOfChannels());
for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i] = _accessor->accessChannel(i);
// if receiving end, register for testable mode (stall detection)
if(isReadable()) {
Martin Christoph Hierholzer
committed
Application::getInstance().testableMode_processVars[variableId] = accessor;
}
}
catch(...) {
this->shutdown();
throw;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
virtual ~TestDecoratorRegisterAccessor() { this->shutdown(); }
Martin Christoph Hierholzer
committed
bool write(ChimeraTK::VersionNumber versionNumber={}) override {
Martin Christoph Hierholzer
committed
bool dataLost = false;
Martin Christoph Hierholzer
committed
preWrite();
Martin Christoph Hierholzer
committed
if(!Application::testableModeTestLock()) {
// may happen if first write in thread is done before first blocking read
Application::testableModeLock("write "+this->getName());
}
Martin Christoph Hierholzer
committed
dataLost = _accessor->write(versionNumber);
Martin Christoph Hierholzer
committed
if(!dataLost) {
++Application::getInstance().testableMode_counter;
Martin Christoph Hierholzer
committed
++Application::getInstance().testableMode_perVarCounter[variableId];
Martin Christoph Hierholzer
committed
if(Application::getInstance().enableDebugTestableMode) {
Martin Christoph Hierholzer
committed
std::cout << "TestDecoratorRegisterAccessor::write[name='"<<this->getName()<<"', id="<<variableId<<"]: testableMode_counter "
"increased, now at value " << Application::getInstance().testableMode_counter << std::endl;
Martin Christoph Hierholzer
committed
}
}
else {
if(Application::getInstance().enableDebugTestableMode) {
Martin Christoph Hierholzer
committed
std::cout << "TestDecoratorRegisterAccessor::write[name='"<<this->getName()<<"', id="<<variableId<<"]: testableMode_counter not "
"increased due to lost data" << std::endl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
postWrite();
Martin Christoph Hierholzer
committed
return dataLost;
Martin Christoph Hierholzer
committed
}
void doReadTransfer() override {
releaseLock();
_accessor->doReadTransfer();
obtainLockAndDecrementCounter();
}
/** Release the testableModeLock */
void releaseLock() {
Martin Christoph Hierholzer
committed
try {
Martin Christoph Hierholzer
committed
Application::testableModeUnlock("doReadTransfer "+this->getName());
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
catch(std::system_error &e) { // ignore operation not permitted errors, since they happen the first time (lock not yet owned)
if(e.code() != std::errc::operation_not_permitted) throw e;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
/** Obtain the testableModeLock if not owned yet, and decrement the counter. */
Martin Christoph Hierholzer
committed
if(!Application::testableModeTestLock()) Application::testableModeLock("doReadTransfer "+this->getName());
Martin Christoph Hierholzer
committed
assert(Application::getInstance().testableMode_perVarCounter[variableId] > 0);
Martin Christoph Hierholzer
committed
assert(Application::getInstance().testableMode_counter > 0);
Martin Christoph Hierholzer
committed
--Application::getInstance().testableMode_counter;
Martin Christoph Hierholzer
committed
--Application::getInstance().testableMode_perVarCounter[variableId];
Martin Christoph Hierholzer
committed
if(Application::getInstance().enableDebugTestableMode) {
Martin Christoph Hierholzer
committed
std::cout << "TestDecoratorRegisterAccessor[name='"<<this->getName()<<"', id="<<variableId<<"]: testableMode_counter "
"decreased, now at value " << Application::getInstance().testableMode_counter << " / " << Application::getInstance().testableMode_perVarCounter[variableId] << std::endl;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
}
bool doReadTransferNonBlocking() override {
Martin Christoph Hierholzer
committed
bool newData = _accessor->doReadTransferNonBlocking();
if(!newData) return false;
obtainLockAndDecrementCounter();
return true;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
bool doReadTransferLatest() override {
Martin Christoph Hierholzer
committed
bool newData = _accessor->doReadTransferLatest();
if(!newData) return false;
Martin Christoph Hierholzer
committed
// the queue has been emptied, so make sure that the testableMode_counter reflects this
Martin Christoph Hierholzer
committed
assert(Application::testableModeTestLock());
Martin Christoph Hierholzer
committed
auto &app = Application::getInstance();
Martin Christoph Hierholzer
committed
app.testableMode_counter -= app.testableMode_perVarCounter[variableId];
app.testableMode_perVarCounter[variableId] = 0;
Martin Christoph Hierholzer
committed
return true;
Martin Christoph Hierholzer
committed
}
Martin Christoph Hierholzer
committed
TransferFuture& readAsync() override {
Martin Christoph Hierholzer
committed
if(TransferElement::hasActiveFuture) {
return activeTestDecoratorFuture;
}
Martin Christoph Hierholzer
committed
auto &future = _accessor->readAsync();
TransferElement::hasActiveFuture = true;
Martin Christoph Hierholzer
committed
activeTestDecoratorFuture.reset(future, this);
Martin Christoph Hierholzer
committed
return activeTestDecoratorFuture;
}
Martin Christoph Hierholzer
committed
void postRead() override {
Martin Christoph Hierholzer
committed
if(!TransferElement::hasActiveFuture) _accessor->postRead();
Martin Christoph Hierholzer
committed
for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i].swap(_accessor->accessChannel(i));
}
void preWrite() override {
Martin Christoph Hierholzer
committed
for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i].swap(_accessor->accessChannel(i));
Martin Christoph Hierholzer
committed
}
void postWrite() override {
Martin Christoph Hierholzer
committed
for(size_t i=0; i<_accessor->getNumberOfChannels(); ++i) buffer_2D[i].swap(_accessor->accessChannel(i));
Martin Christoph Hierholzer
committed
}
bool isSameRegister(const boost::shared_ptr<mtca4u::TransferElement const> &other) const override {
return _accessor->isSameRegister(other);
}
bool isReadOnly() const override {
return _accessor->isReadOnly();
}
bool isReadable() const override {
return _accessor->isReadable();
}
Martin Christoph Hierholzer
committed
bool isWriteable() const override {
return _accessor->isWriteable();
}
Martin Christoph Hierholzer
committed
std::vector< boost::shared_ptr<mtca4u::TransferElement> > getHardwareAccessingElements() override {
return _accessor->getHardwareAccessingElements();
}
void replaceTransferElement(boost::shared_ptr<mtca4u::TransferElement> newElement) override {
_accessor->replaceTransferElement(newElement);
}
void setPersistentDataStorage(boost::shared_ptr<ChimeraTK::PersistentDataStorage> storage) override {
_accessor->setPersistentDataStorage(storage);
}
protected:
Martin Christoph Hierholzer
committed
using mtca4u::NDRegisterAccessor<UserType>::buffer_2D;
Martin Christoph Hierholzer
committed
boost::shared_ptr<mtca4u::NDRegisterAccessor<UserType>> _accessor;
Martin Christoph Hierholzer
committed
friend class TestDecoratorTransferFuture<UserType>;
Martin Christoph Hierholzer
committed
TestDecoratorTransferFuture<UserType> activeTestDecoratorFuture;
Martin Christoph Hierholzer
committed
size_t variableId;
Martin Christoph Hierholzer
committed
};
} /* namespace ChimeraTK */
#endif /* CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR */