9
9
namespace nbl ::system
10
10
{
11
11
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
+
12
31
// Usage:
13
32
/*
14
- * class MyThreadHandler : public IThreadHandler<SomeInternalStateType> { .... };
33
+ * class MyThreadHandler : public IThreadHandler<MyThreadHandler, SomeInternalStateType> { .... };
15
34
*
16
35
* MyThreadHandler handler;
17
36
* std::thread thread(&MyThreadHandler::thread, &handler);
@@ -20,187 +39,181 @@ namespace nbl::system
20
39
* //...
21
40
* handler.terminate(thread);
22
41
* After this handler can be safely destroyed.
42
+ *
23
43
* Every method playing around with object's state shared with the thread must begin with line: `auto raii_handler = createRAIIDisptachHandler();`!
24
44
*/
25
- template <typename CRTP, typename InternalStateType = void >
45
+ template <typename CRTP, typename InternalStateType = void >
26
46
class IThreadHandler
27
47
{
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
62
79
{
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
+ }
66
86
67
- private:
68
- lock_t lk;
69
- cvar_t & cv;
70
- };
87
+ private:
88
+ lock_t lk;
89
+ cvar_t & cv;
90
+ };
71
91
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); }
75
95
76
- // Required accessible methods of class being CRTP parameter:
96
+ // Required accessible methods of class being CRTP parameter:
77
97
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;
81
101
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);
85
105
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
88
108
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); }
91
111
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 ();
96
115
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
+ }
98
127
99
- if constexpr (has_internal_state )
128
+ void terminate ( )
100
129
{
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 ();
102
137
}
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 ()
104
148
{
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 ;
106
155
}
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 ()
137
158
{
138
- m_thread = std::thread (&IThreadHandler<CRTP, InternalStateType>::thread, this );
139
- return true ;
159
+ m_initComplete.wait (false );
140
160
}
141
- return false ;
142
- }
143
161
144
- void waitForInitComplete ()
145
- {
146
- m_initComplete. wait ( false );
147
- }
162
+ ~IThreadHandler ()
163
+ {
164
+ terminate ( );
165
+ }
148
166
149
- ~IThreadHandler ()
150
- {
151
- terminate ();
152
- }
167
+ protected:
168
+ void thread ()
169
+ {
170
+ CRTP* this_ = static_cast <CRTP*>( this );
153
171
154
- protected:
155
- void thread ()
156
- {
157
- CRTP* this_ = static_cast <CRTP*>(this );
172
+ init_impl ();
173
+ internal_state_t * state_ptr = getInternalStatePtr ();
158
174
159
- init_impl ();
160
- internal_state_t * state_ptr = getInternalStatePtr ();
175
+ auto lock = createLock ();
161
176
162
- auto lock = createLock ();
177
+ do {
178
+ m_cvar.wait (lock, [this ,this_] { return this_->wakeupPredicate () || this ->m_quit ; });
163
179
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);
166
193
167
- if (this_-> continuePredicate () && !m_quit )
194
+ if constexpr (has_exit::value )
168
195
{
169
196
if constexpr (has_internal_state)
170
197
{
171
- internal_state_t & internal_state = state_ptr[0 ];
172
- this_->work (lock, internal_state);
198
+ this_->exit (state_ptr);
173
199
}
174
200
else
175
201
{
176
- this_->work (lock );
202
+ this_->exit ( );
177
203
}
178
204
}
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
- }
191
205
}
192
- }
193
206
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 )];
196
209
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
201
214
202
- // Must be last member!
203
- std::thread m_thread;
215
+ // Must be last member!
216
+ std::thread m_thread;
204
217
};
205
218
206
219
}
0 commit comments