Skip to content

Commit fe8b443

Browse files
fix a little bit of formatting and add some ideas
1 parent c035689 commit fe8b443

File tree

1 file changed

+155
-142
lines changed

1 file changed

+155
-142
lines changed

include/nbl/system/IThreadHandler.h

Lines changed: 155 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,28 @@
99
namespace nbl::system
1010
{
1111

12+
#if 0 // C++20 concepts suck at checking for non-public method existence!
13+
namespace impl
14+
{
15+
16+
// TODO: finish these concepts with `work`,`init` and `exit`
17+
template<typename T>
18+
concept ThreadHandler = requires(const T& cstate)
19+
{
20+
{cstate.continuePredicate()} -> std::same_as<bool>;
21+
{cstate.wakeupPredicate()} -> std::same_as<bool>;
22+
{cstate.work()} -> std::same_as<void>;
23+
};
24+
25+
template<typename T>
26+
concept StatefulThreadHandler
27+
28+
}
29+
#endif
30+
1231
// Usage:
1332
/*
14-
* class MyThreadHandler : public IThreadHandler<SomeInternalStateType> { .... };
33+
* class MyThreadHandler : public IThreadHandler<MyThreadHandler,SomeInternalStateType> { .... };
1534
*
1635
* MyThreadHandler handler;
1736
* std::thread thread(&MyThreadHandler::thread, &handler);
@@ -20,187 +39,181 @@ namespace nbl::system
2039
* //...
2140
* handler.terminate(thread);
2241
* After this handler can be safely destroyed.
42+
*
2343
* Every method playing around with object's state shared with the thread must begin with line: `auto raii_handler = createRAIIDisptachHandler();`!
2444
*/
25-
template <typename CRTP, typename InternalStateType = void>
45+
template<typename CRTP, typename InternalStateType = void>
2646
class IThreadHandler
2747
{
28-
private:
29-
// TODO: @AnastzIuk factor this out somewhere? `nbl/core/reflection` ?
30-
#define _NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER(member_func_name)\
31-
class has_##member_func_name\
32-
{\
33-
using true_type = uint32_t;\
34-
using false_type = uint64_t;\
35-
\
36-
template <typename T>\
37-
static true_type& test(decltype(&T::member_func_name));\
38-
template <typename T>\
39-
static false_type& test(...);\
40-
\
41-
public:\
42-
static inline constexpr bool value = (sizeof(test<CRTP>(0)) == sizeof(true_type));\
43-
};
44-
45-
_NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER(init)
46-
_NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER(exit)
47-
48-
#undef _NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER
49-
50-
protected:
51-
using mutex_t = std::mutex;
52-
using cvar_t = std::condition_variable;
53-
using lock_t = std::unique_lock<mutex_t>;
54-
55-
static inline constexpr bool has_internal_state = !std::is_void_v<InternalStateType>;
56-
using internal_state_t = std::conditional_t<has_internal_state, InternalStateType, int>;
57-
58-
struct raii_dispatch_handler_t
59-
{
60-
raii_dispatch_handler_t(mutex_t& _mtx, cvar_t& _cv) : lk(_mtx), cv(_cv) {}
61-
~raii_dispatch_handler_t()
48+
private:
49+
// TODO: factor out into `nbl/core/reflection/has_member_function.h`
50+
#define _NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER(member_func_name)\
51+
class has_##member_func_name\
52+
{\
53+
using true_type = uint32_t;\
54+
using false_type = uint64_t;\
55+
\
56+
template <typename T>\
57+
static true_type& test(decltype(&T::member_func_name));\
58+
template <typename T>\
59+
static false_type& test(...);\
60+
\
61+
public:\
62+
static inline constexpr bool value = (sizeof(test<CRTP>(0)) == sizeof(true_type));\
63+
};
64+
65+
_NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER(init)
66+
_NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER(exit)
67+
68+
#undef _NBL_IMPL_MEMBER_FUNC_PRESENCE_CHECKER
69+
70+
protected:
71+
using mutex_t = std::mutex;
72+
using cvar_t = std::condition_variable;
73+
using lock_t = std::unique_lock<mutex_t>;
74+
75+
static inline constexpr bool has_internal_state = !std::is_void_v<InternalStateType>;
76+
using internal_state_t = std::conditional_t<has_internal_state, InternalStateType, int>;
77+
78+
struct raii_dispatch_handler_t
6279
{
63-
cv.notify_one();
64-
// raii-style unlock happens in destructor of `lk` after notification
65-
}
80+
raii_dispatch_handler_t(mutex_t& _mtx, cvar_t& _cv) : lk(_mtx), cv(_cv) {}
81+
~raii_dispatch_handler_t()
82+
{
83+
cv.notify_one();
84+
// raii-style unlock happens in destructor of `lk` after notification
85+
}
6686

67-
private:
68-
lock_t lk;
69-
cvar_t& cv;
70-
};
87+
private:
88+
lock_t lk;
89+
cvar_t& cv;
90+
};
7191

72-
inline lock_t createLock() { return lock_t(m_mutex); }
73-
inline lock_t tryCreateLock() { return lock_t(m_mutex, std::try_to_lock); }
74-
inline raii_dispatch_handler_t createRAIIDispatchHandler() { return raii_dispatch_handler_t(m_mutex, m_cvar); }
92+
inline lock_t createLock() { return lock_t(m_mutex); }
93+
inline lock_t tryCreateLock() { return lock_t(m_mutex,std::try_to_lock); }
94+
inline raii_dispatch_handler_t createRAIIDispatchHandler() { return raii_dispatch_handler_t(m_mutex, m_cvar); }
7595

76-
// Required accessible methods of class being CRTP parameter:
96+
// Required accessible methods of class being CRTP parameter:
7797

78-
//void init(internal_state_t*); // required only in case of custom internal state, optional otherwise. Parameterless in case of no internal state
79-
//bool wakeupPredicate() const;
80-
//bool continuePredicate() const;
98+
//void init(internal_state_t*); // required only in case of custom internal state, optional otherwise. Parameterless in case of no internal state
99+
//bool wakeupPredicate() const;
100+
//bool continuePredicate() const;
81101

82-
// no `state` parameter in case of no internal state
83-
// lock is locked at the beginning of this function and must be locked at the exit
84-
//void work(lock_t& lock, internal_state_t& state);
102+
// no `state` parameter in case of no internal state
103+
// lock is locked at the beginning of this function and must be locked at the exit
104+
//void work(lock_t& lock, internal_state_t& state);
85105

86-
// lock is locked at the beginning of this function and must be locked at the exit
87-
//void exit(internal_state_t* state); // optional, no `state` parameter in case of no internal state
106+
// lock is locked at the beginning of this function and must be locked at the exit
107+
//void exit(internal_state_t* state); // optional, no `state` parameter in case of no internal state
88108

89-
private:
90-
internal_state_t* getInternalStatePtr() { return reinterpret_cast<internal_state_t*>(m_internal_state_storage); }
109+
private:
110+
internal_state_t* getInternalStatePtr() { return reinterpret_cast<internal_state_t*>(m_internal_state_storage); }
91111

92-
inline void init_impl()
93-
{
94-
//TODO!! temporarily commented (couldn't find the source)
95-
//static_assert(has_internal_state == has_init::value, "Custom internal state require implementation of init() method!");
112+
inline void init_impl()
113+
{
114+
internal_state_t* state_ptr = getInternalStatePtr();
96115

97-
internal_state_t* state_ptr = getInternalStatePtr();
116+
if constexpr (has_internal_state)
117+
{
118+
static_cast<CRTP*>(this)->init(state_ptr);
119+
}
120+
else if (has_init::value)
121+
{
122+
static_cast<CRTP*>(this)->init();
123+
}
124+
m_initComplete.test_and_set();
125+
m_initComplete.notify_one();
126+
}
98127

99-
if constexpr (has_internal_state)
128+
void terminate()
100129
{
101-
static_cast<CRTP*>(this)->init(state_ptr);
130+
{
131+
auto lockAndSignal = createRAIIDispatchHandler();
132+
m_quit = true;
133+
}
134+
135+
if (m_thread.joinable())
136+
m_thread.join();
102137
}
103-
else if (has_init::value)
138+
139+
public:
140+
struct start_on_construction_t {};
141+
constexpr inline static start_on_construction_t start_on_construction {};
142+
143+
IThreadHandler() : m_thread() {}
144+
IThreadHandler(start_on_construction_t) : m_thread(&IThreadHandler<CRTP,InternalStateType>::thread,this) {}
145+
146+
//! Has no effect if thread is already running
147+
bool start()
104148
{
105-
static_cast<CRTP*>(this)->init();
149+
if (m_thread.get_id() == std::thread::id())
150+
{
151+
m_thread = std::thread(&IThreadHandler<CRTP,InternalStateType>::thread,this);
152+
return true;
153+
}
154+
return false;
106155
}
107-
m_initComplete.test_and_set();
108-
m_initComplete.notify_one();
109-
}
110-
111-
void terminate()
112-
{
113-
auto lock = createLock();
114-
m_quit = true;
115-
m_cvar.notify_one();
116-
lock.unlock();
117-
118-
if (m_thread.joinable())
119-
m_thread.join();
120-
}
121-
122-
public:
123-
struct start_on_construction_t {};
124-
constexpr inline static start_on_construction_t start_on_construction {};
125-
126-
IThreadHandler() : m_thread() {}
127-
IThreadHandler(start_on_construction_t) :
128-
m_thread(&IThreadHandler<CRTP, InternalStateType>::thread, this)
129-
{
130-
131-
}
132-
133-
//! Has no effect if thread is already running
134-
bool start()
135-
{
136-
if (m_thread.get_id() == std::thread::id())
156+
157+
void waitForInitComplete()
137158
{
138-
m_thread = std::thread(&IThreadHandler<CRTP, InternalStateType>::thread, this);
139-
return true;
159+
m_initComplete.wait(false);
140160
}
141-
return false;
142-
}
143161

144-
void waitForInitComplete()
145-
{
146-
m_initComplete.wait(false);
147-
}
162+
~IThreadHandler()
163+
{
164+
terminate();
165+
}
148166

149-
~IThreadHandler()
150-
{
151-
terminate();
152-
}
167+
protected:
168+
void thread()
169+
{
170+
CRTP* this_ = static_cast<CRTP*>(this);
153171

154-
protected:
155-
void thread()
156-
{
157-
CRTP* this_ = static_cast<CRTP*>(this);
172+
init_impl();
173+
internal_state_t* state_ptr = getInternalStatePtr();
158174

159-
init_impl();
160-
internal_state_t* state_ptr = getInternalStatePtr();
175+
auto lock = createLock();
161176

162-
auto lock = createLock();
177+
do {
178+
m_cvar.wait(lock, [this,this_] { return this_->wakeupPredicate() || this->m_quit; });
163179

164-
do {
165-
m_cvar.wait(lock, [this,this_] { return this_->wakeupPredicate() || this->m_quit; });
180+
if (this_->continuePredicate() && !m_quit)
181+
{
182+
if constexpr (has_internal_state)
183+
{
184+
internal_state_t& internal_state = state_ptr[0];
185+
this_->work(lock, internal_state);
186+
}
187+
else
188+
{
189+
this_->work(lock);
190+
}
191+
}
192+
} while (!m_quit);
166193

167-
if (this_->continuePredicate() && !m_quit)
194+
if constexpr (has_exit::value)
168195
{
169196
if constexpr (has_internal_state)
170197
{
171-
internal_state_t& internal_state = state_ptr[0];
172-
this_->work(lock, internal_state);
198+
this_->exit(state_ptr);
173199
}
174200
else
175201
{
176-
this_->work(lock);
202+
this_->exit();
177203
}
178204
}
179-
} while (!m_quit);
180-
181-
if constexpr (has_exit::value)
182-
{
183-
if constexpr (has_internal_state)
184-
{
185-
this_->exit(state_ptr);
186-
}
187-
else
188-
{
189-
this_->exit();
190-
}
191205
}
192-
}
193206

194-
protected:
195-
alignas(internal_state_t) uint8_t m_internal_state_storage[sizeof(internal_state_t)];
207+
protected:
208+
alignas(internal_state_t) uint8_t m_internal_state_storage[sizeof(internal_state_t)];
196209

197-
mutex_t m_mutex;
198-
cvar_t m_cvar;
199-
std::atomic_flag m_initComplete; // begins in false state, per C++11 spec
200-
bool m_quit = false; // TODO: make this an atomic_flag
210+
mutex_t m_mutex;
211+
cvar_t m_cvar;
212+
std::atomic_flag m_initComplete; // begins in false state, per C++11 spec
213+
bool m_quit = false; // TODO: make this an atomic_flag
201214

202-
// Must be last member!
203-
std::thread m_thread;
215+
// Must be last member!
216+
std::thread m_thread;
204217
};
205218

206219
}

0 commit comments

Comments
 (0)