File: exception_aware_thread.h

package info (click to toggle)
kmc 3.2.4%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,716 kB
  • sloc: cpp: 38,308; python: 664; makefile: 216; perl: 179; sh: 34
file content (91 lines) | stat: -rw-r--r-- 2,462 bytes parent folder | download | duplicates (2)
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();
	}
};