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 82 83 84 85 86 87 88 89 90 91
|
#pragma once
#include <thread>
#include <functional>
#include <cassert>
#include "thread_cancellation_exception.h"
class CThreadExceptionCollector
{
std::mutex mtx;
std::vector<std::exception_ptr> collected_exceptions;
public:
static CThreadExceptionCollector& Inst()
{
static CThreadExceptionCollector inst;
return inst;
}
void CollectException(std::exception_ptr&& exc_ptr)
{
std::lock_guard<std::mutex> lck(mtx);
collected_exceptions.push_back(std::move(exc_ptr));
}
void RethrowIfAnyException()
{
if (!collected_exceptions.empty())
{
auto first_exception = std::move(collected_exceptions.front());
collected_exceptions.clear();
std::rethrow_exception(first_exception);
}
}
};
class CExceptionAwareThread
{
struct Details
{
std::function<void()> fun; //TODO: it should be possible to implement without std::function, some information avaiable here: https://stackoverflow.com/questions/47496358/c-lambdas-how-to-capture-variadic-parameter-pack-from-the-upper-scope
std::thread thread;
template<typename Callable, typename... Args>
explicit Details(Callable&& f, Args&&... args) :
fun(std::bind(std::forward<Callable>(f), std::forward<Args>(args)...)),
thread([this] {
try
{
fun();
}
catch (const CThreadCancellationException& ex) //threads is exiting because it was cancelled by critical error in other thread
{
}
catch (...)
{
CThreadExceptionCollector::Inst().CollectException(std::current_exception());
}
})
{
}
};
//I'm wraping everythin into unique_ptr because if one creates a vector of CExceptionAwareThread, push_back may cause moves which may invalidate this pointer, which is needed to bound thread object with state
std::unique_ptr<Details> details;
public:
CExceptionAwareThread() = default;
CExceptionAwareThread(CExceptionAwareThread&& rhs) = default;
CExceptionAwareThread& operator=(CExceptionAwareThread&& rhs) = default;
CExceptionAwareThread(const CExceptionAwareThread& rhs) = delete;
CExceptionAwareThread& operator=(const CExceptionAwareThread& rhs) = delete;
template<typename Callable, typename... Args>
explicit CExceptionAwareThread(Callable&& f, Args&&... args) :
details(std::make_unique<Details>(std::forward<Callable>(f), std::forward<Args>(args)...))
{
}
void join()
{
details->thread.join();
}
~CExceptionAwareThread()
{
if(details)
if (details->thread.joinable())
details->thread.join();
}
};
|