<commit_before>#ifndef DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_VECTOR_HH
#define DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_VECTOR_HH

// system
#include <vector>

// dune-stuff
#include <dune/stuff/common/vector.hh>

namespace Dune
{

namespace Detailed {

namespace Discretizations
{

namespace Assembler
{

namespace Local
{

namespace Codim1
{

/**
  \todo Add neumann boundary treatment
  \todo When adding neumann: think of numTmpObjectsRequired()!
  \todo Add penalty parameter
  **/
template< class LocalFunctionalImp >
class Vector
{
public:

  typedef LocalFunctionalImp
    LocalFunctionalType;

  typedef Vector< LocalFunctionalType >
    ThisType;

  typedef typename LocalFunctionalType::RangeFieldType
    RangeFieldType;

  //! constructor
  Vector( const LocalFunctionalType localFunctional )
    : localFunctional_( localFunctional )
  {
  }

private:
  //! copy constructor
  Vector( const ThisType& other )
    : localFunctional_( other.localFunctional() )
  {
  }

public:
  const LocalFunctionalType& localFunctional() const
  {
    return localFunctional_;
  }

  /**
    \todo Add neumann treatment here!
    **/
  std::vector< unsigned int > numTmpObjectsRequired() const
  {
    std::vector< unsigned int > ret( 2, 0 );
    // we require 1 tmp vector in this local assembler
    ret[0] = 1;
    // the functional itself requires that much local matrices
    ret[1] = localFunctional_.numTmpObjectsRequired();
    return ret;
  }

  template< class TestSpaceType,
            class EntityType,
            class SystemVectorType,
            class LocalVectorType >
  void assembleLocal( const TestSpaceType& testSpace,
                      const EntityType& entity,
                      SystemVectorType& systemVector,
                      std::vector< std::vector< LocalVectorType > >& tmpLocalVectorsContainer ) const
  {
    // get the local basefunction set
    typedef typename TestSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType
      LocalTesBaseFunctionSetType;

    const LocalTesBaseFunctionSetType localTestBaseFunctionSet = testSpace.baseFunctionSet().local( entity );

    // check tmp local vectors
    assert( tmpLocalVectorsContainer.size() > 1 );
    std::vector< LocalVectorType >& tmpLocalVectors = tmpLocalVectorsContainer[0];
    if( tmpLocalVectors.size() < 1 )
    {
      tmpLocalVectors.resize( 1, LocalVectorType( testSpace.map().maxLocalSize(), RangeFieldType( 0.0 ) ) );
    }

    // some types
    typedef typename TestSpaceType::GridViewType
      GridViewType;

    typedef typename GridViewType::IntersectionIterator
      IntersectionIteratorType;

    typedef typename IntersectionIteratorType::Intersection
      IntersectionType;

    typedef typename IntersectionType::EntityPointer
      EntityPointerType;

    const GridViewType& gridView = testSpace.gridView();

    const IntersectionIteratorType lastIntersection = gridView.iend( entity );

    // do loop over all intersections
    for( IntersectionIteratorType intIt = gridView.ibegin( entity ); intIt != lastIntersection; ++intIt )
    {
      const IntersectionType& intersection = *intIt;

      if( !intersection.neighbor() && intersection.boundary() ) // if boundary intersection
      {
        // clear target vectors
        Dune::Stuff::Common::clear(tmpLocalVectors[0]);

        localFunctional_.applyLocal(  localTestBaseFunctionSet,
                                      intersection,
                                      tmpLocalVectors[0],
                                      tmpLocalVectorsContainer[1] );

        // write local vector to global
        addToVector( testSpace, entity, tmpLocalVectors[0], systemVector );

      }// end if boundary intersection
    } // done loop over all intersections
  } // end method assembleLocal

private:

  //! assignment operator
  ThisType& operator=( const ThisType& );

  template< class TestSpaceType,
            class EntityType,
            class LocalVectorType,
            class SystemVectorType >
  void addToVector( const TestSpaceType& testSpace,
                    const EntityType& entity,
                    const LocalVectorType& localVector,
                    SystemVectorType& systemVector ) const
  {
    for( unsigned int j = 0; j < testSpace.baseFunctionSet().local( entity ).size(); ++j )
    {
      const unsigned int globalJ = testSpace.map().toGlobal( entity, j );

      systemVector[globalJ] += localVector[j];
    }
  } // end method addToVector

  const LocalFunctionalType localFunctional_;
}; // end class Vector

} // end namespace Codim1

} // end namespace Local

} // end namespace Assembler

} // namespace Discretizations

} // namespace Detailed

} // end namespace Dune

#endif // DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_VECTOR_HH
<commit_msg>[assembler.local.codim1.vector] added Neumann<commit_after>#ifndef DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_VECTOR_HH
#define DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_VECTOR_HH

#include <vector>

#include <dune/common/shared_ptr.hh>

#include <dune/stuff/common/vector.hh>
#include <dune/stuff/grid/boundaryinfo.hh>

namespace Dune {
namespace Detailed {
namespace Discretizations {
namespace Assembler {
namespace Local {
namespace Codim1 {
namespace Vector {

template< class LocalFunctionalImp, class BoundaryInfoImp >
class Neumann
{
public:
  typedef LocalFunctionalImp LocalFunctionalType;

  typedef BoundaryInfoImp BoundaryInfoType;

  typedef Neumann< LocalFunctionalType, BoundaryInfoType > ThisType;

  Neumann(const LocalFunctionalType& _localFunctional,
          const Dune::shared_ptr< const BoundaryInfoType > _boundaryInfo)
    : localFunctional_(_localFunctional)
    , boundaryInfo_(_boundaryInfo)
  {}

  const LocalFunctionalType& localFunctional() const
  {
    return localFunctional_;
  }

  const Dune::shared_ptr< const BoundaryInfoType > boundaryInfo() const
  {
    return boundaryInfo_;
  }

  std::vector< unsigned int > numTmpObjectsRequired() const
  {
    std::vector< unsigned int > ret(2, 0);
    // we require 1 tmp vector in this local assembler
    ret[0] = 1;
    // the functional itself requires that much local matrices
    ret[1] = localFunctional_.numTmpObjectsRequired();
    return ret;
  } // std::vector< unsigned int > numTmpObjectsRequired() const

  template< class TestSpaceType,
            class EntityType,
            class VectorType,
            class LocalVectorType >
  void assembleLocal(const TestSpaceType& testSpace,
                     const EntityType& entity,
                     VectorType& vector,
                     std::vector< std::vector< LocalVectorType > >& tmpLocalVectorsContainer) const
  {
    // get the local basefunction set
    typedef typename TestSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType LocalTesBaseFunctionSetType;
    const LocalTesBaseFunctionSetType localTestBaseFunctionSet = testSpace.baseFunctionSet().local(entity);

    // check tmp local vectors
    typedef typename LocalFunctionalType::FunctionSpaceType::RangeFieldType RangeFieldType;
    assert(tmpLocalVectorsContainer.size() > 1);
    std::vector< LocalVectorType >& tmpLocalVectors = tmpLocalVectorsContainer[0];
    if (tmpLocalVectors.size() < numTmpObjectsRequired()[0])
      tmpLocalVectors.resize( numTmpObjectsRequired()[0],
                              LocalVectorType(testSpace.map().maxLocalSize(),RangeFieldType(0)));

    // do loop over all intersections
    typedef typename TestSpaceType::GridViewType GridViewType;
    typedef typename GridViewType::IntersectionIterator IntersectionIteratorType;
    typedef typename IntersectionIteratorType::Intersection IntersectionType;
    typedef typename IntersectionType::EntityPointer EntityPointerType;
    const GridViewType& gridView = testSpace.gridView();
    for (IntersectionIteratorType intersectionIt = gridView.ibegin(entity);
         intersectionIt != gridView.iend(entity);
         ++intersectionIt) {
      const IntersectionType& intersection = *intersectionIt;
      if (boundaryInfo_->neumann(intersection)) {
        Dune::Stuff::Common::clear(tmpLocalVectors[0]);
        localFunctional_.applyLocal(localTestBaseFunctionSet,
                                    intersection,
                                    tmpLocalVectors[0],
                                    tmpLocalVectorsContainer[1]);
        addToVector(testSpace, entity, tmpLocalVectors[0], vector);
      }
    } // do loop over all intersections
  } // void assembleLocal(...)

private:
  Neumann(const ThisType&);
  ThisType& operator=(const ThisType&);

  template< class TestSpaceType,
            class EntityType,
            class LocalVectorType,
            class SystemVectorType >
  void addToVector(const TestSpaceType& testSpace,
                   const EntityType& entity,
                   const LocalVectorType& localVector,
                   SystemVectorType& systemVector) const
  {
    for (unsigned int j = 0; j < testSpace.baseFunctionSet().local(entity).size(); ++j) {
      const unsigned int globalJ = testSpace.map().toGlobal(entity, j);
      systemVector[globalJ] += localVector[j];
    }
  } // vodi addToVector(...)

  const LocalFunctionalType& localFunctional_;
  const Dune::shared_ptr< const BoundaryInfoType > boundaryInfo_;
}; // class Neumann

} // namespace Vector
} // namespace Codim1
} // namespace Local
} // namespace Assembler
} // namespace Discretizations
} // namespace Detailed
} // namespace Dune

#endif // DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_VECTOR_HH
<|endoftext|>


<commit_before>#include <cutehmi/services/Service.hpp>

#include <cutehmi/services/ServiceManager.hpp>
#include <cutehmi/Notification.hpp>

#include <QCoreApplication>
#include <QThread>

