2015-11-07 09:00:25 +01:00
|
|
|
#include "ThreadSystem.h"
|
|
|
|
#include "../logging/logging.h"
|
|
|
|
|
|
|
|
using std::function;
|
|
|
|
using namespace cpputils::logging;
|
|
|
|
|
|
|
|
namespace cpputils {
|
|
|
|
|
|
|
|
ThreadSystem &ThreadSystem::singleton() {
|
|
|
|
static ThreadSystem system;
|
|
|
|
return system;
|
|
|
|
}
|
|
|
|
|
2015-11-07 09:11:12 +01:00
|
|
|
ThreadSystem::ThreadSystem(): _runningThreads(), _mutex() {
|
2015-11-07 09:00:25 +01:00
|
|
|
//Stopping the thread before fork() (and then also restarting it in the parent thread after fork()) is important,
|
|
|
|
//because as a running thread it might hold locks or condition variables that won't play well when forked.
|
|
|
|
pthread_atfork(&ThreadSystem::_onBeforeFork, &ThreadSystem::_onAfterFork, &ThreadSystem::_onAfterFork);
|
|
|
|
}
|
|
|
|
|
2015-11-12 22:07:20 +01:00
|
|
|
ThreadSystem::Handle ThreadSystem::start(function<bool()> loopIteration) {
|
2015-11-07 09:11:12 +01:00
|
|
|
boost::unique_lock<boost::mutex> lock(_mutex);
|
2015-11-07 09:00:25 +01:00
|
|
|
auto thread = _startThread(loopIteration);
|
|
|
|
_runningThreads.push_back(RunningThread{loopIteration, std::move(thread)});
|
|
|
|
return std::prev(_runningThreads.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadSystem::stop(Handle handle) {
|
2015-11-07 09:11:12 +01:00
|
|
|
boost::unique_lock<boost::mutex> lock(_mutex);
|
2015-11-07 21:21:54 +01:00
|
|
|
boost::thread thread = std::move(handle->thread);
|
|
|
|
thread.interrupt();
|
2015-11-07 09:00:25 +01:00
|
|
|
_runningThreads.erase(handle);
|
2015-11-07 21:21:54 +01:00
|
|
|
|
|
|
|
//It's fine if another thread gets the mutex while we still wait for the join. Joining doesn't change any internal state.
|
|
|
|
lock.unlock();
|
|
|
|
thread.join();
|
2015-11-07 09:00:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadSystem::_onBeforeFork() {
|
|
|
|
singleton()._stopAllThreadsForRestart();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadSystem::_onAfterFork() {
|
|
|
|
singleton()._restartAllThreads();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadSystem::_stopAllThreadsForRestart() {
|
2015-11-07 09:11:12 +01:00
|
|
|
_mutex.lock(); // Is unlocked in the after-fork handler. This way, the whole fork() is protected.
|
2015-11-07 09:00:25 +01:00
|
|
|
for (RunningThread &thread : _runningThreads) {
|
|
|
|
thread.thread.interrupt();
|
|
|
|
}
|
|
|
|
for (RunningThread &thread : _runningThreads) {
|
|
|
|
thread.thread.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadSystem::_restartAllThreads() {
|
|
|
|
for (RunningThread &thread : _runningThreads) {
|
|
|
|
thread.thread = _startThread(thread.loopIteration);
|
|
|
|
}
|
2015-11-07 09:11:12 +01:00
|
|
|
_mutex.unlock(); // Was locked in the before-fork handler
|
2015-11-07 09:00:25 +01:00
|
|
|
}
|
|
|
|
|
2015-11-12 22:07:20 +01:00
|
|
|
boost::thread ThreadSystem::_startThread(function<bool()> loopIteration) {
|
2015-11-07 09:00:25 +01:00
|
|
|
return boost::thread(std::bind(&ThreadSystem::_runThread, loopIteration));
|
|
|
|
}
|
|
|
|
|
2015-11-12 22:07:20 +01:00
|
|
|
void ThreadSystem::_runThread(function<bool()> loopIteration) {
|
2015-11-07 09:00:25 +01:00
|
|
|
try {
|
2015-11-12 22:07:20 +01:00
|
|
|
bool cont = true;
|
|
|
|
while(cont) {
|
2015-11-07 09:00:25 +01:00
|
|
|
boost::this_thread::interruption_point();
|
2015-11-12 22:07:20 +01:00
|
|
|
cont = loopIteration(); // This might also be interrupted.
|
2015-11-07 09:00:25 +01:00
|
|
|
}
|
2015-11-12 22:07:20 +01:00
|
|
|
//The thread is terminated gracefully.
|
2015-11-07 09:00:25 +01:00
|
|
|
} catch (const boost::thread_interrupted &e) {
|
|
|
|
//Do nothing, exit thread.
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
LOG(ERROR) << "LoopThread crashed: " << e.what();
|
|
|
|
} catch (...) {
|
|
|
|
LOG(ERROR) << "LoopThread crashed";
|
|
|
|
}
|
2015-11-12 22:07:20 +01:00
|
|
|
//TODO We should remove the thread from _runningThreads here, not in stop().
|
2015-11-07 09:00:25 +01:00
|
|
|
}
|
|
|
|
}
|