Skip to content
Snippets Groups Projects
TestableModeAccessorDecorator.h 7.18 KiB
Newer Older
 *
 *  Created on: Feb 17, 2017
 *      Author: Martin Hierholzer
 */

#ifndef CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR
#define CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR

#include <ChimeraTK/NDRegisterAccessorDecorator.h>
  /** Decorator of the NDRegisterAccessor which facilitates tests of the
   * application */
  template<typename UserType>
  class TestableModeAccessorDecorator : public ChimeraTK::NDRegisterAccessorDecorator<UserType> {
   public:
    TestableModeAccessorDecorator(boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> accessor, bool handleRead,
        bool handleWrite, size_t variableIdRead, size_t variableIdWrite)
    : ChimeraTK::NDRegisterAccessorDecorator<UserType>(accessor), _handleRead(handleRead), _handleWrite(handleWrite),
      _variableIdRead(variableIdRead), _variableIdWrite(variableIdWrite) {
      assert(_variableIdRead != 0);
      assert(_variableIdWrite != 0);

      // if receiving end, register for testable mode (stall detection)
      if(this->isReadable() && handleRead) {
        Application::getInstance().testableMode_processVars[_variableIdRead] = accessor;
        assert(accessor->getAccessModeFlags().has(AccessMode::wait_for_new_data));
      }

      // if this decorating a bidirectional process variable, set the
      // valueRejectCallback
      auto bidir = boost::dynamic_pointer_cast<BidirectionalProcessArray<UserType>>(accessor);
      if(bidir) {
        bidir->setValueRejectCallback([this] { decrementCounter(); });
      }
      else {
        assert(!(handleRead && handleWrite));
      }
    bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber = {}) override {
      if(!_handleWrite) return _target->writeTransfer(versionNumber);

      bool dataLost = false;
      if(!Application::testableModeTestLock()) {
        // may happen if first write in thread is done before first blocking read
        Application::testableModeLock("write " + this->getName());
      }
      dataLost = _target->writeTransfer(versionNumber);
      if(!dataLost) {
        ++Application::getInstance().testableMode_counter;
        ++Application::getInstance().testableMode_perVarCounter[_variableIdWrite];
        if(Application::getInstance().enableDebugTestableMode) {
          std::cout << "TestableModeAccessorDecorator::write[name='" << this->getName() << "', id=" << _variableIdWrite
                    << "]: testableMode_counter increased, now at value "
                    << Application::getInstance().testableMode_counter << std::endl;
        }
      }
      else {
        if(Application::getInstance().enableDebugTestableMode) {
          std::cout << "TestableModeAccessorDecorator::write[name='" << this->getName() << "', id=" << _variableIdWrite
                    << "]: testableMode_counter not increased due to lost data" << std::endl;
        }
      }
      return dataLost;
    }

    bool doWriteTransferDestructively(ChimeraTK::VersionNumber versionNumber = {}) override {
      if(!_handleWrite) return _target->writeTransferDestructively(versionNumber);

      bool dataLost = false;
      if(!Application::testableModeTestLock()) {
        // may happen if first write in thread is done before first blocking read
        Application::testableModeLock("write " + this->getName());
      }
      dataLost = _target->writeTransferDestructively(versionNumber);
      if(!dataLost) {
        ++Application::getInstance().testableMode_counter;
        ++Application::getInstance().testableMode_perVarCounter[_variableIdWrite];
        if(Application::getInstance().enableDebugTestableMode) {
          std::cout << "TestableModeAccessorDecorator::write[name='" << this->getName() << "', id=" << _variableIdWrite
                    << "]: testableMode_counter increased, now at value "
                    << Application::getInstance().testableMode_counter << std::endl;
        }
      }
      else {
        if(Application::getInstance().enableDebugTestableMode) {
          std::cout << "TestableModeAccessorDecorator::write[name='" << this->getName() << "', id=" << _variableIdWrite
                    << "]: testableMode_counter not increased due to lost data" << std::endl;
        }
      }
      return dataLost;
    void doReadTransferSynchronously() override { _target->readTransfer(); }
    /** Release the testableModeLock */
    void releaseLock() {
      if(Application::testableModeTestLock()) Application::testableModeUnlock("doReadTransfer " + this->getName());
    void doPreRead(TransferType type) override {
      _target->preRead(type);

      // Blocking reads have to release the lock so the data transport can happen
      if(_handleRead && type == TransferType::read &&
          TransferElement::_accessModeFlags.has(AccessMode::wait_for_new_data)) {

    /** Obtain the testableModeLock if not owned yet, and decrement the counter.
     */
    void obtainLockAndDecrementCounter(bool hasNewData) {
      if(!Application::testableModeTestLock()) Application::testableModeLock("doReadTransfer " + this->getName());
      if(Application::getInstance().testableMode_perVarCounter[_variableIdRead] > 0) {
        assert(Application::getInstance().testableMode_counter > 0);
        --Application::getInstance().testableMode_counter;
        --Application::getInstance().testableMode_perVarCounter[_variableIdRead];
        if(Application::getInstance().enableDebugTestableMode) {
          std::cout << "TestableModeAccessorDecorator[name='" << this->getName() << "', id=" << _variableIdRead
                    << "]: testableMode_counter decreased, now at value "
                    << Application::getInstance().testableMode_counter << " / "
                    << Application::getInstance().testableMode_perVarCounter[_variableIdRead] << std::endl;
        }
      else {
        if(Application::getInstance().enableDebugTestableMode) {
          std::cout << "TestableModeAccessorDecorator[name='" << this->getName() << "', id=" << _variableIdRead
                    << "]: testableMode_counter NOT decreased, was already at value "
                    << Application::getInstance().testableMode_counter << " / "
                    << Application::getInstance().testableMode_perVarCounter[_variableIdRead] << std::endl;
          std::cout << Application::getInstance().testableMode_names[_variableIdRead] << std::endl;
        }
    /** Obtain the testableModeLock if not owned yet, decrement the counter, and
     * release the lock again. */
    void decrementCounter() {
      obtainLockAndDecrementCounter(true);
      releaseLock();
    }
    void doPostRead(TransferType type, bool hasNewData) override {
      if(_handleRead) obtainLockAndDecrementCounter(hasNewData);
      ChimeraTK::NDRegisterAccessorDecorator<UserType>::doPostRead(type, hasNewData);
   protected:
    using ChimeraTK::NDRegisterAccessor<UserType>::buffer_2D;
    using ChimeraTK::NDRegisterAccessorDecorator<UserType>::_target;
    bool _handleRead, _handleWrite;
    size_t _variableIdRead, _variableIdWrite;
  };

} /* namespace ChimeraTK */

#endif /* CHIMERATK_TEST_DECORATOR_REGISTER_ACCCESSOR */