namespace cutehmi {
namespace services {

constexpr int Service::INITIAL_STOP_TIMEOUT;
constexpr int Service::INITIAL_START_TIMEOUT;
constexpr int Service::INITIAL_REPAIR_TIMEOUT;
constexpr const char * Service::INITIAL_NAME;

Service::Service(QObject * parent):
	QObject(parent),
	m(new Members)
{
	ServiceManager::Instance().add(this);
	setStatus(DefaultStatus());
}

Service::~Service()
{
	stop();

	// Wait till either service stops or is interrupted.
	if (m->stateInterface)
		while (!m->stateInterface->stopped().active() && !m->stateInterface->interrupted().active()) {
			// Due to repair and start timeouts service may end up in broken state. Try to stop the service repeatedly.
			stop();

			// QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents) slows down shutdown, so execute this loop aggressively.
			QCoreApplication::processEvents();
		}

	if (m->serviceable)
		ServiceManager::Instance().leave(this);
	ServiceManager::Instance().remove(this);

	if (m->stateMachine)
		m->stateMachine->stop();
}

int Service::stopTimeout() const
{
	return m->stopTimeout;
}

void Service::setStopTimeout(int timeout)
{
	if (m->stopTimeout != timeout) {
		m->stopTimeout = timeout;
		emit stopTimeoutChanged();
	}
}

int Service::startTimeout() const
{
	return m->startTimeout;
}

void Service::setStartTimeout(int startTimeout)
{
	if (m->startTimeout != startTimeout) {
		m->startTimeout = startTimeout;
		emit startTimeoutChanged();
	}
}

int Service::repairTimeout() const
{
	return m->repairTimeout;
}

void Service::setRepairTimeout(int repairTimeout)
{
	if (m->repairTimeout != repairTimeout) {
		m->repairTimeout = repairTimeout;
		emit repairTimeoutChanged();
	}
}

QString Service::name() const
{
	return m->name;
}

void Service::setName(const QString & name)
{
	if (m->name != name) {
		m->name = name;
		emit nameChanged();
	}
}

QString Service::status() const
{
	return m->status;
}

void Service::setServiceable(QVariant serviceable)
{
	QObject * qobjectPtr = serviceable.value<QObject *>();
	Serviceable * serviceablePtr = dynamic_cast<Serviceable *>(qobjectPtr);
	if (qobjectPtr != nullptr && serviceablePtr == nullptr)
		CUTEHMI_WARNING("Object assigned as serviceable to '"  << name() << "' service does not implement 'cutehmi::services::Serviceable' interface.");

	if (m->serviceable != serviceablePtr) {
		if (m->serviceable)
			ServiceManager::Instance().leave(this);
		destroyStateMachine();
		if (serviceablePtr)
			initializeStateMachine(*serviceablePtr);
		m->serviceable = serviceablePtr;
		if (m->serviceable)
			ServiceManager::Instance().manage(this);
		else
			setStatus(DefaultStatus());
		emit serviceableChanged();
	}
}

QVariant Service::serviceable() const
{
	return QVariant::fromValue(m->serviceable);
}

void Service::start()
{
	emit started();
}

void Service::stop()
{
	emit stopped();
}

internal::StateInterface * Service::stateInterface()
{
	return m->stateInterface;
}

const internal::StateInterface * Service::stateInterface() const
{
	return m->stateInterface;
}

void Service::activate()
{
	emit activated();
}

void Service::setStatus(const QString & status)
{
	if (m->status != status) {
		m->status = status;
		emit statusChanged();
	}
}

QString & Service::DefaultStatus()
{
	static QString name = QObject::tr("Unmanaged");
	return name;
}

void Service::destroyStateMachine()
{
	if (m->stateMachine) {
		m->stateMachine->stop();
		m->stateMachine->deleteLater();
		m->stateMachine = nullptr;
		m->stateInterface = nullptr;
	}
}

void Service::initializeStateMachine(Serviceable & serviceable)
{
	try {
		m->stateMachine = new QStateMachine(this);
		m->stateInterface = new internal::StateInterface(m->stateMachine);

		connect(m->stateInterface, & internal::StateInterface::statusChanged, this, [this]() {
			setStatus(m->stateInterface->status());
		} );

		// Variable m->lastNotifiableState is used to prevent notification spam, i.e. when service fails to leave notifiable state
		// through intermediate, non-notifiable state (e.g. 'broken' is a notifiable state, 'repairing' is an intermediate state;
		// without the condition "Service 'XYZ' broke" message would be posted after each failed repair attempt).
		connect(& m->stateInterface->interrupted(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->interrupted())
				Notification::Critical(tr("Stop sequence of '%1' service has been interrupted, because it took more than %2 [ms] to stop the service.").arg(name()).arg(stopTimeout()));
			m->lastNotifiableState = & m->stateInterface->interrupted();
		});
		connect(& m->stateInterface->started(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->started())
				Notification::Info(tr("Service '%1' has started.").arg(name()));
			m->lastNotifiableState = & m->stateInterface->started();
		});
		connect(& m->stateInterface->stopped(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->stopped())
				Notification::Info(tr("Service '%1' is stopped.").arg(name()));
			m->lastNotifiableState = & m->stateInterface->stopped();
		});
		connect(& m->stateInterface->broken(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->broken())
				Notification::Warning(tr("Service '%1' broke.").arg(name()));
			m->lastNotifiableState = & m->stateInterface->broken();
		});

		// Configure timeouts.

		connect(& m->stateInterface->stopping(), & QState::entered, [this]() {
			if (stopTimeout() >= 0)
				m->timeoutTimer.start(stopTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->stopping(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		connect(& m->stateInterface->evacuating(), & QState::entered, [this]() {
			if (stopTimeout() >= 0)
				m->timeoutTimer.start(stopTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->evacuating(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		connect(& m->stateInterface->starting(), & QState::entered, [this]() {
			if (startTimeout() >= 0)
				m->timeoutTimer.start(startTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->starting(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		connect(& m->stateInterface->repairing(), & QState::entered, [this]() {
			if (repairTimeout() >= 0)
				m->timeoutTimer.start(repairTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->repairing(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		m->stateInterface->stopped().assignProperty(m->stateInterface, "status", tr("Stopped"));
		m->stateInterface->interrupted().assignProperty(m->stateInterface, "status", tr("Interrupted"));
		m->stateInterface->starting().assignProperty(m->stateInterface, "status", tr("Starting"));
		m->stateInterface->started().assignProperty(m->stateInterface, "status", tr("Started"));
		m->stateInterface->idling().assignProperty(m->stateInterface, "status", tr("Idling"));
		m->stateInterface->yielding().assignProperty(m->stateInterface, "status", tr("Yielding"));
		m->stateInterface->active().assignProperty(m->stateInterface, "status", tr("Active"));
		m->stateInterface->stopping().assignProperty(m->stateInterface, "status", tr("Stopping"));
		m->stateInterface->broken().assignProperty(m->stateInterface, "status", tr("Broken"));
		m->stateInterface->repairing().assignProperty(m->stateInterface, "status", tr("Repairing"));
		m->stateInterface->evacuating().assignProperty(m->stateInterface, "status", tr("Evacuating"));

		m->stateMachine->addState(& m->stateInterface->stopped());
		m->stateMachine->addState(& m->stateInterface->interrupted());
		m->stateMachine->addState(& m->stateInterface->starting());
		m->stateMachine->addState(& m->stateInterface->started());
		m->stateMachine->addState(& m->stateInterface->stopping());
		m->stateMachine->addState(& m->stateInterface->broken());
		m->stateMachine->addState(& m->stateInterface->repairing());
		m->stateMachine->addState(& m->stateInterface->evacuating());
		m->stateMachine->setInitialState(& m->stateInterface->stopped());

		m->stateInterface->stopped().addTransition(this, & Service::started, & m->stateInterface->starting());
		m->stateInterface->started().addTransition(this, & Service::stopped, & m->stateInterface->stopping());
		m->stateInterface->broken().addTransition(this, & Service::started, & m->stateInterface->repairing());
		m->stateInterface->broken().addTransition(this, & Service::stopped, & m->stateInterface->evacuating());
		m->stateInterface->stopping().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->interrupted());
		m->stateInterface->starting().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->broken());
		m->stateInterface->repairing().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->broken());
		m->stateInterface->yielding().addTransition(this, & Service::activated, & m->stateInterface->active());
		m->stateInterface->evacuating().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->interrupted());

		addTransition(& m->stateInterface->starting(), & m->stateInterface->started(), serviceable.transitionToStarted());
		addTransition(& m->stateInterface->repairing(), & m->stateInterface->started(), serviceable.transitionToStarted());
		addTransition(& m->stateInterface->stopping(), & m->stateInterface->stopped(), serviceable.transitionToStopped());
		addTransition(& m->stateInterface->starting(), & m->stateInterface->broken(), serviceable.transitionToBroken());
		addTransition(& m->stateInterface->started(), & m->stateInterface->broken(), serviceable.transitionToBroken());
		addTransition(& m->stateInterface->repairing(), & m->stateInterface->broken(), serviceable.transitionToBroken());
		if (serviceable.transitionToIdling())
			addTransition(& m->stateInterface->active(), & m->stateInterface->idling(), serviceable.transitionToIdling());
		addTransition(& m->stateInterface->idling(), & m->stateInterface->yielding(), serviceable.transitionToYielding());
		addTransition(& m->stateInterface->evacuating(), & m->stateInterface->stopped(), serviceable.transitionToStopped());

		addStatuses(serviceable.configureBroken(& m->stateInterface->broken()));
		addStatuses(serviceable.configureStarted(& m->stateInterface->active(), & m->stateInterface->idling(), & m->stateInterface->yielding()));
		addStatuses(serviceable.configureStarting(& m->stateInterface->starting()));
		addStatuses(serviceable.configureStopping(& m->stateInterface->stopping()));
		addStatuses(serviceable.configureRepairing(& m->stateInterface->repairing()));
		addStatuses(serviceable.configureEvacuating(& m->stateInterface->evacuating()));

		m->stateMachine->start();
		QCoreApplication::processEvents();	// This is required in order to truly start state machine and prevent it from ignoring incoming events.
	} catch (const std::exception & e) {
		CUTEHMI_CRITICAL("Could not initialize new state machine, because of following exception: " << e.what());
	}
}

void Service::addTransition(QState * source, QState * target, std::unique_ptr<QAbstractTransition> transition)
{
	if (transition) {
		transition->setParent(source);
		transition->setTargetState(target);
		source->addTransition(transition.release());
	} else
		source->addTransition(target);
}

void Service::addStatuses(std::unique_ptr<Serviceable::ServiceStatuses> statuses)
{
	if (statuses)
		for (auto stateStatus = statuses->begin(); stateStatus != statuses->end(); ++stateStatus)
			stateStatus.key()->assignProperty(m->stateInterface, "status", stateStatus.value());
}

}
}

//(c)C: Copyright © 2019-2020, Michał Policht <michal@policht.pl>, Yuri Chornoivan <yurchor@ukr.net>. All rights reserved.
//(c)C: This file is a part of CuteHMI.
//(c)C: CuteHMI is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//(c)C: CuteHMI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
//(c)C: You should have received a copy of the GNU Lesser General Public License along with CuteHMI.  If not, see <https://www.gnu.org/licenses/>.
<commit_msg>Print service status in debug output.<commit_after>#include <cutehmi/services/Service.hpp>

#include <cutehmi/services/ServiceManager.hpp>
#include <cutehmi/Notification.hpp>

#include <QCoreApplication>
#include <QThread>

namespace cutehmi {
namespace services {

constexpr int Service::INITIAL_STOP_TIMEOUT;
constexpr int Service::INITIAL_START_TIMEOUT;
constexpr int Service::INITIAL_REPAIR_TIMEOUT;
constexpr const char * Service::INITIAL_NAME;

Service::Service(QObject * parent):
	QObject(parent),
	m(new Members)
{
	ServiceManager::Instance().add(this);
	setStatus(DefaultStatus());
}

Service::~Service()
{
	stop();

	// Wait till either service stops or is interrupted.
	if (m->stateInterface)
		while (!m->stateInterface->stopped().active() && !m->stateInterface->interrupted().active()) {
			// Due to repair and start timeouts service may end up in broken state. Try to stop the service repeatedly.
			stop();

			// QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents) slows down shutdown, so execute this loop aggressively.
			QCoreApplication::processEvents();
		}

	if (m->serviceable)
		ServiceManager::Instance().leave(this);
	ServiceManager::Instance().remove(this);

	if (m->stateMachine)
		m->stateMachine->stop();
}

int Service::stopTimeout() const
{
	return m->stopTimeout;
}

void Service::setStopTimeout(int timeout)
{
	if (m->stopTimeout != timeout) {
		m->stopTimeout = timeout;
		emit stopTimeoutChanged();
	}
}

int Service::startTimeout() const
{
	return m->startTimeout;
}

void Service::setStartTimeout(int startTimeout)
{
	if (m->startTimeout != startTimeout) {
		m->startTimeout = startTimeout;
		emit startTimeoutChanged();
	}
}

int Service::repairTimeout() const
{
	return m->repairTimeout;
}

void Service::setRepairTimeout(int repairTimeout)
{
	if (m->repairTimeout != repairTimeout) {
		m->repairTimeout = repairTimeout;
		emit repairTimeoutChanged();
	}
}

QString Service::name() const
{
	return m->name;
}

void Service::setName(const QString & name)
{
	if (m->name != name) {
		m->name = name;
		emit nameChanged();
	}
}

QString Service::status() const
{
	return m->status;
}

void Service::setServiceable(QVariant serviceable)
{
	QObject * qobjectPtr = serviceable.value<QObject *>();
	Serviceable * serviceablePtr = dynamic_cast<Serviceable *>(qobjectPtr);
	if (qobjectPtr != nullptr && serviceablePtr == nullptr)
		CUTEHMI_WARNING("Object assigned as serviceable to '"  << name() << "' service does not implement 'cutehmi::services::Serviceable' interface.");

	if (m->serviceable != serviceablePtr) {
		if (m->serviceable)
			ServiceManager::Instance().leave(this);
		destroyStateMachine();
		if (serviceablePtr)
			initializeStateMachine(*serviceablePtr);
		m->serviceable = serviceablePtr;
		if (m->serviceable)
			ServiceManager::Instance().manage(this);
		else
			setStatus(DefaultStatus());
		emit serviceableChanged();
	}
}

QVariant Service::serviceable() const
{
	return QVariant::fromValue(m->serviceable);
}

void Service::start()
{
	emit started();
}

void Service::stop()
{
	emit stopped();
}

internal::StateInterface * Service::stateInterface()
{
	return m->stateInterface;
}

const internal::StateInterface * Service::stateInterface() const
{
	return m->stateInterface;
}

void Service::activate()
{
	emit activated();
}

void Service::setStatus(const QString & status)
{
	if (m->status != status) {
		m->status = status;
		if (m->serviceable)
			CUTEHMI_DEBUG(name() << ": " << status);
		emit statusChanged();
	}
}

QString & Service::DefaultStatus()
{
	static QString name = QObject::tr("Unmanaged");
	return name;
}

void Service::destroyStateMachine()
{
	if (m->stateMachine) {
		m->stateMachine->stop();
		m->stateMachine->deleteLater();
		m->stateMachine = nullptr;
		m->stateInterface = nullptr;
	}
}

void Service::initializeStateMachine(Serviceable & serviceable)
{
	try {
		m->stateMachine = new QStateMachine(this);
		m->stateInterface = new internal::StateInterface(m->stateMachine);

		connect(m->stateInterface, & internal::StateInterface::statusChanged, this, [this]() {
			setStatus(m->stateInterface->status());
		} );

		// Variable m->lastNotifiableState is used to prevent notification spam, i.e. when service fails to leave notifiable state
		// through intermediate, non-notifiable state (e.g. 'broken' is a notifiable state, 'repairing' is an intermediate state;
		// without the condition "Service 'XYZ' broke" message would be posted after each failed repair attempt).
		connect(& m->stateInterface->interrupted(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->interrupted())
				Notification::Critical(tr("Stop sequence of '%1' service has been interrupted, because it took more than %2 [ms] to stop the service.").arg(name()).arg(stopTimeout()));
			m->lastNotifiableState = & m->stateInterface->interrupted();
		});
		connect(& m->stateInterface->started(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->started())
				Notification::Info(tr("Service '%1' has started.").arg(name()));
			m->lastNotifiableState = & m->stateInterface->started();
		});
		connect(& m->stateInterface->stopped(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->stopped())
				Notification::Info(tr("Service '%1' is stopped.").arg(name()));
			m->lastNotifiableState = & m->stateInterface->stopped();
		});
		connect(& m->stateInterface->broken(), & QState::entered, [this]() {
			if (m->lastNotifiableState != & m->stateInterface->broken())
				Notification::Warning(tr("Service '%1' broke.").arg(name()));
			m->lastNotifiableState = & m->stateInterface->broken();
		});

		// Configure timeouts.

		connect(& m->stateInterface->stopping(), & QState::entered, [this]() {
			if (stopTimeout() >= 0)
				m->timeoutTimer.start(stopTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->stopping(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		connect(& m->stateInterface->evacuating(), & QState::entered, [this]() {
			if (stopTimeout() >= 0)
				m->timeoutTimer.start(stopTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->evacuating(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		connect(& m->stateInterface->starting(), & QState::entered, [this]() {
			if (startTimeout() >= 0)
				m->timeoutTimer.start(startTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->starting(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		connect(& m->stateInterface->repairing(), & QState::entered, [this]() {
			if (repairTimeout() >= 0)
				m->timeoutTimer.start(repairTimeout());
		});
		// It's safer to stop timeout, so that it won't make false shot.
		connect(& m->stateInterface->repairing(), & QState::exited, & m->timeoutTimer, & QTimer::stop);

		m->stateInterface->stopped().assignProperty(m->stateInterface, "status", tr("Stopped"));
		m->stateInterface->interrupted().assignProperty(m->stateInterface, "status", tr("Interrupted"));
		m->stateInterface->starting().assignProperty(m->stateInterface, "status", tr("Starting"));
		m->stateInterface->started().assignProperty(m->stateInterface, "status", tr("Started"));
		m->stateInterface->idling().assignProperty(m->stateInterface, "status", tr("Idling"));
		m->stateInterface->yielding().assignProperty(m->stateInterface, "status", tr("Yielding"));
		m->stateInterface->active().assignProperty(m->stateInterface, "status", tr("Active"));
		m->stateInterface->stopping().assignProperty(m->stateInterface, "status", tr("Stopping"));
		m->stateInterface->broken().assignProperty(m->stateInterface, "status", tr("Broken"));
		m->stateInterface->repairing().assignProperty(m->stateInterface, "status", tr("Repairing"));
		m->stateInterface->evacuating().assignProperty(m->stateInterface, "status", tr("Evacuating"));

		m->stateMachine->addState(& m->stateInterface->stopped());
		m->stateMachine->addState(& m->stateInterface->interrupted());
		m->stateMachine->addState(& m->stateInterface->starting());
		m->stateMachine->addState(& m->stateInterface->started());
		m->stateMachine->addState(& m->stateInterface->stopping());
		m->stateMachine->addState(& m->stateInterface->broken());
		m->stateMachine->addState(& m->stateInterface->repairing());
		m->stateMachine->addState(& m->stateInterface->evacuating());
		m->stateMachine->setInitialState(& m->stateInterface->stopped());

		m->stateInterface->stopped().addTransition(this, & Service::started, & m->stateInterface->starting());
		m->stateInterface->started().addTransition(this, & Service::stopped, & m->stateInterface->stopping());
		m->stateInterface->broken().addTransition(this, & Service::started, & m->stateInterface->repairing());
		m->stateInterface->broken().addTransition(this, & Service::stopped, & m->stateInterface->evacuating());
		m->stateInterface->stopping().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->interrupted());
		m->stateInterface->starting().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->broken());
		m->stateInterface->repairing().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->broken());
		m->stateInterface->yielding().addTransition(this, & Service::activated, & m->stateInterface->active());
		m->stateInterface->evacuating().addTransition(& m->timeoutTimer, & QTimer::timeout, & m->stateInterface->interrupted());

		addTransition(& m->stateInterface->starting(), & m->stateInterface->started(), serviceable.transitionToStarted());
		addTransition(& m->stateInterface->repairing(), & m->stateInterface->started(), serviceable.transitionToStarted());
		addTransition(& m->stateInterface->stopping(), & m->stateInterface->stopped(), serviceable.transitionToStopped());
		addTransition(& m->stateInterface->starting(), & m->stateInterface->broken(), serviceable.transitionToBroken());
		addTransition(& m->stateInterface->started(), & m->stateInterface->broken(), serviceable.transitionToBroken());
		addTransition(& m->stateInterface->repairing(), & m->stateInterface->broken(), serviceable.transitionToBroken());
		if (serviceable.transitionToIdling())
			addTransition(& m->stateInterface->active(), & m->stateInterface->idling(), serviceable.transitionToIdling());
		addTransition(& m->stateInterface->idling(), & m->stateInterface->yielding(), serviceable.transitionToYielding());
		addTransition(& m->stateInterface->evacuating(), & m->stateInterface->stopped(), serviceable.transitionToStopped());

		addStatuses(serviceable.configureBroken(& m->stateInterface->broken()));
		addStatuses(serviceable.configureStarted(& m->stateInterface->active(), & m->stateInterface->idling(), & m->stateInterface->yielding()));
		addStatuses(serviceable.configureStarting(& m->stateInterface->starting()));
		addStatuses(serviceable.configureStopping(& m->stateInterface->stopping()));
		addStatuses(serviceable.configureRepairing(& m->stateInterface->repairing()));
		addStatuses(serviceable.configureEvacuating(& m->stateInterface->evacuating()));

		m->stateMachine->start();
		QCoreApplication::processEvents();	// This is required in order to truly start state machine and prevent it from ignoring incoming events.
	} catch (const std::exception & e) {
		CUTEHMI_CRITICAL("Could not initialize new state machine, because of following exception: " << e.what());
	}
}

void Service::addTransition(QState * source, QState * target, std::unique_ptr<QAbstractTransition> transition)
{
	if (transition) {
		transition->setParent(source);
		transition->setTargetState(target);
		source->addTransition(transition.release());
	} else
		source->addTransition(target);
}

void Service::addStatuses(std::unique_ptr<Serviceable::ServiceStatuses> statuses)
{
	if (statuses)
		for (auto stateStatus = statuses->begin(); stateStatus != statuses->end(); ++stateStatus)
			stateStatus.key()->assignProperty(m->stateInterface, "status", stateStatus.value());
}

}
}

//(c)C: Copyright © 2019-2020, Michał Policht <michal@policht.pl>, Yuri Chornoivan <yurchor@ukr.net>. All rights reserved.
//(c)C: This file is a part of CuteHMI.
//(c)C: CuteHMI is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//(c)C: CuteHMI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
//(c)C: You should have received a copy of the GNU Lesser General Public License along with CuteHMI.  If not, see <https://www.gnu.org/licenses/>.
<|endoftext|>


<commit_before>//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include <assert.h>
#include <dlfcn.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <set>

// The name of the CoreCLR native runtime DLL.
static const char * const coreClrDll = "libcoreclr.so";

// Windows types used by the ExecuteAssembly function
typedef unsigned int DWORD;
typedef const char16_t* LPCWSTR;
typedef const char* LPCSTR;
typedef int32_t HRESULT;

#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)

// Prototype of the ExecuteAssembly function from the libcoreclr.do
typedef HRESULT (*ExecuteAssemblyFunction)(
                    LPCSTR exePath,
                    LPCSTR coreClrPath,
                    LPCSTR appDomainFriendlyName,
                    int propertyCount,
                    LPCSTR* propertyKeys,
                    LPCSTR* propertyValues,
                    int argc,
                    LPCSTR* argv,
                    LPCSTR managedAssemblyPath,
                    DWORD* exitCode);

// Display the command line options
void DisplayUsage()
{
    fprintf(
        stderr,
        "Usage: corerun [OPTIONS] assembly [ARGUMENTS]\n"
        "Execute the specified managed assembly with the passed in arguments\n\n"
        "Options:\n"
        "-c, --clr-path  path to the libcoreclr.so and the managed CLR assemblies\n");
}

// Get absolute path from the specified path.
// Return true in case of a success, false otherwise.
bool GetAbsolutePath(const char* path, std::string& absolutePath)
{
    bool result = false;

    char realPath[PATH_MAX];
    if (realpath(path, realPath) != nullptr && realPath[0] != '\0')
    {
        absolutePath.assign(realPath);
        // The realpath should return canonicalized path without the trailing slash
        assert(absolutePath.back() != '/');

        result = true;
    }

    return result;
}

void GetDirectory(const char* path, std::string& directory)
{
    directory.assign(path);
    size_t lastSlash = directory.rfind('/');
    directory.erase(lastSlash);
}

// Parse the command line arguments
bool ParseArguments(
        const int argc,
        const char* argv[],
        const char** clrFilesPath,
        const char** managedAssemblyPath,
        int* managedAssemblyArgc,
        const char*** managedAssemblyArgv)
{
    bool success = false;

    *clrFilesPath = nullptr;
    *managedAssemblyPath = nullptr;
    *managedAssemblyArgv = nullptr;
    *managedAssemblyArgc = 0;

    // The command line must contain at least the current exe name and the managed assembly path
    if (argc >= 2)
    {
        for (int i = 1; i < argc; i++)
        {
            // Check for an option
            if (argv[i][0] == '-')
            {
                // Path to the libcoreclr.so and the managed CLR assemblies
                if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--clr-path") == 0)
                {
                    i++;
                    if (i < argc)
                    {
                        *clrFilesPath = argv[i];
                    }
                    else
                    {
                        fprintf(stderr, "Option %s: missing path\n", argv[i - 1]);
                        break;
                    }
                }
                else if (strcmp(argv[i], "--help") == 0)
                {
                    DisplayUsage();
                    break;
                }
                else
                {
                    fprintf(stderr, "Unknown option %s\n", argv[i]);
                    break;
                }
            }
            else
            {
                // First argument that is not an option is the managed assembly to execute
                *managedAssemblyPath = argv[i];

                int managedArgvOffset = (i + 1);
                *managedAssemblyArgc = argc - managedArgvOffset;
                if (*managedAssemblyArgc != 0)
                {
                    *managedAssemblyArgv = &argv[managedArgvOffset];
                }
                success = true;
                break;
            }
        }
    }
    else
    {
        DisplayUsage();
    }

    return success;
}

// Add all *.dll, *.ni.dll, *.exe, and *.ni.exe files from the specified directory
// to the tpaList string;
void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
{
    const char * const tpaExtensions[] = {
                ".ni.dll",		// Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
                ".dll",
                ".ni.exe",
                ".exe",
                };

    DIR* dir = opendir(directory);
    if (dir == nullptr)
    {
        return;
    }

    std::set<std::string> addedAssemblies;

    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
    // then files with .dll extension, etc.
    for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
    {
        const char* ext = tpaExtensions[extIndex];
        int extLength = strlen(ext);

        struct dirent* entry;

        // For all entries in the directory
        while ((entry = readdir(dir)) != nullptr)
        {
            // We are interested in files only
            if (entry->d_type != DT_REG)
            {
                continue;
            }

            std::string filename(entry->d_name);

            // Check if the extension matches the one we are looking for
            size_t extPos = filename.length() - extLength;
            if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
            {
                continue;
            }

            std::string filenameWithoutExt(filename.substr(0, extPos));

            // Make sure if we have an assembly with multiple extensions present,
            // we insert only one version of it.
            if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
            {
                addedAssemblies.insert(filenameWithoutExt);

                tpaList.append(directory);
                tpaList.append("/");
                tpaList.append(filename);
                tpaList.append(":");
            }
        }

        // Rewind the directory stream to be able to iterate over it for the next extension
        rewinddir(dir);
    }

    closedir(dir);
}

//
// Execute the specified managed assembly.
//
// Parameters:
//  currentExePath          - Path of the current executable
//  clrFilesAbsolutePath    - Absolute path of a folder where the libcoreclr.so and CLR managed assemblies are stored
//  managedAssemblyPath     - Path to the managed assembly to execute
//  managedAssemblyArgc     - Number of arguments passed to the executed assembly
//  managedAssemblyArgv     - Array of arguments passed to the executed assembly
//
// Returns:
//  ExitCode of the assembly
//
int ExecuteManagedAssembly(
            const char* currentExeAbsolutePath,
            const char* clrFilesAbsolutePath,
            const char* managedAssemblyAbsolutePath,
            int managedAssemblyArgc,
            const char** managedAssemblyArgv)
{
    // Indicates failure
    int exitCode = -1;

    std::string coreClrDllPath(clrFilesAbsolutePath);
    coreClrDllPath.append("/");
    coreClrDllPath.append(coreClrDll);

    if (coreClrDllPath.length() >= PATH_MAX)
    {
        fprintf(stderr, "Absolute path to libcoreclr.so too long\n");
        return -1;
    }

    // Get just the path component of the managed assembly path
    std::string appPath;
    GetDirectory(managedAssemblyAbsolutePath, appPath);

    std::string nativeDllSearchDirs(appPath);
    nativeDllSearchDirs.append(":");
    nativeDllSearchDirs.append(clrFilesAbsolutePath);

    std::string tpaList;
    AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList);

    void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW);
    if (coreclrLib != nullptr)
    {
        ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreclrLib, "ExecuteAssembly");
        if (executeAssembly != nullptr)
        {
            // Allowed property names:
            // APPBASE
            // - The base path of the application from which the exe and other assemblies will be loaded
            //
            // TRUSTED_PLATFORM_ASSEMBLIES
            // - The list of complete paths to each of the fully trusted assemblies
            //
            // APP_PATHS
            // - The list of paths which will be probed by the assembly loader
            //
            // APP_NI_PATHS
            // - The list of additional paths that the assembly loader will probe for ngen images
            //
            // NATIVE_DLL_SEARCH_DIRECTORIES
            // - The list of paths that will be probed for native DLLs called by PInvoke
            //
            const char *propertyKeys[] = {
                "TRUSTED_PLATFORM_ASSEMBLIES",
                "APP_PATHS",
                "APP_NI_PATHS",
                "NATIVE_DLL_SEARCH_DIRECTORIES"
            };
            const char *propertyValues[] = {
                // TRUSTED_PLATFORM_ASSEMBLIES
                tpaList.c_str(),
                // APP_PATHS
                appPath.c_str(),
                // APP_NI_PATHS
                appPath.c_str(),
                // NATIVE_DLL_SEARCH_DIRECTORIES
                nativeDllSearchDirs.c_str()
            };

            HRESULT st = executeAssembly(
                            currentExeAbsolutePath,
                            coreClrDllPath.c_str(),
                            "unixcorerun",
                            sizeof(propertyKeys) / sizeof(propertyKeys[0]),
                            propertyKeys,
                            propertyValues,
                            managedAssemblyArgc,
                            managedAssemblyArgv,
                            managedAssemblyAbsolutePath,
                            (DWORD*)&exitCode);

            if (!SUCCEEDED(st))
            {
                fprintf(stderr, "ExecuteAssembly failed - status: 0x%08x\n", st);
            }
        }
        else
        {
            fprintf(stderr, "Function ExecuteAssembly not found in the libcoreclr.so\n");
        }

        if (dlclose(coreclrLib) != 0)
        {
            fprintf(stderr, "Warning - dlclose failed\n");
        }
    }
    else
    {
        char* error = dlerror();
        fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
    }

    return exitCode;
}

int main(const int argc, const char* argv[])
{
    const char* clrFilesPath;
    const char* managedAssemblyPath;
    const char** managedAssemblyArgv;
    int managedAssemblyArgc;

    if (!ParseArguments(
            argc,
            argv,
            &clrFilesPath,
            &managedAssemblyPath,
            &managedAssemblyArgc,
            &managedAssemblyArgv))
    {
        // Invalid command line
        return -1;
    }

    // Check if the specified managed assembly file exists
    struct stat sb;
    if (stat(managedAssemblyPath, &sb) == -1)
    {
        perror("Managed assembly not found");
        return -1;
    }

    // Verify that the managed assembly path points to a file
    if (!S_ISREG(sb.st_mode))
    {
        fprintf(stderr, "The specified managed assembly is not a file\n");
        return -1;
    }

    // Convert the specified path to CLR files to an absolute path since the libcoreclr.so
    // requires it.
    std::string clrFilesAbsolutePath;
    std::string clrFilesRelativePath;

    if (clrFilesPath == nullptr)
    {
        // There was no CLR files path specified, use the folder of the corerun
        GetDirectory(argv[0], clrFilesRelativePath);
        clrFilesPath = clrFilesRelativePath.c_str();

        // TODO: consider using an env variable (if defined) as a fall-back.
        // The windows version of the corerun uses core_root env variable
    }

    if (!GetAbsolutePath(clrFilesPath, clrFilesAbsolutePath))
    {
        perror("Failed to convert CLR files path to absolute path");
        return -1;
    }

    std::string managedAssemblyAbsolutePath;
    if (!GetAbsolutePath(managedAssemblyPath, managedAssemblyAbsolutePath))
    {
        perror("Failed to convert managed assembly path to absolute path");
        return -1;
    }

    int exitCode = ExecuteManagedAssembly(
                            argv[0],
                            clrFilesAbsolutePath.c_str(),
                            managedAssemblyAbsolutePath.c_str(),
                            managedAssemblyArgc,
                            managedAssemblyArgv);
    return exitCode;
}
<commit_msg>Fix std::out_of_range exception with short filenames (Linux)<commit_after>//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include <assert.h>
#include <dlfcn.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <set>

// The name of the CoreCLR native runtime DLL.
static const char * const coreClrDll = "libcoreclr.so";

// Windows types used by the ExecuteAssembly function
typedef unsigned int DWORD;
typedef const char16_t* LPCWSTR;
typedef const char* LPCSTR;
typedef int32_t HRESULT;

#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)

// Prototype of the ExecuteAssembly function from the libcoreclr.do
typedef HRESULT (*ExecuteAssemblyFunction)(
                    LPCSTR exePath,
                    LPCSTR coreClrPath,
                    LPCSTR appDomainFriendlyName,
                    int propertyCount,
                    LPCSTR* propertyKeys,
                    LPCSTR* propertyValues,
                    int argc,
                    LPCSTR* argv,
                    LPCSTR managedAssemblyPath,
                    DWORD* exitCode);

// Display the command line options
void DisplayUsage()
{
    fprintf(
        stderr,
        "Usage: corerun [OPTIONS] assembly [ARGUMENTS]\n"
        "Execute the specified managed assembly with the passed in arguments\n\n"
        "Options:\n"
        "-c, --clr-path  path to the libcoreclr.so and the managed CLR assemblies\n");
}

// Get absolute path from the specified path.
// Return true in case of a success, false otherwise.
bool GetAbsolutePath(const char* path, std::string& absolutePath)
{
    bool result = false;

    char realPath[PATH_MAX];
    if (realpath(path, realPath) != nullptr && realPath[0] != '\0')
    {
        absolutePath.assign(realPath);
        // The realpath should return canonicalized path without the trailing slash
        assert(absolutePath.back() != '/');

        result = true;
    }

    return result;
}

void GetDirectory(const char* path, std::string& directory)
{
    directory.assign(path);
    size_t lastSlash = directory.rfind('/');
    directory.erase(lastSlash);
}

// Parse the command line arguments
bool ParseArguments(
        const int argc,
        const char* argv[],
        const char** clrFilesPath,
        const char** managedAssemblyPath,
        int* managedAssemblyArgc,
        const char*** managedAssemblyArgv)
{
    bool success = false;

    *clrFilesPath = nullptr;
    *managedAssemblyPath = nullptr;
    *managedAssemblyArgv = nullptr;
    *managedAssemblyArgc = 0;

    // The command line must contain at least the current exe name and the managed assembly path
    if (argc >= 2)
    {
        for (int i = 1; i < argc; i++)
        {
            // Check for an option
            if (argv[i][0] == '-')
            {
                // Path to the libcoreclr.so and the managed CLR assemblies
                if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--clr-path") == 0)
                {
                    i++;
                    if (i < argc)
                    {
                        *clrFilesPath = argv[i];
                    }
                    else
                    {
                        fprintf(stderr, "Option %s: missing path\n", argv[i - 1]);
                        break;
                    }
                }
                else if (strcmp(argv[i], "--help") == 0)
                {
                    DisplayUsage();
                    break;
                }
                else
                {
                    fprintf(stderr, "Unknown option %s\n", argv[i]);
                    break;
                }
            }
            else
            {
                // First argument that is not an option is the managed assembly to execute
                *managedAssemblyPath = argv[i];

                int managedArgvOffset = (i + 1);
                *managedAssemblyArgc = argc - managedArgvOffset;
                if (*managedAssemblyArgc != 0)
                {
                    *managedAssemblyArgv = &argv[managedArgvOffset];
                }
                success = true;
                break;
            }
        }
    }
    else
    {
        DisplayUsage();
    }

    return success;
}

// Add all *.dll, *.ni.dll, *.exe, and *.ni.exe files from the specified directory
// to the tpaList string;
void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
{
    const char * const tpaExtensions[] = {
                ".ni.dll",		// Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
                ".dll",
                ".ni.exe",
                ".exe",
                };

    DIR* dir = opendir(directory);
    if (dir == nullptr)
    {
        return;
    }

    std::set<std::string> addedAssemblies;

    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
    // then files with .dll extension, etc.
    for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
    {
        const char* ext = tpaExtensions[extIndex];
        int extLength = strlen(ext);

        struct dirent* entry;

        // For all entries in the directory
        while ((entry = readdir(dir)) != nullptr)
        {
            // We are interested in files only
            if (entry->d_type != DT_REG)
            {
                continue;
            }

            std::string filename(entry->d_name);

            // Check if the extension matches the one we are looking for
            int extPos = filename.length() - extLength;
            if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
            {
                continue;
            }

            std::string filenameWithoutExt(filename.substr(0, extPos));

            // Make sure if we have an assembly with multiple extensions present,
            // we insert only one version of it.
            if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
            {
                addedAssemblies.insert(filenameWithoutExt);

                tpaList.append(directory);
                tpaList.append("/");
                tpaList.append(filename);
                tpaList.append(":");
            }
        }

        // Rewind the directory stream to be able to iterate over it for the next extension
        rewinddir(dir);
    }

    closedir(dir);
}

//
// Execute the specified managed assembly.
//
// Parameters:
//  currentExePath          - Path of the current executable
//  clrFilesAbsolutePath    - Absolute path of a folder where the libcoreclr.so and CLR managed assemblies are stored
//  managedAssemblyPath     - Path to the managed assembly to execute
//  managedAssemblyArgc     - Number of arguments passed to the executed assembly
//  managedAssemblyArgv     - Array of arguments passed to the executed assembly
//
// Returns:
//  ExitCode of the assembly
//
int ExecuteManagedAssembly(
            const char* currentExeAbsolutePath,
            const char* clrFilesAbsolutePath,
            const char* managedAssemblyAbsolutePath,
            int managedAssemblyArgc,
            const char** managedAssemblyArgv)
{
    // Indicates failure
    int exitCode = -1;

    std::string coreClrDllPath(clrFilesAbsolutePath);
    coreClrDllPath.append("/");
    coreClrDllPath.append(coreClrDll);

    if (coreClrDllPath.length() >= PATH_MAX)
    {
        fprintf(stderr, "Absolute path to libcoreclr.so too long\n");
        return -1;
    }

    // Get just the path component of the managed assembly path
    std::string appPath;
    GetDirectory(managedAssemblyAbsolutePath, appPath);

    std::string nativeDllSearchDirs(appPath);
    nativeDllSearchDirs.append(":");
    nativeDllSearchDirs.append(clrFilesAbsolutePath);

    std::string tpaList;
    AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList);

    void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW);
    if (coreclrLib != nullptr)
    {
        ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreclrLib, "ExecuteAssembly");
        if (executeAssembly != nullptr)
        {
            // Allowed property names:
            // APPBASE
            // - The base path of the application from which the exe and other assemblies will be loaded
            //
            // TRUSTED_PLATFORM_ASSEMBLIES
            // - The list of complete paths to each of the fully trusted assemblies
            //
            // APP_PATHS
            // - The list of paths which will be probed by the assembly loader
            //
            // APP_NI_PATHS
            // - The list of additional paths that the assembly loader will probe for ngen images
            //
            // NATIVE_DLL_SEARCH_DIRECTORIES
            // - The list of paths that will be probed for native DLLs called by PInvoke
            //
            const char *propertyKeys[] = {
                "TRUSTED_PLATFORM_ASSEMBLIES",
                "APP_PATHS",
                "APP_NI_PATHS",
                "NATIVE_DLL_SEARCH_DIRECTORIES"
            };
            const char *propertyValues[] = {
                // TRUSTED_PLATFORM_ASSEMBLIES
                tpaList.c_str(),
                // APP_PATHS
                appPath.c_str(),
                // APP_NI_PATHS
                appPath.c_str(),
                // NATIVE_DLL_SEARCH_DIRECTORIES
                nativeDllSearchDirs.c_str()
            };

            HRESULT st = executeAssembly(
                            currentExeAbsolutePath,
                            coreClrDllPath.c_str(),
                            "unixcorerun",
                            sizeof(propertyKeys) / sizeof(propertyKeys[0]),
                            propertyKeys,
                            propertyValues,
                            managedAssemblyArgc,
                            managedAssemblyArgv,
                            managedAssemblyAbsolutePath,
                            (DWORD*)&exitCode);

            if (!SUCCEEDED(st))
            {
                fprintf(stderr, "ExecuteAssembly failed - status: 0x%08x\n", st);
            }
        }
        else
        {
            fprintf(stderr, "Function ExecuteAssembly not found in the libcoreclr.so\n");
        }

        if (dlclose(coreclrLib) != 0)
        {
            fprintf(stderr, "Warning - dlclose failed\n");
        }
    }
    else
    {
        char* error = dlerror();
        fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
    }

    return exitCode;
}

int main(const int argc, const char* argv[])
{
    const char* clrFilesPath;
    const char* managedAssemblyPath;
    const char** managedAssemblyArgv;
    int managedAssemblyArgc;

    if (!ParseArguments(
            argc,
            argv,
            &clrFilesPath,
            &managedAssemblyPath,
            &managedAssemblyArgc,
            &managedAssemblyArgv))
    {
        // Invalid command line
        return -1;
    }

    // Check if the specified managed assembly file exists
    struct stat sb;
    if (stat(managedAssemblyPath, &sb) == -1)
    {
        perror("Managed assembly not found");
        return -1;
    }

    // Verify that the managed assembly path points to a file
    if (!S_ISREG(sb.st_mode))
    {
        fprintf(stderr, "The specified managed assembly is not a file\n");
        return -1;
    }

    // Convert the specified path to CLR files to an absolute path since the libcoreclr.so
    // requires it.
    std::string clrFilesAbsolutePath;
    std::string clrFilesRelativePath;

    if (clrFilesPath == nullptr)
    {
        // There was no CLR files path specified, use the folder of the corerun
        GetDirectory(argv[0], clrFilesRelativePath);
        clrFilesPath = clrFilesRelativePath.c_str();

        // TODO: consider using an env variable (if defined) as a fall-back.
        // The windows version of the corerun uses core_root env variable
    }

    if (!GetAbsolutePath(clrFilesPath, clrFilesAbsolutePath))
    {
        perror("Failed to convert CLR files path to absolute path");
        return -1;
    }

    std::string managedAssemblyAbsolutePath;
    if (!GetAbsolutePath(managedAssemblyPath, managedAssemblyAbsolutePath))
    {
        perror("Failed to convert managed assembly path to absolute path");
        return -1;
    }

    int exitCode = ExecuteManagedAssembly(
                            argv[0],
                            clrFilesAbsolutePath.c_str(),
                            managedAssemblyAbsolutePath.c_str(),
                            managedAssemblyArgc,
                            managedAssemblyArgv);
    return exitCode;
}
<|endoftext|>


<commit_before>
#include "simple_paint_file.h"
#include "simple_paint_util.h"

#include <iup.h>
#include <im_convert.h>
#include <im_process.h>

void SimplePaintFile::Close()
{
  if (filename)
    delete[] filename;

  if (image)
    imImageDestroy(image);
}

void SimplePaintFile::SetFilename(const char* new_filename)
{
  if (filename != new_filename)
  {
    if (filename)
      delete[] filename;

    filename = str_duplicate(new_filename);
  }
}

imImage* SimplePaintFile::Read(const char* new_filename) const
{
  int error;
  imImage* image = imFileImageLoadBitmap(new_filename, 0, &error);
  if (error)
    show_file_error(error);
  return image;
}

bool SimplePaintFile::Write(const char* new_filename) const
{
  const char* format = imImageGetAttribString(image, "FileFormat");
  int error = imFileImageSave(new_filename, format, image);
  if (error)
  {
    show_file_error(error);
    return false;
  }
  return true;
}

void SimplePaintFile::SetFormat(const char* new_filename)
{
  const char* ext = str_fileext(new_filename);
  const char* format = "JPEG";
  if (str_compare(ext, "jpg", 0) || str_compare(ext, "jpeg", 0))
    format = "JPEG";
  else if (str_compare(ext, "bmp", 0))
    format = "BMP";
  else if (str_compare(ext, "png", 0))
    format = "PNG";
  else if (str_compare(ext, "tga", 0))
    format = "TGA";
  else if (str_compare(ext, "tif", 0) || str_compare(ext, "tiff", 0))
    format = "TIFF";
  imImageSetAttribString(image, "FileFormat", format);
}

bool SimplePaintFile::SaveCheck()
{
  if (dirty)
  {
    switch (IupAlarm("Warning", "File not saved! Save it now?", "Yes", "No", "Cancel"))
    {
    case 1:  /* save the changes and continue */
      SaveFile();
      break;
    case 2:  /* ignore the changes and continue */
      break;
    case 3:  /* cancel */
      return false;
    }
  }
  return true;
}

void SimplePaintFile::SaveFile()
{
  if (Write(filename))
    dirty = false;
}

bool SimplePaintFile::SaveAsFile(const char* new_filename)
{
  SetFormat(new_filename);

  if (Write(new_filename))
  {
    SetFilename(new_filename);
    dirty = false;

    return true;
  }

  return false;
}

static void image_fill_white(imImage* image)
{
  float color[3];

  color[0] = 255;
  color[1] = 255;
  color[2] = 255;

  imProcessRenderConstant(image, color);
}

bool SimplePaintFile::New(int width, int height)
{
  imImage* new_image = imImageCreate(width, height, IM_RGB, IM_BYTE);
  if (!new_image)
  {
    show_file_error(IM_ERR_MEM);
    return false;
  }

  /* new image default contents */
  image_fill_white(new_image);

  /* default file format */
  imImageSetAttribString(new_image, "FileFormat", "JPEG");

  SetImage(new_image);

  /* set properties */
  SetFilename(0);
  dirty = false;

  return true;
}

void SimplePaintFile::SetImage(imImage* new_image, bool release)
{
  /* remove previous one if any */
  if (release && image)
    imImageDestroy(image);

  /* set properties (leave filename as it is) */
  dirty = true;
  image = new_image;
}

void SimplePaintFile::New(imImage* new_image)
{
  /* this tests are necessary only for open and paste */

  /* we are going to support only RGB images with no alpha */
  imImageRemoveAlpha(new_image);
  if (new_image->color_space != IM_RGB)
  {
    imImage* rgb_image = imImageCreateBased(new_image, -1, -1, IM_RGB, -1);
    imConvertColorSpace(new_image, rgb_image);
    imImageDestroy(new_image);

    new_image = rgb_image;
  }

  /* default file format */
  const char* format = imImageGetAttribString(new_image, "FileFormat");
  if (!format)
    imImageSetAttribString(new_image, "FileFormat", "JPEG");

  SetImage(new_image);
}

bool SimplePaintFile::Open(const char* new_filename)
{
  imImage* new_image = Read(new_filename);
  if (new_image)
  {
    New(new_image);

    /* set properties */
    dirty = false;
    SetFilename(new_filename);

    return true;
  }

  return false;
}
<commit_msg><commit_after>
#include "simple_paint_file.h"
#include "simple_paint_util.h"

#include <iup.h>
#include <im_convert.h>
#include <im_process.h>

void SimplePaintFile::Close()
{
  if (filename)
    delete[] filename;

  if (image)
    imImageDestroy(image);
}

void SimplePaintFile::SetFilename(const char* new_filename)
{
  if (filename != new_filename)
  {
    if (filename)
      delete[] filename;

    filename = str_duplicate(new_filename);
  }
}

imImage* SimplePaintFile::Read(const char* new_filename) const
{
  int error;
  imImage* image = imFileImageLoadBitmap(new_filename, 0, &error);
  if (error)
    show_file_error(error);
  return image;
}

bool SimplePaintFile::Write(const char* new_filename) const
{
  const char* format = imImageGetAttribString(image, "FileFormat");
  int error = imFileImageSave(new_filename, format, image);
  if (error)
  {
    show_file_error(error);
    return false;
  }
  return true;
}

void SimplePaintFile::SetFormat(const char* new_filename)
{
  const char* ext = str_fileext(new_filename);
  const char* format = "JPEG";
  if (str_compare(ext, "jpg", 0) || str_compare(ext, "jpeg", 0))
    format = "JPEG";
  else if (str_compare(ext, "bmp", 0))
    format = "BMP";
  else if (str_compare(ext, "png", 0))
    format = "PNG";
  else if (str_compare(ext, "tga", 0))
    format = "TGA";
  else if (str_compare(ext, "tif", 0) || str_compare(ext, "tiff", 0))
    format = "TIFF";
  imImageSetAttribString(image, "FileFormat", format);
}

bool SimplePaintFile::SaveCheck()
{
  if (dirty)
  {
    switch (IupAlarm("Warning", "File not saved! Save it now?", "Yes", "No", "Cancel"))
    {
    case 1:  /* save the changes and continue */
      SaveFile();
      break;
    case 2:  /* ignore the changes and continue */
      break;
    case 3:  /* cancel */
      return false;
    }
  }
  return true;
}

void SimplePaintFile::SaveFile()
{
  if (Write(filename))
    dirty = false;
}

bool SimplePaintFile::SaveAsFile(const char* new_filename)
{
  SetFormat(new_filename);

  if (Write(new_filename))
  {
    SetFilename(new_filename);
    dirty = false;

    return true;
  }

  return false;
}

static void image_fill_white(imImage* image)
{
  float color[3];

  color[0] = 255;
  color[1] = 255;
  color[2] = 255;

  imProcessRenderConstant(image, color);
}

bool SimplePaintFile::New(int width, int height)
{
  imImage* new_image = imImageCreate(width, height, IM_RGB, IM_BYTE);
  if (!new_image)
  {
    show_file_error(IM_ERR_MEM);
    return false;
  }

  /* new image default contents */
  image_fill_white(new_image);

  /* default file format */
  imImageSetAttribString(new_image, "FileFormat", "JPEG");

  SetImage(new_image);

  /* set properties */
  SetFilename(0);
  dirty = false;

  return true;
}

void SimplePaintFile::SetImage(imImage* new_image, bool release)
{
  /* remove previous one if any */
  if (release && image)
    imImageDestroy(image);

  /* set properties (leave filename as it is) */
  dirty = true;
  image = new_image;
}

void SimplePaintFile::New(imImage* new_image)
{
  /* this tests are necessary only for open and paste */

  /* we are going to support only RGB images with no alpha */
  imImageRemoveAlpha(new_image);
  if (new_image->color_space != IM_RGB)
  {
    imImage* rgb_image = imImageCreateBased(new_image, -1, -1, IM_RGB, -1);
    imConvertColorSpace(new_image, rgb_image);
    imImageDestroy(new_image);

    new_image = rgb_image;
  }

  /* default file format */
  const char* format = imImageGetAttribString(new_image, "FileFormat");
  if (!format)
    imImageSetAttribString(new_image, "FileFormat", "JPEG");

  SetImage(new_image);
}

bool SimplePaintFile::Open(const char* new_filename)
{
  imImage* new_image = Read(new_filename);
  if (new_image)
  {
    New(new_image);

    /* set properties */
    dirty = false;
    SetFilename(new_filename);

    return true;
  }

  return false;
}
<|endoftext|>


<commit_before>/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @author Intel, Evgueni Brevnov
 * @version $Revision: 1.1 $
 */

#include <stdlib.h>
#include <apr_thread_mutex.h>

#include "open/hythread.h"
#include "open/jthread.h"
#include "open/gc.h"

#include "jni.h"
#include "jni_direct.h"
#include "environment.h"
#include "classloader.h"
#include "compile.h"
#include "nogc.h"
#include "jni_utils.h"
#include "vm_stats.h"
#include "thread_dump.h"
#include "interpreter.h"
#include "finalize.h"

#define LOG_DOMAIN "vm.core.shutdown"
#include "cxxlog.h"

#define PROCESS_EXCEPTION(messageId, message) \
{ \
    LECHO(messageId, message << "Internal error: "); \
\
    if (jni_env->ExceptionCheck()== JNI_TRUE) \
    { \
        jni_env->ExceptionDescribe(); \
        jni_env->ExceptionClear(); \
    } \
\
    return JNI_ERR; \
} \

/**
 * Calls java.lang.System.execShutdownSequence() method.
 *
 * @param jni_env JNI environment of the current thread
 */
static jint exec_shutdown_sequence(JNIEnv * jni_env) {
    jclass system_class;
    jmethodID shutdown_method;

    assert(hythread_is_suspend_enabled());
    BEGIN_RAISE_AREA;

    system_class = jni_env->FindClass("java/lang/System");
    if (jni_env->ExceptionCheck() == JNI_TRUE || system_class == NULL) {
        // This is debug message only. May appear when VM is already in shutdown stage.
        PROCESS_EXCEPTION(38, "{0}can't find java.lang.System class.");
    }

    shutdown_method = jni_env->GetStaticMethodID(system_class, "execShutdownSequence", "()V");
    if (jni_env->ExceptionCheck() == JNI_TRUE || shutdown_method == NULL) {
        PROCESS_EXCEPTION(39, "{0}can't find java.lang.System.execShutdownSequence() method.");
    }

    jni_env->CallStaticVoidMethod(system_class, shutdown_method);

    if (jni_env->ExceptionCheck() == JNI_TRUE) {
        PROCESS_EXCEPTION(40, "{0}java.lang.System.execShutdownSequence() method completed with an exception.");
    }

    END_RAISE_AREA;
    return JNI_OK;
}

static void vm_shutdown_callback() {
    hythread_suspend_enable();
    set_unwindable(false);

    vm_thread_t vm_thread = jthread_self_vm_thread();
    assert(vm_thread);

    jobject java_thread = vm_thread->java_thread;
    assert(java_thread);

    IDATA UNUSED status = jthread_detach(java_thread);
    assert(status == TM_ERROR_NONE);

    hythread_exit(NULL);
}

static void vm_thread_cancel(vm_thread_t thread) {
    assert(thread);

    // grab hythread global lock
    hythread_global_lock();

    IDATA UNUSED status = jthread_vm_detach(thread);
    assert(status == TM_ERROR_NONE);

    hythread_cancel((hythread_t)thread);

    // release hythread global lock
    hythread_global_unlock();
}

/**
 * Stops running java threads by throwing an exception
 * up to the first native frame.
 *
 * @param[in] vm_env a global VM environment
 */
static void vm_shutdown_stop_java_threads(Global_Env * vm_env) {
    hythread_t self;
    hythread_t * running_threads;
    hythread_t native_thread;
    hythread_iterator_t it;
    VM_thread *vm_thread;

    self = hythread_self();

    // Collect running java threads.
    // Set callbacks to let threads exit
    TRACE2("shutdown", "stopping threads, self " << self);
    it = hythread_iterator_create(NULL);
    running_threads = (hythread_t *)apr_palloc(vm_env->mem_pool,
            hythread_iterator_size(it) * sizeof(hythread_t));
    int size = 0;
    while(native_thread = hythread_iterator_next(&it)) {
        vm_thread = jthread_get_vm_thread(native_thread);
        if (native_thread != self && vm_thread != NULL) {
            hythread_set_safepoint_callback(native_thread,
                    vm_shutdown_callback);
            running_threads[size] = native_thread;
            ++size;
        }
    }
    hythread_iterator_release(&it);

    TRACE2("shutdown", "joining threads");
    // join running threads
    // blocked and waiting threads won't be joined
    for (int i = 0; i < size; i++) {
        hythread_join_timed(running_threads[i], 100/size + 1, 0);
    }

    TRACE2("shutdown", "cancelling threads");
    // forcedly kill remaining threads
    // There is a small chance that some of these threads are not in the point
    // safe for killing, e.g. in malloc()
    it = hythread_iterator_create(NULL);
    while(native_thread = hythread_iterator_next(&it)) {
        vm_thread = jthread_get_vm_thread(native_thread);
        // we should not cancel self and
        // non-java threads (i.e. vm_thread == NULL)
        if (native_thread != self && vm_thread != NULL) {
            vm_thread_cancel(vm_thread);
            TRACE2("shutdown", "cancelling " << native_thread);
            STD_FREE(vm_thread);
        }
    }
    hythread_iterator_release(&it);

    TRACE2("shutdown", "shutting down threads complete");
}

/**
 * Waits until all non-daemon threads finish their execution,
 * initiates VM shutdown sequence and stops running threads if any.
 *
 * @param[in] java_vm JVM that should be destroyed
 * @param[in] java_thread current java thread
 */
jint vm_destroy(JavaVM_Internal * java_vm, jthread java_thread)
{
    IDATA status;
    JNIEnv * jni_env;
    jobject uncaught_exception;

    assert(hythread_is_suspend_enabled());

    jni_env = jthread_get_JNI_env(java_thread);

    // Wait until all non-daemon threads finish their execution.
    status = jthread_wait_for_all_nondaemon_threads();
    if (status != TM_ERROR_NONE) {
        TRACE("Failed to wait for all non-daemon threads completion.");
        return JNI_ERR;
    }

    // Print out gathered data.
#ifdef VM_STATS
    VM_Statistics::get_vm_stats().print();
#endif

    // Remember thread's uncaught exception if any.
    uncaught_exception = jni_env->ExceptionOccurred();
    jni_env->ExceptionClear();

    // Execute pending shutdown hooks & finalizers
    status = exec_shutdown_sequence(jni_env);
    if (status != JNI_OK) return (jint)status;

    if(get_native_finalizer_thread_flag())
        wait_native_fin_threads_detached();
    if(get_native_ref_enqueue_thread_flag())
        wait_native_ref_thread_detached();

    // Raise uncaught exception to current thread.
    // It will be properly processed in jthread_java_detach().
    if (uncaught_exception) {
        exn_raise_object(uncaught_exception);
    }

    // Send VM_Death event and switch to DEAD phase.
    // This should be the last event sent by VM.
    // This event should be sent before Agent_OnUnload called.
    jvmti_send_vm_death_event();

    // prepare thread manager to shutdown
    hythread_shutdowning();

    // Stop all (except current) java threads
    // before destroying VM-wide data.
    vm_shutdown_stop_java_threads(java_vm->vm_env);

    // TODO: ups we don't stop native threads as well :-((
    // We are lucky! Currently, there are no such threads.

    // Detach current main thread.
    status = jthread_detach(java_thread);

    // check detach status
    if (status != TM_ERROR_NONE)
        return JNI_ERR;

    // Shutdown signals
    extern void shutdown_signals();
    shutdown_signals();

    // Call Agent_OnUnload() for agents and unload agents.
    java_vm->vm_env->TI->Shutdown(java_vm);

    // Block thread creation.
    // TODO: investigate how to achieve that with ThreadManager

    // Starting this moment any exception occurred in java thread will cause
    // entire java stack unwinding to the most recent native frame.
    // JNI is not available as well.
    assert(java_vm->vm_env->vm_state == Global_Env::VM_RUNNING);
    java_vm->vm_env->vm_state = Global_Env::VM_SHUTDOWNING;

    TRACE2("shutdown", "vm_destroy complete");
    return JNI_OK;
}

static IDATA vm_interrupt_process(void * data) {
    vm_thread_t * threadBuf;
    int i;

    threadBuf = (vm_thread_t *)data;
    i = 0;
    // Join all threads.
    while (threadBuf[i] != NULL) {
        hythread_join((hythread_t)threadBuf[i]);
        STD_FREE(threadBuf[i]);
        i++;
    }

    STD_FREE(threadBuf);

    // Return 130 to be compatible with RI.
    exit(130);
}

/**
 * Initiates VM shutdown sequence.
 */
static IDATA vm_interrupt_entry_point(void * data) {
    JNIEnv * jni_env;
    JavaVMAttachArgs args;
    JavaVM * java_vm;
    jint status;

    java_vm = (JavaVM *)data;
    args.version = JNI_VERSION_1_2;
    args.group = NULL;
    args.name = "InterruptionHandler";

    status = AttachCurrentThread(java_vm, (void **)&jni_env, &args);
    if (status == JNI_OK) {
        exec_shutdown_sequence(jni_env);
        DetachCurrentThread(java_vm);
    }
    return status;
}

/**
 * Release allocated resourses.
 */
static IDATA vm_dump_process(void * data) {
    vm_thread_t * threadBuf;
    int i;

    threadBuf = (vm_thread_t *)data;
    i = 0;
    // Join all threads and release allocated resources.
    while (threadBuf[i] != NULL) {
        hythread_join((hythread_t)threadBuf[i]);
        STD_FREE(threadBuf[i]);
        i++;
    }
    STD_FREE(threadBuf);

    return TM_ERROR_NONE;
}

/**
 * Dumps all java stacks.
 */
static IDATA vm_dump_entry_point(void * data) {
    JNIEnv * jni_env;
    JavaVMAttachArgs args;
    JavaVM * java_vm;
    jint status;

    java_vm = (JavaVM *)data;
    args.version = JNI_VERSION_1_2;
    args.group = NULL;
    args.name = "DumpHandler";

    status = AttachCurrentThread(java_vm, (void **)&jni_env, &args);
    if (status == JNI_OK) {
        // TODO: specify particular VM to notify.
        jvmti_notify_data_dump_request();
        st_print_all(stdout);
        DetachCurrentThread(java_vm);
    }
    return status;
}

/**
 * Current process received an interruption signal (Ctrl+C pressed).
 * Shutdown all running VMs and terminate the process.
 */
void vm_interrupt_handler(int UNREF x) {
    JavaVM ** vmBuf;
    vm_thread_t * threadBuf;
    int nVMs;
    IDATA status;

    status = JNI_GetCreatedJavaVMs(NULL, 0, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK)
        return;

    vmBuf = (JavaVM **) STD_MALLOC(nVMs * sizeof(JavaVM *));
    status = JNI_GetCreatedJavaVMs(vmBuf, nVMs, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK)
        goto cleanup;

    threadBuf = (vm_thread_t*) STD_MALLOC((nVMs + 1) * sizeof(vm_thread_t));
    assert(threadBuf);

    // Create a new thread for each VM to avoid scalability and deadlock problems.
    for (int i = 0; i < nVMs; i++) {
        threadBuf[i] = jthread_allocate_thread();
        status = hythread_create_ex((hythread_t)threadBuf[i], NULL, 0, 0, NULL,
            vm_interrupt_entry_point, (void *)vmBuf[i]);
        assert(status == TM_ERROR_NONE);
    }

    // spawn a new thread which will terminate the process.
    status = hythread_create(NULL, 0, 0, 0,
        vm_interrupt_process, (void *)threadBuf);
    assert(status == TM_ERROR_NONE);

    // set a NULL terminator
    threadBuf[nVMs] = NULL;

cleanup:
    STD_FREE(vmBuf);
}

/**
 * Current process received an ??? signal (Ctrl+Break pressed).
 * Prints java stack traces for each VM running in the current process.
 */
void vm_dump_handler(int UNREF x) {
    int nVMs;
    vm_thread_t *threadBuf;

    jint status = JNI_GetCreatedJavaVMs(NULL, 0, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK)
        return;

    JavaVM ** vmBuf = (JavaVM **) STD_MALLOC(nVMs * sizeof(JavaVM *));
    status = JNI_GetCreatedJavaVMs(vmBuf, nVMs, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK) {
        goto cleanup;
    }

    threadBuf =
        (vm_thread_t*)STD_MALLOC((nVMs + 1) * sizeof(vm_thread_t));
    assert(threadBuf);

    // Create a new thread for each VM to avoid scalability and deadlock problems.
    IDATA UNUSED hy_status;
    for (int i = 0; i < nVMs; i++) {
        threadBuf[i] = jthread_allocate_thread();
        hy_status = hythread_create_ex((hythread_t)threadBuf[i],
            NULL, 0, 0, NULL, vm_dump_entry_point, (void *)vmBuf[i]);
        assert(hy_status == TM_ERROR_NONE);
    }

    // spawn a new thread which will release resources.
    hy_status = hythread_create(NULL, 0, 0, 0,
        vm_dump_process, (void *)threadBuf);
    assert(hy_status == TM_ERROR_NONE);

cleanup:
    STD_FREE(vmBuf);
}
<commit_msg>Applied VM side fix for HARMONY-5019 [jdktools][jpda] Agent has to ensure that all of its threads are terminated when Agent_OnUnload exits<commit_after>/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @author Intel, Evgueni Brevnov
 * @version $Revision: 1.1 $
 */

#include <stdlib.h>
#include <apr_thread_mutex.h>

#include "open/hythread.h"
#include "open/jthread.h"
#include "open/gc.h"

#include "jni.h"
#include "jni_direct.h"
#include "environment.h"
#include "classloader.h"
#include "compile.h"
#include "nogc.h"
#include "jni_utils.h"
#include "vm_stats.h"
#include "thread_dump.h"
#include "interpreter.h"
#include "finalize.h"

#define LOG_DOMAIN "vm.core.shutdown"
#include "cxxlog.h"

#define PROCESS_EXCEPTION(messageId, message) \
{ \
    LECHO(messageId, message << "Internal error: "); \
\
    if (jni_env->ExceptionCheck()== JNI_TRUE) \
    { \
        jni_env->ExceptionDescribe(); \
        jni_env->ExceptionClear(); \
    } \
\
    return JNI_ERR; \
} \

/**
 * Calls java.lang.System.execShutdownSequence() method.
 *
 * @param jni_env JNI environment of the current thread
 */
static jint exec_shutdown_sequence(JNIEnv * jni_env) {
    jclass system_class;
    jmethodID shutdown_method;

    assert(hythread_is_suspend_enabled());
    BEGIN_RAISE_AREA;

    system_class = jni_env->FindClass("java/lang/System");
    if (jni_env->ExceptionCheck() == JNI_TRUE || system_class == NULL) {
        // This is debug message only. May appear when VM is already in shutdown stage.
        PROCESS_EXCEPTION(38, "{0}can't find java.lang.System class.");
    }

    shutdown_method = jni_env->GetStaticMethodID(system_class, "execShutdownSequence", "()V");
    if (jni_env->ExceptionCheck() == JNI_TRUE || shutdown_method == NULL) {
        PROCESS_EXCEPTION(39, "{0}can't find java.lang.System.execShutdownSequence() method.");
    }

    jni_env->CallStaticVoidMethod(system_class, shutdown_method);

    if (jni_env->ExceptionCheck() == JNI_TRUE) {
        PROCESS_EXCEPTION(40, "{0}java.lang.System.execShutdownSequence() method completed with an exception.");
    }

    END_RAISE_AREA;
    return JNI_OK;
}

static void vm_shutdown_callback() {
    hythread_suspend_enable();
    set_unwindable(false);

    vm_thread_t vm_thread = jthread_self_vm_thread();
    assert(vm_thread);

    jobject java_thread = vm_thread->java_thread;
    assert(java_thread);

    IDATA UNUSED status = jthread_detach(java_thread);
    assert(status == TM_ERROR_NONE);

    hythread_exit(NULL);
}

static void vm_thread_cancel(vm_thread_t thread) {
    assert(thread);

    // grab hythread global lock
    hythread_global_lock();

    IDATA UNUSED status = jthread_vm_detach(thread);
    assert(status == TM_ERROR_NONE);

    hythread_cancel((hythread_t)thread);

    // release hythread global lock
    hythread_global_unlock();
}

/**
 * Stops running java threads by throwing an exception
 * up to the first native frame.
 *
 * @param[in] vm_env a global VM environment
 */
static void vm_shutdown_stop_java_threads(Global_Env * vm_env) {
    hythread_t self;
    hythread_t * running_threads;
    hythread_t native_thread;
    hythread_iterator_t it;
    VM_thread *vm_thread;

    self = hythread_self();

    // Collect running java threads.
    // Set callbacks to let threads exit
    TRACE2("shutdown", "stopping threads, self " << self);
    it = hythread_iterator_create(NULL);
    running_threads = (hythread_t *)apr_palloc(vm_env->mem_pool,
            hythread_iterator_size(it) * sizeof(hythread_t));
    int size = 0;
    while(native_thread = hythread_iterator_next(&it)) {
        vm_thread = jthread_get_vm_thread(native_thread);
        if (native_thread != self && vm_thread != NULL) {
            hythread_set_safepoint_callback(native_thread,
                    vm_shutdown_callback);
            running_threads[size] = native_thread;
            ++size;
        }
    }
    hythread_iterator_release(&it);

    TRACE2("shutdown", "joining threads");
    // join running threads
    // blocked and waiting threads won't be joined
    for (int i = 0; i < size; i++) {
        hythread_join_timed(running_threads[i], 100/size + 1, 0);
    }

    TRACE2("shutdown", "cancelling threads");
    // forcedly kill remaining threads
    // There is a small chance that some of these threads are not in the point
    // safe for killing, e.g. in malloc()
    it = hythread_iterator_create(NULL);
    while(native_thread = hythread_iterator_next(&it)) {
        vm_thread = jthread_get_vm_thread(native_thread);
        // we should not cancel self and
        // non-java threads (i.e. vm_thread == NULL)
        if (native_thread != self && vm_thread != NULL) {
            vm_thread_cancel(vm_thread);
            TRACE2("shutdown", "cancelling " << native_thread);
            STD_FREE(vm_thread);
        }
    }
    hythread_iterator_release(&it);

    TRACE2("shutdown", "shutting down threads complete");
}

/**
 * Waits until all non-daemon threads finish their execution,
 * initiates VM shutdown sequence and stops running threads if any.
 *
 * @param[in] java_vm JVM that should be destroyed
 * @param[in] java_thread current java thread
 */
jint vm_destroy(JavaVM_Internal * java_vm, jthread java_thread)
{
    IDATA status;
    JNIEnv * jni_env;
    jobject uncaught_exception;

    assert(hythread_is_suspend_enabled());

    jni_env = jthread_get_JNI_env(java_thread);

    // Wait until all non-daemon threads finish their execution.
    status = jthread_wait_for_all_nondaemon_threads();
    if (status != TM_ERROR_NONE) {
        TRACE("Failed to wait for all non-daemon threads completion.");
        return JNI_ERR;
    }

    // Print out gathered data.
#ifdef VM_STATS
    VM_Statistics::get_vm_stats().print();
#endif

    // Remember thread's uncaught exception if any.
    uncaught_exception = jni_env->ExceptionOccurred();
    jni_env->ExceptionClear();

    // Execute pending shutdown hooks & finalizers
    status = exec_shutdown_sequence(jni_env);
    if (status != JNI_OK) return (jint)status;

    if(get_native_finalizer_thread_flag())
        wait_native_fin_threads_detached();
    if(get_native_ref_enqueue_thread_flag())
        wait_native_ref_thread_detached();

    // Raise uncaught exception to current thread.
    // It will be properly processed in jthread_java_detach().
    if (uncaught_exception) {
        exn_raise_object(uncaught_exception);
    }

    // Send VM_Death event and switch to DEAD phase.
    // This should be the last event sent by VM.
    // This event should be sent before Agent_OnUnload called.
    jvmti_send_vm_death_event();

    // prepare thread manager to shutdown
    hythread_shutdowning();

    // Call Agent_OnUnload() for agents and unload agents.
    // Gregory -
    // We cannot call this function after vm_shutdown_stop_java_threads!!!
    // In this case agent's thread won't be shutdown properly, and the
    // code of agent's thread will run in unmapped address space
    // of unloaded agent's library. This is bad and will almost certainly crash.
    java_vm->vm_env->TI->Shutdown(java_vm);

    // Stop all (except current) java threads
    // before destroying VM-wide data.
    vm_shutdown_stop_java_threads(java_vm->vm_env);

    // TODO: ups we don't stop native threads as well :-((
    // We are lucky! Currently, there are no such threads.

    // Detach current main thread.
    status = jthread_detach(java_thread);

    // check detach status
    if (status != TM_ERROR_NONE)
        return JNI_ERR;

    // Shutdown signals
    extern void shutdown_signals();
    shutdown_signals();

    // Block thread creation.
    // TODO: investigate how to achieve that with ThreadManager

    // Starting this moment any exception occurred in java thread will cause
    // entire java stack unwinding to the most recent native frame.
    // JNI is not available as well.
    assert(java_vm->vm_env->vm_state == Global_Env::VM_RUNNING);
    java_vm->vm_env->vm_state = Global_Env::VM_SHUTDOWNING;

    TRACE2("shutdown", "vm_destroy complete");
    return JNI_OK;
}

static IDATA vm_interrupt_process(void * data) {
    vm_thread_t * threadBuf;
    int i;

    threadBuf = (vm_thread_t *)data;
    i = 0;
    // Join all threads.
    while (threadBuf[i] != NULL) {
        hythread_join((hythread_t)threadBuf[i]);
        STD_FREE(threadBuf[i]);
        i++;
    }

    STD_FREE(threadBuf);

    // Return 130 to be compatible with RI.
    exit(130);
}

/**
 * Initiates VM shutdown sequence.
 */
static IDATA vm_interrupt_entry_point(void * data) {
    JNIEnv * jni_env;
    JavaVMAttachArgs args;
    JavaVM * java_vm;
    jint status;

    java_vm = (JavaVM *)data;
    args.version = JNI_VERSION_1_2;
    args.group = NULL;
    args.name = "InterruptionHandler";

    status = AttachCurrentThread(java_vm, (void **)&jni_env, &args);
    if (status == JNI_OK) {
        exec_shutdown_sequence(jni_env);
        DetachCurrentThread(java_vm);
    }
    return status;
}

/**
 * Release allocated resourses.
 */
static IDATA vm_dump_process(void * data) {
    vm_thread_t * threadBuf;
    int i;

    threadBuf = (vm_thread_t *)data;
    i = 0;
    // Join all threads and release allocated resources.
    while (threadBuf[i] != NULL) {
        hythread_join((hythread_t)threadBuf[i]);
        STD_FREE(threadBuf[i]);
        i++;
    }
    STD_FREE(threadBuf);

    return TM_ERROR_NONE;
}

/**
 * Dumps all java stacks.
 */
static IDATA vm_dump_entry_point(void * data) {
    JNIEnv * jni_env;
    JavaVMAttachArgs args;
    JavaVM * java_vm;
    jint status;

    java_vm = (JavaVM *)data;
    args.version = JNI_VERSION_1_2;
    args.group = NULL;
    args.name = "DumpHandler";

    status = AttachCurrentThread(java_vm, (void **)&jni_env, &args);
    if (status == JNI_OK) {
        // TODO: specify particular VM to notify.
        jvmti_notify_data_dump_request();
        st_print_all(stdout);
        DetachCurrentThread(java_vm);
    }
    return status;
}

/**
 * Current process received an interruption signal (Ctrl+C pressed).
 * Shutdown all running VMs and terminate the process.
 */
void vm_interrupt_handler(int UNREF x) {
    JavaVM ** vmBuf;
    vm_thread_t * threadBuf;
    int nVMs;
    IDATA status;

    status = JNI_GetCreatedJavaVMs(NULL, 0, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK)
        return;

    vmBuf = (JavaVM **) STD_MALLOC(nVMs * sizeof(JavaVM *));
    status = JNI_GetCreatedJavaVMs(vmBuf, nVMs, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK)
        goto cleanup;

    threadBuf = (vm_thread_t*) STD_MALLOC((nVMs + 1) * sizeof(vm_thread_t));
    assert(threadBuf);

    // Create a new thread for each VM to avoid scalability and deadlock problems.
    for (int i = 0; i < nVMs; i++) {
        threadBuf[i] = jthread_allocate_thread();
        status = hythread_create_ex((hythread_t)threadBuf[i], NULL, 0, 0, NULL,
            vm_interrupt_entry_point, (void *)vmBuf[i]);
        assert(status == TM_ERROR_NONE);
    }

    // spawn a new thread which will terminate the process.
    status = hythread_create(NULL, 0, 0, 0,
        vm_interrupt_process, (void *)threadBuf);
    assert(status == TM_ERROR_NONE);

    // set a NULL terminator
    threadBuf[nVMs] = NULL;

cleanup:
    STD_FREE(vmBuf);
}

/**
 * Current process received an ??? signal (Ctrl+Break pressed).
 * Prints java stack traces for each VM running in the current process.
 */
void vm_dump_handler(int UNREF x) {
    int nVMs;
    vm_thread_t *threadBuf;

    jint status = JNI_GetCreatedJavaVMs(NULL, 0, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK)
        return;

    JavaVM ** vmBuf = (JavaVM **) STD_MALLOC(nVMs * sizeof(JavaVM *));
    status = JNI_GetCreatedJavaVMs(vmBuf, nVMs, &nVMs);
    assert(nVMs <= 1);
    if (status != JNI_OK) {
        goto cleanup;
    }

    threadBuf =
        (vm_thread_t*)STD_MALLOC((nVMs + 1) * sizeof(vm_thread_t));
    assert(threadBuf);

    // Create a new thread for each VM to avoid scalability and deadlock problems.
    IDATA UNUSED hy_status;
    for (int i = 0; i < nVMs; i++) {
        threadBuf[i] = jthread_allocate_thread();
        hy_status = hythread_create_ex((hythread_t)threadBuf[i],
            NULL, 0, 0, NULL, vm_dump_entry_point, (void *)vmBuf[i]);
        assert(hy_status == TM_ERROR_NONE);
    }

    // spawn a new thread which will release resources.
    hy_status = hythread_create(NULL, 0, 0, 0,
        vm_dump_process, (void *)threadBuf);
    assert(hy_status == TM_ERROR_NONE);

cleanup:
    STD_FREE(vmBuf);
}
<|endoftext|>