Starting from 2021-07-01, all LRZ GitLab users will be required to explicitly accept the GitLab Terms of Service. Please see the detailed information at https://doku.lrz.de/display/PUBLIC/GitLab and make sure that your projects conform to the requirements.

Commit 0314b02a authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Improved implementation of the Runnable interface:

* Added new common base class RunnableWithConditionalWait that directly includes the std::condition_variable
* RunnableWithConditionalWait::stop() notifies the condition variable repeatedly as long until the thread has stopped. This avoids program hangs at shutdown due to race conditions.
parent e5e3428c
......@@ -128,13 +128,6 @@ namespace campvis {
cgt::GlContextManager::getRef().releaseContext(_canvas, false);
}
void AbstractPipeline::stop() {
_stopExecution = true;
_evaluationCondition.notify_all();
Runnable::stop();
}
void AbstractPipeline::onPropertyChanged(const AbstractProperty* prop) {
if (prop == &_renderTargetID) {
setPipelineDirty();
......
......@@ -55,7 +55,7 @@ namespace campvis {
/**
* Abstract base class for CAMPVis Pipelines.
*/
class CAMPVIS_CORE_API AbstractPipeline : public HasPropertyCollection, public cgt::Runnable, public cgt::EventHandler, public cgt::EventListener {
class CAMPVIS_CORE_API AbstractPipeline : public HasPropertyCollection, public cgt::RunnableWithConditionalWait, public cgt::EventHandler, public cgt::EventListener {
public:
/**
* Creates a AbstractPipeline.
......@@ -121,9 +121,6 @@ namespace campvis {
*/
virtual void run();
/// \see Runnable::stop
virtual void stop();
/**
* Executes this pipeline.
* To be implemented in the subclass.
......@@ -235,7 +232,6 @@ namespace campvis {
DataNameProperty _renderTargetID; ///< ID of the render target image to be rendered to the canvas
private:
std::condition_variable _evaluationCondition; ///< conditional wait to be used when the pipeline currently does not need to be executed.
tbb::atomic<bool> _enabled; ///< flag whether this pipeline is currently enabled
tbb::atomic<bool> _pipelineDirty; ///< Flag whether this pipeline is dirty and executePipeline() needs to be called.
......
......@@ -26,30 +26,6 @@
* *
**********************************************************************/
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed 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.
//
// ================================================================================================
#include "opengljobprocessor.h"
#include <tbb/tick_count.h>
......@@ -97,13 +73,6 @@ namespace cgt {
_jobPool.recycle();
}
void OpenGLJobProcessor::stop() {
_stopExecution = true;
_evaluationCondition.notify_all();
Runnable::stop();
}
void OpenGLJobProcessor::run() {
cgtAssert(_context != nullptr, "You have to set the context first before calling OpenGLJobProcessor::run()!");
std::unique_lock<std::mutex> lock(*cgt::GlContextManager::getRef().getGlMutexForContext(_context));
......
......@@ -26,29 +26,6 @@
* *
**********************************************************************/
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed 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.
//
// ================================================================================================
#ifndef OPENGLJOBPROCESSOR_H__
#define OPENGLJOBPROCESSOR_H__
......@@ -79,7 +56,7 @@ namespace cgt {
* and acquired OpenGL context. You can execute OpenGL calls asynchroniously using enqueueJob()
* or synchronously using the ScopedSynchronousGlJobExecution guard.
*/
class CGT_API OpenGLJobProcessor : public cgt::Singleton<OpenGLJobProcessor>, public cgt::Runnable {
class CGT_API OpenGLJobProcessor : public cgt::Singleton<OpenGLJobProcessor>, public cgt::RunnableWithConditionalWait {
friend class cgt::Singleton<OpenGLJobProcessor>; ///< CRTP
friend class AbstractJob; ///< so the custom new/delete operator can access the memory pool
......@@ -117,9 +94,6 @@ namespace cgt {
* \return _context
*/
cgt::GLCanvas* getContext();
/// \see Runnable::stop
void stop();
/**
* Performs the job processing using conditional wait.
......@@ -165,7 +139,6 @@ namespace cgt {
tbb::atomic<bool> _performGarbageCollection; ///< Flag whether to perform garbage cxollection
tbb::atomic<int> _pause; ///< Counter of pause requests
std::condition_variable _evaluationCondition; ///< conditional wait to be used when there are currently no jobs to process
private:
typedef std::allocator<AbstractJob> pool_allocator_t;
......
......@@ -26,39 +26,14 @@
* *
**********************************************************************/
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed 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.
//
// ================================================================================================
#include "runnable.h"
namespace cgt {
namespace {
void invokeThread(Runnable* r) {
r->run();
}
void invokeThread(Runnable* r) {
r->run();
r->_running = false;
}
Runnable::Runnable()
: _stopExecution()
, _thread(0)
......@@ -75,15 +50,13 @@ namespace cgt {
}
void Runnable::stop() {
if (!_running || _thread == 0)
if (_thread == 0)
return;
_stopExecution = true;
try {
if (_thread->joinable())
_thread->join();
_running = false;
}
catch(std::exception& e) {
LERRORC("CAMPVis.core.tools.Runnable", "Caught exception during _thread.join: " << e.what());
......@@ -94,5 +67,25 @@ namespace cgt {
_thread = new std::thread(&invokeThread, this);
_running = true;
}
// ================================================================================================
RunnableWithConditionalWait::RunnableWithConditionalWait()
: Runnable()
{}
RunnableWithConditionalWait::~RunnableWithConditionalWait() {
}
void RunnableWithConditionalWait::stop() {
while (_running) {
_stopExecution = true;
_evaluationCondition.notify_all();
std::this_thread::yield();
}
Runnable::stop();
}
}
......@@ -26,30 +26,6 @@
* *
**********************************************************************/
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed 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.
//
// ================================================================================================
#ifndef RUNNABLE_H__
#define RUNNABLE_H__
......@@ -59,6 +35,8 @@
#include <tbb/atomic.h>
namespace cgt {
class RunnableWithConditionalWait;
/**
* Abstract base class for objects that shall run in a separate thread.
* Runnable object manage their own thread, which is created when calling start(). The new Thread starts
......@@ -66,6 +44,9 @@ namespace cgt {
* and waits for the thread to finish. Hence, you should test for _stopExecution in your run() method.
*/
class CGT_API Runnable {
friend void invokeThread(Runnable* r);
friend class RunnableWithConditionalWait;
public:
/**
* Creates a new Runnable object
......@@ -73,7 +54,7 @@ namespace cgt {
Runnable();
/**
* Destructor, stops and waits the thread if the thread is still running.
* Destructor, stops and waits for the thread to finish if the thread is still running.
*/
virtual ~Runnable();
......@@ -106,6 +87,37 @@ namespace cgt {
tbb::atomic<bool> _running;
};
/**
* Extension of the Runnable interface for threads that should use conditional wait to pause
* their work when there's nothing to do.
* This version adds a protected std::condition_variable to be used for conditional wait and
* further overloads the stop() method to cleanly halt the thread by repeatedly notifying
* the condition variable (as notifications may get lost due to race conditions).
*/
class CGT_API RunnableWithConditionalWait : public Runnable {
public:
/**
* Creates a new RunnableWithConditionalWait object
*/
RunnableWithConditionalWait();
/**
* Destructor, stops and waits for thread to finish if the thread is still running.
*/
virtual ~RunnableWithConditionalWait();
/**
* Sets the _stopExecution flag and waits for the thread to finish.
*/
virtual void stop();
protected:
/// conditional wait to be used when there are currently no jobs to process
std::condition_variable _evaluationCondition;
};
}
#endif // RUNNABLE_H__
......@@ -81,17 +81,11 @@ namespace sigslot {
}
else {
// there currently is no event in this queue -> go sleep
if (! _stopExecution)
_evaluationCondition.wait(lock);
_evaluationCondition.wait(lock);
}
}
}
void signal_manager::stop() {
_evaluationCondition.notify_all();
cgt::Runnable::stop();
}
bool signal_manager::isCurrentThreadSignalManagerThread() const {
return std::this_thread::get_id() == _this_thread_id;
}
......
......@@ -298,7 +298,7 @@ namespace sigslot {
*
* signal_manager can be considered as thread-safe.
*/
class SIGSLOT_API signal_manager : public cgt::Singleton<signal_manager>, public cgt::Runnable {
class SIGSLOT_API signal_manager : public cgt::Singleton<signal_manager>, public cgt::RunnableWithConditionalWait {
friend class cgt::Singleton<signal_manager>; ///< CRTP
friend class _signal_handle_base; ///< so the custom new/delete operator can access the memory pool
......@@ -345,8 +345,6 @@ namespace sigslot {
/// \see Runnable:run
virtual void run();
/// \see Runnable:stop
virtual void stop();
private:
/// Private constructor only for singleton
......@@ -360,9 +358,7 @@ namespace sigslot {
SignalHandlingMode _handlingMode; ///< Mode for handling signals
SignalQueue _signalQueue; ///< Queue for signals to be dispatched
std::condition_variable _evaluationCondition; ///< conditional wait to be used when there are currently no jobs to process
std::mutex _ecMutex; ///< Mutex for protecting _evaluationCondition
std::thread::id _this_thread_id;
typedef std::allocator<_signal_handle_base> pool_allocator_t;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment