1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
/*************************************************************************
* Copyright (C) 2006 by Janek Kozicki *
* cosurgi@berlios.de *
* *
* This program is free software; it is licensed under the terms of the *
* GNU General Public License v2 or later. See file LICENSE for details. *
*************************************************************************/
#pragma once
#include <lib/base/Logging.hpp>
#include <atomic>
#include <mutex>
namespace yade {
/*!
\brief ThreadRunner takes care of starting/stopping (executing) the
ThreadWorker in the separate thread.
It is achieved by either:
- one execution of { ThreadWorker::singleAction(); } in separate thread
- a loop { while(looping() ) ThreadWorker::singleAction(); } in separate thread
Lifetime of ThreadRunner is guaranteed to be longer or equal to
the lifetime of the separate thread of execution.
The ThreadRunner owner must make sure that ThreadWorker has longer or
equal lifetime than instance of ThreadRunner. Otherwise ThreadRunner
will try to execute a dead object, which will lead to crash.
Do not destroy immediately after call to singleAction(). Destructor can
kick in before a separate thread starts, which will lead to a crash.
User can explicitly ask the running thread to terminate execution. If
the thread supports it, it will terminate.
\note This code is reentrant. Simultaneous requests from other threads to
start/stop or perform singleAction() are expected.
So ThreadWorker(s) are running, while the user is interacting with the
UI frontend (doesn't matter whether the UI is graphical, ncurses or
any other).
*/
class ThreadWorker;
class ThreadRunner {
private:
ThreadWorker* m_thread_worker;
std::atomic_bool m_looping { false };
std::mutex m_callmutex;
std::mutex m_runmutex;
void run();
void call();
DECLARE_LOGGER;
public:
ThreadRunner() = delete;
ThreadRunner(ThreadWorker* c)
: m_thread_worker(c) {};
~ThreadRunner();
/// perform ThreadWorker::singleAction() in separate thread
void spawnSingleAction();
/// start doing singleAction() in a loop in separate thread
void start();
/// stop the loop (changes the flag checked by looping() )
void stop();
/// precondition for the loop started with start().
bool looping() const;
/// precondition for invoking destructor. (small step towards fixing problem with ThreadRunner::call())
//bool permissionToDestroy() const;
//! last exception thrown by the worker, if any
// TODO: https://www.boost.org/doc/libs/1_75_0/libs/exception/doc/tutorial_exception_ptr.html - Transporting of Exceptions Between Threads
std::runtime_error workerException { "placeholder ThreadWorker exception" };
//! if true, workerException is copy of the exception thrown by the worker
std::atomic_bool workerThrew { false };
};
} // namespace yade
|