libcryfs/src/cpp-utils/thread/ThreadSystem.cpp

87 lines
3.1 KiB
C++
Raw Normal View History

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;
}
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);
}
ThreadSystem::Handle ThreadSystem::start(function<bool()> loopIteration) {
boost::unique_lock<boost::mutex> lock(_mutex);
2017-10-08 14:15:11 +02:00
auto thread = _startThread(loopIteration);
_runningThreads.push_back(RunningThread{std::move(loopIteration), std::move(thread)});
2015-11-07 09:00:25 +01:00
return std::prev(_runningThreads.end());
}
void ThreadSystem::stop(Handle handle) {
boost::unique_lock<boost::mutex> lock(_mutex);
boost::thread thread = std::move(handle->thread);
thread.interrupt();
2015-11-07 09:00:25 +01:00
_runningThreads.erase(handle);
//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() {
_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);
}
_mutex.unlock(); // Was locked in the before-fork handler
2015-11-07 09:00:25 +01:00
}
boost::thread ThreadSystem::_startThread(function<bool()> loopIteration) {
2017-10-01 10:04:29 +02:00
return boost::thread([loopIteration = std::move(loopIteration)] {
ThreadSystem::_runThread(std::move(loopIteration));
});
2015-11-07 09:00:25 +01:00
}
void ThreadSystem::_runThread(function<bool()> loopIteration) {
2015-11-07 09:00:25 +01:00
try {
bool cont = true;
while(cont) {
2015-11-07 09:00:25 +01:00
boost::this_thread::interruption_point();
cont = loopIteration(); // This might also be interrupted.
2015-11-07 09:00:25 +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) {
2017-02-04 20:03:20 +01:00
LOG(ERROR, "LoopThread crashed: {}", e.what());
2015-11-07 09:00:25 +01:00
} catch (...) {
2017-02-04 20:03:20 +01:00
LOG(ERROR, "LoopThread crashed");
2015-11-07 09:00:25 +01:00
}
//TODO We should remove the thread from _runningThreads here, not in stop().
2015-11-07 09:00:25 +01:00
}
}