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