diff --git a/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h b/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h index 0af199cbb1..6a5120b8fa 100644 --- a/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h +++ b/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h @@ -1,249 +1,241 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITK_NON_BLOCKING_ALGORITHM_H_INCLUDED_DFARdfWN1tr #define MITK_NON_BLOCKING_ALGORITHM_H_INCLUDED_DFARdfWN1tr #include "MitkAlgorithmsExtExports.h" #include #include #include #include "mitkCommon.h" #include "mitkDataStorage.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include "mitkSmartPointerProperty.h" #include "mitkWeakPointer.h" #include "mitkImage.h" #include "mitkSurface.h" #include #include #include /// from itkNewMacro(), additionally calls Initialize(), because this couldn't be done from the constructor of /// NonBlockingAlgorithm /// (you can't call virtual functions from the constructor of the superclass) #define mitkAlgorithmNewMacro(classname) \ \ static Pointer \ New(void) \ { \ classname *rawPtr = new classname(); \ Pointer smartPtr = rawPtr; \ rawPtr->UnRegister(); \ rawPtr->Initialize(); \ return smartPtr; \ \ } \ \ virtual::itk::LightObject::Pointer \ CreateAnother(void) const override \ \ { \ Pointer smartPtr = classname::New(); \ ::itk::LightObject::Pointer lightPtr = smartPtr.GetPointer(); \ smartPtr->Initialize(this); \ return lightPtr; \ \ } namespace mitk { /*! Invokes ResultsAvailable with each new result done centralize use of itk::MultiThreader in this class @todo do the property-handling in this class @todo process "incoming" events in this class @todo sollen segmentierungs-dinger von mitk::ImageSource erben? Ivo fragen, wie das mit AllocateOutputs, etc. gehen soll eine ImageSourceAlgorithm koennte dann die noetigen Methoden wie GenerateData(), GetOutput() ueberschreiben, so dass von dort aus die Methoden von NonBlockingAlgorithm aufgerufen werden. Erben v.a. um die Output-Sachen zu uebernehmen, die Anpassungen das einfuehren einer Zwischenklasse, um die Interaces zu verheiraten. */ class MITKALGORITHMSEXT_EXPORT NonBlockingAlgorithm : public itk::Object { public: - // for threading - class MITKALGORITHMSEXT_EXPORT ThreadParameters - { - public: - itk::SmartPointer m_Algorithm; - }; - mitkClassMacroItkParent(NonBlockingAlgorithm, itk::Object); void SetDataStorage(DataStorage &storage); DataStorage *GetDataStorage(); // parameter setting /// For any kind of normal types template void SetParameter(const char *parameter, const T &value) { // MITK_INFO << "SetParameter(" << parameter << ") " << typeid(T).name() << std::endl; // m_ParameterListMutex->Lock(); m_Parameters->SetProperty(parameter, GenericProperty::New(value)); // m_ParameterListMutex->Unlock(); } /// For any kind of smart pointers template void SetPointerParameter(const char *parameter, const itk::SmartPointer &value) { // MITK_INFO << this << "->SetParameter smartpointer(" << parameter << ") " << typeid(itk::SmartPointer).name() // << std::endl; m_ParameterListMutex.lock(); m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value.GetPointer())); m_ParameterListMutex.unlock(); } // virtual void SetParameter( const char*, mitk::BaseProperty* ); // for "number of iterations", ... // create some property observing to inform algorithm object about changes // perhaps some TriggerParameter(string) macro that creates an observer for changes in a specific property like // "2ndPoint" for LineAlgorithms /// For any kind of BaseData, like Image, Surface, etc. Will be stored inside some SmartPointerProperty void SetPointerParameter(const char *parameter, BaseData *value); /// For any kind of ITK images (C pointers) template void SetItkImageAsMITKImagePointerParameter(const char *parameter, itk::Image *itkImage) { // MITK_INFO << "SetParameter ITK image(" << parameter << ") " << typeid(itk::Image).name() << std::endl; // create an MITK image for that mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage = ImportItkImage(itkImage); SetPointerParameter(parameter, mitkImage); } /// For any kind of ITK images (smartpointers) template void SetItkImageAsMITKImagePointerParameter(const char *parameter, const itk::SmartPointer> &itkImage) { // MITK_INFO << "SetParameter ITK image(" << parameter << ") " << typeid(itk::SmartPointer >).name() << std::endl; // create an MITK image for that mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage = ImportItkImage(itkImage); SetPointerParameter(parameter, mitkImage); } // parameter getting template void GetParameter(const char *parameter, T &value) const { // MITK_INFO << "GetParameter normal(" << parameter << ") " << typeid(T).name() << std::endl; // m_ParameterListMutex->Lock(); BaseProperty *p = m_Parameters->GetProperty(parameter); GenericProperty *gp = dynamic_cast *>(p); if (gp) { value = gp->GetValue(); // m_ParameterListMutex->Unlock(); return; } // m_ParameterListMutex->Unlock(); std::string error("There is no parameter \""); error += parameter; error += '"'; throw std::invalid_argument(error); } template void GetPointerParameter(const char *parameter, itk::SmartPointer &value) const { // MITK_INFO << this << "->GetParameter smartpointer(" << parameter << ") " << typeid(itk::SmartPointer).name() // << std::endl; // m_ParameterListMutex->Lock(); BaseProperty *p = m_Parameters->GetProperty(parameter); if (p) { SmartPointerProperty *spp = dynamic_cast(p); if (spp) { T *t = dynamic_cast(spp->GetSmartPointer().GetPointer()); value = t; // m_ParameterListMutex->Unlock(); return; } } // m_ParameterListMutex->Unlock(); std::string error("There is no parameter \""); error += parameter; error += '"'; throw std::invalid_argument(error); } // start/stop functions virtual void Reset(); void StartAlgorithm(); // for those who want to trigger calculations on their own // --> need for an OPTION: manual/automatic starting void StartBlockingAlgorithm(); // for those who want to trigger calculations on their own void StopAlgorithm(); void TriggerParameterModified(const itk::EventObject &); void ThreadedUpdateSuccessful(const itk::EventObject &); void ThreadedUpdateFailed(const itk::EventObject &); protected: NonBlockingAlgorithm(); // use smart pointers ~NonBlockingAlgorithm() override; void DefineTriggerParameter(const char *); void UnDefineTriggerParameter(const char *); virtual void Initialize(const NonBlockingAlgorithm *other = nullptr); virtual bool ReadyToRun(); virtual bool ThreadedUpdateFunction(); // will be called from a thread after calling StartAlgorithm virtual void ThreadedUpdateSuccessful(); // will be called after the ThreadedUpdateFunction() returned virtual void ThreadedUpdateFailed(); // will when ThreadedUpdateFunction() returns false PropertyList::Pointer m_Parameters; WeakPointer m_DataStorage; private: - static itk::ITK_THREAD_RETURN_TYPE StaticNonBlockingAlgorithmThread(ThreadParameters *param); + static void StaticNonBlockingAlgorithmThread(NonBlockingAlgorithm* algorithm); typedef std::map MapTypeStringUInt; MapTypeStringUInt m_TriggerPropertyConnections; std::mutex m_ParameterListMutex; int m_UpdateRequests; - ThreadParameters m_ThreadParameters; std::thread m_Thread; bool m_KillRequest; }; } // namespace #include "mitkNonBlockingAlgorithmEvents.h" #endif diff --git a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp index 7459b1e4ac..1ba1cfa8de 100644 --- a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp +++ b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp @@ -1,189 +1,177 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkNonBlockingAlgorithm.h" #include "mitkCallbackFromGUIThread.h" #include "mitkDataStorage.h" #include namespace mitk { NonBlockingAlgorithm::NonBlockingAlgorithm() : m_UpdateRequests(0), m_KillRequest(false) { m_Parameters = PropertyList::New(); } NonBlockingAlgorithm::~NonBlockingAlgorithm() { if (m_Thread.joinable()) - m_Thread.join(); + m_Thread.detach(); } void mitk::NonBlockingAlgorithm::SetDataStorage(DataStorage &storage) { m_DataStorage = &storage; } DataStorage *mitk::NonBlockingAlgorithm::GetDataStorage() { return m_DataStorage.Lock(); } void NonBlockingAlgorithm::Initialize(const NonBlockingAlgorithm *itkNotUsed(other)) { // define one input, one output basedata object // some basedata input - image, surface, whatever BaseData::Pointer input; SetPointerParameter("Input", input); // some basedata output BaseData::Pointer output; SetPointerParameter("Output", output); } void NonBlockingAlgorithm::SetPointerParameter(const char *parameter, BaseData *value) { m_ParameterListMutex.lock(); m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value)); m_ParameterListMutex.unlock(); } void NonBlockingAlgorithm::DefineTriggerParameter(const char *parameter) { BaseProperty *value = m_Parameters->GetProperty(parameter); if (value && m_TriggerPropertyConnections.find(parameter) == m_TriggerPropertyConnections.end()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &NonBlockingAlgorithm::TriggerParameterModified); m_TriggerPropertyConnections[parameter] = value->AddObserver(itk::ModifiedEvent(), command); } } void NonBlockingAlgorithm::UnDefineTriggerParameter(const char *parameter) { auto iter = m_TriggerPropertyConnections.find(parameter); if (iter != m_TriggerPropertyConnections.end()) { BaseProperty *value = m_Parameters->GetProperty(parameter); MITK_ERROR(!value) << "NonBlockingAlgorithm::UnDefineTriggerProperty() in bad state." << std::endl; ; value->RemoveObserver(m_TriggerPropertyConnections[parameter]); m_TriggerPropertyConnections.erase(iter); } } void NonBlockingAlgorithm::Reset() { Initialize(); } void NonBlockingAlgorithm::StartBlockingAlgorithm() { StartAlgorithm(); StopAlgorithm(); } void NonBlockingAlgorithm::StartAlgorithm() { if (!ReadyToRun()) return; // let algorithm check if all input/parameters are ok if (m_KillRequest) return; // someone wants us to die m_ParameterListMutex.lock(); - m_ThreadParameters.m_Algorithm = this; ++m_UpdateRequests; m_ParameterListMutex.unlock(); if (m_Thread.joinable()) // thread already running. But something obviously wants us to recalculate the output { return; // thread already running } // spawn a thread that calls ThreadedUpdateFunction(), and ThreadedUpdateFinished() on us - m_Thread = std::thread(StaticNonBlockingAlgorithmThread, &m_ThreadParameters); + this->Register(); + m_Thread = std::thread(StaticNonBlockingAlgorithmThread, this); } void NonBlockingAlgorithm::StopAlgorithm() { if (m_Thread.joinable()) m_Thread.join(); // waits for the thread to terminate on its own } - // a static function to call a member of NonBlockingAlgorithm from inside an ITK thread - itk::ITK_THREAD_RETURN_TYPE NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(ThreadParameters *param) + // a static function to call a member of NonBlockingAlgorithm from inside a thread + void NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(NonBlockingAlgorithm* algorithm) { - NonBlockingAlgorithm::Pointer algorithm = param->m_Algorithm; - // this UserData tells us, which BubbleTool's method to call - if (!algorithm) - { - return itk::ITK_THREAD_RETURN_DEFAULT_VALUE; - } - algorithm->m_ParameterListMutex.lock(); while (algorithm->m_UpdateRequests > 0) { algorithm->m_UpdateRequests = 0; algorithm->m_ParameterListMutex.unlock(); // actually call the methods that do the work if (algorithm->ThreadedUpdateFunction()) // returns a bool for success/failure { - itk::ReceptorMemberCommand::Pointer command = - itk::ReceptorMemberCommand::New(); + auto command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateSuccessful); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); - // algorithm->ThreadedUpdateSuccessful(); + } else { - itk::ReceptorMemberCommand::Pointer command = - itk::ReceptorMemberCommand::New(); + auto command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateFailed); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); - // algorithm->ThreadedUpdateFailed(); } algorithm->m_ParameterListMutex.lock(); } algorithm->m_ParameterListMutex.unlock(); - - return ITK_THREAD_RETURN_DEFAULT_VALUE; } void NonBlockingAlgorithm::TriggerParameterModified(const itk::EventObject &) { StartAlgorithm(); } bool NonBlockingAlgorithm::ReadyToRun() { return true; // default is always ready } bool NonBlockingAlgorithm::ThreadedUpdateFunction() { return true; } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateSuccessful(const itk::EventObject &) { ThreadedUpdateSuccessful(); - m_ThreadParameters.m_Algorithm = nullptr; } void NonBlockingAlgorithm::ThreadedUpdateSuccessful() { // notify observers that a result is ready InvokeEvent(ResultAvailable(this)); + this->UnRegister(); } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateFailed(const itk::EventObject &) { ThreadedUpdateFailed(); - m_ThreadParameters.m_Algorithm = nullptr; // delete } void NonBlockingAlgorithm::ThreadedUpdateFailed() { // notify observers that something went wrong InvokeEvent(ProcessingError(this)); + this->UnRegister(); } } // namespace