99namespace 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 >
2646class 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