Line | Branch | Exec | Source |
---|---|---|---|
1 | /* GATE PROJECT LICENSE: | ||
2 | +----------------------------------------------------------------------------+ | ||
3 | | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> | | ||
4 | | All rights reserved. | | ||
5 | | | | ||
6 | | Redistribution and use in source and binary forms, with or without | | ||
7 | | modification, are permitted provided that the following conditions are met:| | ||
8 | | | | ||
9 | | 1. Redistributions of source code must retain the above copyright notice, | | ||
10 | | this list of conditions and the following disclaimer. | | ||
11 | | 2. Redistributions in binary form must reproduce the above copyright | | ||
12 | | notice, this list of conditions and the following disclaimer in the | | ||
13 | | documentation and/or other materials provided with the distribution. | | ||
14 | | | | ||
15 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"| | ||
16 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | ||
17 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ||
18 | | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | | ||
19 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | ||
20 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | ||
21 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | ||
22 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | ||
23 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | ||
24 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | | ||
25 | | THE POSSIBILITY OF SUCH DAMAGE. | | ||
26 | +----------------------------------------------------------------------------+ | ||
27 | */ | ||
28 | |||
29 | /** @file | ||
30 | * @brief Threads and Thread-related objects | ||
31 | * @ingroup gatecore_cpp | ||
32 | */ | ||
33 | |||
34 | |||
35 | #ifndef GATE_THREADING_HPP_INCLUDED | ||
36 | #define GATE_THREADING_HPP_INCLUDED | ||
37 | |||
38 | #include "gate/gate_core_api.hpp" | ||
39 | #include "gate/gatetypes.hpp" | ||
40 | #include "gate/exceptions.hpp" | ||
41 | #include "gate/memalloc.hpp" | ||
42 | #include "gate/threading.h" | ||
43 | #include "gate/wrappers.hpp" | ||
44 | #include "gate/runnables.hpp" | ||
45 | |||
46 | |||
47 | namespace gate | ||
48 | { | ||
49 | |||
50 | /// @brief | ||
51 | class GATE_CORE_CPP_API Thread : private NonCopyable | ||
52 | { | ||
53 | public: | ||
54 | typedef ::gate_thread_t handle_t; | ||
55 | typedef ::gate_thread_id_t id_t; | ||
56 | |||
57 | Thread(); | ||
58 | ~Thread() noexcept; | ||
59 | |||
60 | id_t start(gate_runnable_t* executor); | ||
61 | id_t start(IRunnable& code); | ||
62 | id_t start(gate_thread_native_entry entry_function, gate_thread_native_param_type entry_param); | ||
63 | |||
64 | Result<id_t> tryStart(gate_runnable_t* executor) noexcept; | ||
65 | Result<id_t> tryStart(IRunnable& code) noexcept; | ||
66 | Result<id_t> tryStart(gate_thread_native_entry entry_function, gate_thread_native_param_type entry_param) noexcept; | ||
67 | |||
68 | void release() noexcept; | ||
69 | result_t join(); | ||
70 | Result<result_t> tryJoin() noexcept; | ||
71 | |||
72 | private: | ||
73 | handle_t* ptrhandle; | ||
74 | handle_t handle; | ||
75 | |||
76 | id_t startDispatcher(IRunnableBuilder* methodRunnerPtr); | ||
77 | |||
78 | public: | ||
79 | static handle_t self(id_t* ptr_id = 0); | ||
80 | static void sleep(uint32_t milliseconds); | ||
81 | static void yield(); | ||
82 | static void yieldIfPreemptive(); | ||
83 | |||
84 | enum ModelEnum | ||
85 | { | ||
86 | Model_None = GATE_THREAD_MODEL_NONE, | ||
87 | Model_Manual = GATE_THREAD_MODEL_MANUAL, | ||
88 | Model_Native = GATE_THREAD_MODEL_NATIVE | ||
89 | }; | ||
90 | static ModelEnum supportedModel(); | ||
91 | |||
92 | public: /* type-generic thread starters */ | ||
93 | template<class F> | ||
94 | struct FunctorDispatcher | ||
95 | { | ||
96 | private: | ||
97 | F functor; | ||
98 | |||
99 | static result_t f_dispatcher(void* ptr_f) | ||
100 | { | ||
101 | F& ref_functor = *static_cast<F*>(ptr_f); | ||
102 | ref_functor(); | ||
103 | return results::Ok; | ||
104 | } | ||
105 | public: | ||
106 | typedef FunctorDispatcher<F> self_t; | ||
107 | |||
108 | FunctorDispatcher(F func) | ||
109 | : functor(func) | ||
110 | { | ||
111 | } | ||
112 | |||
113 | FunctorDispatcher(self_t const& src) | ||
114 | : functor(src.functor) | ||
115 | { | ||
116 | } | ||
117 | |||
118 | static gate_result_t dispatch_functor(void* data_ptr) | ||
119 | { | ||
120 | gate_result_t ret = GATE_RESULT_FAILED; | ||
121 | self_t* ptr_dispatcher = static_cast<self_t*>(data_ptr); | ||
122 | if (ptr_dispatcher) | ||
123 | { | ||
124 | gate_exception_info_t xinfo = gate::catchException(&f_dispatcher, &ptr_dispatcher->functor); | ||
125 | ret = xinfo.result_code; | ||
126 | delete ptr_dispatcher; | ||
127 | } | ||
128 | else | ||
129 | { | ||
130 | ret = GATE_RESULT_NULLPOINTER; | ||
131 | } | ||
132 | return ret; | ||
133 | } | ||
134 | }; | ||
135 | |||
136 | template<class F> | ||
137 | id_t startFunc(F functor) | ||
138 | { | ||
139 | id_t thread_id = id_t(); | ||
140 | typedef FunctorDispatcher<F> dispatcher_t; | ||
141 | Ptr<dispatcher_t> dispatcher(new dispatcher_t(functor)); | ||
142 | gate_runnable_t* ptr_runnable = gate_runnable_create( | ||
143 | &dispatcher_t::dispatch_functor, dispatcher.get(), | ||
144 | sizeof(dispatcher_t), &dispatcher_t::create, dispatcher_t::release); | ||
145 | |||
146 | if (NULL == ptr_runnable) | ||
147 | { | ||
148 | GATEXX_RAISE_ERROR(results::OutOfMemory); | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | Runnable runner(ptr_runnable); | ||
153 | thread_id = this->start(ptr_runnable); | ||
154 | // thread is started, otherwise an exception/error is thrown | ||
155 | |||
156 | gate_object_retain(ptr_runnable); // another reference is now assigned as a dispatcher parameter | ||
157 | dispatcher.release(); // the dispatcher was moved into the runnable, it can be released here | ||
158 | } | ||
159 | |||
160 | return thread_id; | ||
161 | } | ||
162 | |||
163 | template<class F, class A1> | ||
164 | 2 | id_t startFunc(F func, A1 a1) | |
165 | { | ||
166 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | Delegate1<A1> d(func); |
167 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
4 | return this->startDispatcher(new RunnableDispatcher1<A1>(d, a1)); |
168 | } | ||
169 | |||
170 | template<class F, class A1, class A2> | ||
171 | id_t startFunc(F func, A1 a1, A2 a2) | ||
172 | { | ||
173 | Delegate2<A1, A2> d(func); | ||
174 | return this->startDispatcher(new RunnableDispatcher2<A1, A2>(d, a1, a2)); | ||
175 | } | ||
176 | |||
177 | template<class F, class A1, class A2, class A3> | ||
178 | id_t startFunc(F func, A1 a1, A2 a2, A3 a3) | ||
179 | { | ||
180 | Delegate3<A1, A2, A3> d(func); | ||
181 | return this->startDispatcher(new RunnableDispatcher3<A1, A2, A3>(d, a1, a2, a3)); | ||
182 | } | ||
183 | |||
184 | template<class F, class A1, class A2, class A3, class A4> | ||
185 | id_t startFunc(F func, A1 a1, A2 a2, A3 a3, A4 a4) | ||
186 | { | ||
187 | Delegate4<A1, A2, A3, A4> d(func); | ||
188 | return this->startDispatcher(new RunnableDispatcher4<A1, A2, A3, A4>(d, a1, a2, a3, a4)); | ||
189 | } | ||
190 | |||
191 | |||
192 | |||
193 | template<class CLS> | ||
194 | id_t startMethod(CLS* obj, void(CLS::* method)(void)) | ||
195 | { | ||
196 | Delegate0 d(obj, method); | ||
197 | return this->startDispatcher(new RunnableDispatcher0(d)); | ||
198 | } | ||
199 | |||
200 | template<class CLS, class A1> | ||
201 | id_t startMethod(CLS* obj, void(CLS::* method)(A1), A1 a1) | ||
202 | { | ||
203 | Delegate1<A1> d(obj, method); | ||
204 | return this->startDispatcher(new RunnableDispatcher1<A1>(d, a1)); | ||
205 | } | ||
206 | |||
207 | template<class CLS, class A1, class A2> | ||
208 | id_t startMethod(CLS* obj, void(CLS::* method)(A1, A2), A1 a1, A2 a2) | ||
209 | { | ||
210 | Delegate2<A1, A2> d(obj, method); | ||
211 | return this->startDispatcher(new RunnableDispatcher2<A1, A2>(d, a1, a2)); | ||
212 | } | ||
213 | |||
214 | template<class CLS, class A1, class A2, class A3> | ||
215 | id_t startMethod(CLS* obj, void(CLS::* method)(A1, A2, A3), A1 a1, A2 a2, A3 a3) | ||
216 | { | ||
217 | Delegate3<A1, A2, A3> d(obj, method); | ||
218 | return this->startDispatcher(new RunnableDispatcher3<A1, A2, A3>(d, a1, a2, a3)); | ||
219 | } | ||
220 | |||
221 | template<class CLS, class A1, class A2, class A3, class A4> | ||
222 | id_t startMethod(CLS* obj, void(CLS::* method)(A1, A2, A3, A4), A1 a1, A2 a2, A3 a3, A4 a4) | ||
223 | { | ||
224 | Delegate4<A1, A2, A3, A4> d(obj, method); | ||
225 | return this->startDispatcher(new RunnableDispatcher4<A1, A2, A3, A4>(d, a1, a2, a3, a4)); | ||
226 | } | ||
227 | }; | ||
228 | |||
229 | |||
230 | /// @brief | ||
231 | class GATE_CORE_CPP_API ThreadStorage : private NonCopyable | ||
232 | { | ||
233 | public: | ||
234 | ThreadStorage(); | ||
235 | ~ThreadStorage() noexcept; | ||
236 | |||
237 | Result<void*> tryGet() const; | ||
238 | Result<Void> trySet(void* data); | ||
239 | void* get() const; | ||
240 | void set(void* data); | ||
241 | |||
242 | private: | ||
243 | gate_thread_storage_t impl; | ||
244 | }; | ||
245 | |||
246 | |||
247 | /// @brief | ||
248 | /// @tparam T | ||
249 | template<class T> | ||
250 | class ThreadVar : private NonCopyable | ||
251 | { | ||
252 | private: | ||
253 | ThreadStorage& thread_store; | ||
254 | |||
255 | void destroy_type(void* ptr_data) | ||
256 | { | ||
257 | if (ptr_data) | ||
258 | { | ||
259 | var_type_t* ptr = static_cast<var_type_t*>(ptr_data); | ||
260 | gate::destructType(ptr); | ||
261 | gate_mem_dealloc(ptr); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | public: | ||
266 | typedef T var_type_t; | ||
267 | |||
268 | ThreadVar(ThreadStorage& storage) | ||
269 | : thread_store(storage) | ||
270 | { | ||
271 | |||
272 | } | ||
273 | ~ThreadVar() noexcept | ||
274 | { | ||
275 | } | ||
276 | |||
277 | bool_t empty() const | ||
278 | { | ||
279 | void* data = this->thread_store.get(); | ||
280 | return data != NULL; | ||
281 | } | ||
282 | |||
283 | bool_t operator!() const | ||
284 | { | ||
285 | return this->empty(); | ||
286 | } | ||
287 | |||
288 | var_type_t* get() | ||
289 | { | ||
290 | void* data = this->thread_store.get(); | ||
291 | if (data) | ||
292 | { | ||
293 | return static_cast<var_type_t*>(data); | ||
294 | } | ||
295 | return NULL; | ||
296 | } | ||
297 | |||
298 | var_type_t const* get() const | ||
299 | { | ||
300 | void* data = this->thread_store.get(); | ||
301 | if (data) | ||
302 | { | ||
303 | return static_cast<var_type_t const*>(data); | ||
304 | } | ||
305 | return NULL; | ||
306 | } | ||
307 | |||
308 | var_type_t& operator*() | ||
309 | { | ||
310 | var_type_t* ptr = this->get(); | ||
311 | if (!ptr) | ||
312 | { | ||
313 | GATEXX_RAISE_ERROR(results::NullPointer); | ||
314 | } | ||
315 | return *ptr; | ||
316 | } | ||
317 | var_type_t const& operator*() const | ||
318 | { | ||
319 | var_type_t const* ptr = this->get(); | ||
320 | if (!ptr) | ||
321 | { | ||
322 | GATEXX_RAISE_ERROR(results::NullPointer); | ||
323 | } | ||
324 | return *ptr; | ||
325 | } | ||
326 | var_type_t* operator->() | ||
327 | { | ||
328 | var_type_t* ptr = this->get(); | ||
329 | if (!ptr) | ||
330 | { | ||
331 | GATEXX_RAISE_ERROR(results::NullPointer); | ||
332 | } | ||
333 | return ptr; | ||
334 | } | ||
335 | var_type_t const* operator->() const | ||
336 | { | ||
337 | var_type_t const* ptr = this->get(); | ||
338 | if (!ptr) | ||
339 | { | ||
340 | GATEXX_RAISE_ERROR(results::NullPointer); | ||
341 | } | ||
342 | return ptr; | ||
343 | } | ||
344 | |||
345 | |||
346 | void create() | ||
347 | { | ||
348 | var_type_t* ptr = gate_mem_alloc(sizeof(var_type_t)); | ||
349 | if (ptr == NULL) | ||
350 | { | ||
351 | GATEXX_RAISE_ERROR(results::OutOfMemory); | ||
352 | } | ||
353 | |||
354 | void* previous_data = this->thread_store.get(); | ||
355 | try | ||
356 | { | ||
357 | gate::constructType(ptr); | ||
358 | try | ||
359 | { | ||
360 | this->thread_store.set(ptr); | ||
361 | } | ||
362 | catch (...) | ||
363 | { | ||
364 | gate::destructType(ptr); | ||
365 | throw; | ||
366 | } | ||
367 | } | ||
368 | catch (...) | ||
369 | { | ||
370 | gate_mem_dealloc(ptr); | ||
371 | throw; | ||
372 | } | ||
373 | destroy_type(previous_data); | ||
374 | } | ||
375 | |||
376 | void create(var_type_t const& src) | ||
377 | { | ||
378 | var_type_t* ptr = gate_mem_alloc(sizeof(var_type_t)); | ||
379 | if (ptr == NULL) | ||
380 | { | ||
381 | GATEXX_RAISE_ERROR(results::OutOfMemory); | ||
382 | } | ||
383 | |||
384 | void* previous_data = this->thread_store.get(); | ||
385 | try | ||
386 | { | ||
387 | gate::copyConstructType(ptr, src); | ||
388 | try | ||
389 | { | ||
390 | this->thread_store.set(ptr); | ||
391 | } | ||
392 | catch (...) | ||
393 | { | ||
394 | gate::destructType(ptr); | ||
395 | throw; | ||
396 | } | ||
397 | } | ||
398 | catch (...) | ||
399 | { | ||
400 | gate_mem_dealloc(ptr); | ||
401 | throw; | ||
402 | } | ||
403 | destroy_type(previous_data); | ||
404 | } | ||
405 | |||
406 | void destroy() | ||
407 | { | ||
408 | void* data = this->thread_store.get(); | ||
409 | destroy_type(data); | ||
410 | this->thread_store.set(NULL); | ||
411 | } | ||
412 | |||
413 | }; | ||
414 | |||
415 | } // end of namespace gate | ||
416 | |||
417 | #endif | ||
418 |