µOS++ IIIe Reference 7.0.0
The third edition of µOS++, a POSIX inspired open source framework, written in C++
Loading...
Searching...
No Matches
thread_internal.h
Go to the documentation of this file.
1/*
2 * This file is part of the µOS++ distribution.
3 * (https://github.com/micro-os-plus)
4 * Copyright (c) 2016-2023 Liviu Ionescu. All rights reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software
7 * for any purpose is hereby granted, under the terms of the MIT license.
8 *
9 * If a copy of the license was not distributed with this file, it can
10 * be obtained from https://opensource.org/licenses/mit/.
11 */
12
13// ============================================================================
14// This file is for internal use in µOS++ and should not be included
15// in applications.
16/*
17 * (References are to ISO/IEC DIS 14882:2017)
18 *
19 * A trivially copyable class is a class:
20 * - where each copy constructor, move constructor, copy assignment
21 * operator, and move assignment operator (15.8, 16.5.3) is either
22 * deleted or trivial,
23 * - that has at least one non-deleted copy constructor, move
24 * constructor, copy assignment operator, or move assignment operator, and
25 * - that has a trivial, non-deleted destructor (15.4).
26 */
27
28// ----------------------------------------------------------------------------
29
30#pragma GCC diagnostic push
31#if defined(__clang__)
32#pragma clang diagnostic ignored "-Wc++98-compat"
33#endif
34
35// ----------------------------------------------------------------------------
36
40class thread
41{
42public:
43
44 using native_handle_type = os::rtos::thread*; // See 33.2.3
45
62 class id
63 {
64 public:
65 id () noexcept;
66
67 explicit
68 id (native_handle_type system_thread) noexcept;
69
70 id (const id&) = default;
71 id&
72 operator= (const id&) = default;
73
74 ~id () = default;
75
76 private:
77
78 friend class thread;
79 friend struct std::hash<thread::id>;
80
81 // Only two of them, the other 4 are defined in terms of these.
82 friend bool
83 operator== (thread::id x, thread::id y) noexcept;
84
85 friend bool
86 operator< (thread::id x, thread::id y) noexcept;
87
88 // The id is actually a pointer to the system thread.
89 native_handle_type native_thread_;
90 };
91
92 thread () noexcept = default;
93
94 template<typename F, //
95 typename ... Args>
96 explicit
97 thread (F&& f, Args&&... args);
98
99 ~thread ();
100
101 thread (const thread&) = delete;
102 thread (thread&& t) noexcept;
103
104 thread&
105 operator= (const thread&) = delete;
106 thread&
107 operator= (thread&& t) noexcept;
108
109 // ----------------------------------------------------------------------
110
111 void
112 swap (thread& t) noexcept;
113
114 bool
115 joinable (void) const noexcept;
116
117 void
118 join (void);
119
120 void
121 detach (void);
122
123 id
124 get_id (void) const noexcept;
125
126 native_handle_type
127 native_handle ();
128
129 static unsigned
130 hardware_concurrency (void) noexcept;
131
132private:
133
134 template<typename F_T>
135 static void
136 run_function_object (const void* func_object);
137
138 template<typename F_T>
139 static void
140 delete_function_object (const void* func_obj);
141
142 void
143 delete_system_thread (void);
144
145 // The current implementation creates temporary id() objects
146 // and copies (moves?) them here, but this is not a problem,
147 // the id is actually a pointer.
148 id id_;
149
150 using function_object_deleter_t = void (*) (void*);
151 function_object_deleter_t function_object_deleter_ = nullptr;
152
153public:
154
155};
156
157// Enforce the copyable requirement.
158static_assert(std::is_trivially_copyable<thread::id>::value,
159 "thread::id must be trivially copyable");
160
161// ========================================================================
162
163void
164swap (thread& x, thread& y) noexcept;
165
166/*
167 * Relational operators allow thread::id objects to be used as keys
168 * in associative containers.
169 */
170bool
171operator== (thread::id x, thread::id y) noexcept;
172bool
173operator!= (thread::id x, thread::id y) noexcept;
174bool
175operator< (thread::id x, thread::id y) noexcept;
176bool
177operator<= (thread::id x, thread::id y) noexcept;
178bool
179operator> (thread::id x, thread::id y) noexcept;
180bool
181operator>= (thread::id x, thread::id y) noexcept;
182
183#if 0
184template<class charT, class traits>
185basic_ostream<charT, traits>&
186operator<<(basic_ostream<charT, traits>& out, thread::id id);
187#endif
188
189// Hash support
190template<class T>
191 struct hash;
192
193template<>
194 struct hash<thread::id> ;
195
196// ========================================================================
202namespace this_thread
203{
204
208 thread::id
209 get_id () noexcept;
210
214 void
215 yield () noexcept;
216
223 template<typename Clock_T = os::estd::chrono::systick_clock, typename Rep_T,
224 typename Period_T>
225 constexpr void
226 sleep_for (const std::chrono::duration<Rep_T, Period_T>& rel_time);
227
232 template<typename Clock_T, typename Duration_T>
233 void
234 sleep_until (const std::chrono::time_point<Clock_T, Duration_T>& abs_time);
235
236} /* namespace this_thread */
237
238// ========================================================================
239// Inline & template implementations.
240
241// ========================================================================
242
243inline void
244swap (thread& x, thread& y) noexcept
245{
246 x.swap (y);
247}
248
249inline bool
250operator== (thread::id x, thread::id y) noexcept
251{
252 return x.native_thread_ == y.native_thread_;
253}
254
255inline bool
256operator!= (thread::id x, thread::id y) noexcept
257{
258 return !(x == y);
259}
260
261inline bool
262operator< (thread::id x, thread::id y) noexcept
263{
264 return x.native_thread_ < y.native_thread_;
265}
266
267inline bool
268operator<= (thread::id x, thread::id y) noexcept
269{
270 return !(y < x);
271}
272
273inline bool
274operator> (thread::id x, thread::id y) noexcept
275{
276 return y < x;
277}
278
279inline bool
280operator>= (thread::id x, thread::id y) noexcept
281{
282 return !(x < y);
283}
284
285// ========================================================================
286
287inline
288thread::id::id () noexcept :
289native_thread_ ( nullptr)
290 {
291 }
292
293inline
294thread::id::id (native_handle_type native_thread) noexcept :
295native_thread_ ( native_thread)
296 {
297 }
298
299// ------------------------------------------------------------------------
300
301inline thread::id
302thread::get_id () const noexcept
303{
304 return id_;
305}
306
307inline thread::native_handle_type
308thread::native_handle ()
309{
310 return id_.native_thread_;
311}
312
313inline unsigned
314thread::hardware_concurrency () noexcept
315{
316 return 1;
317}
318
319template<typename F_T>
320 void
321 thread::run_function_object (const void* func_obj)
322 {
323 os::trace::printf ("%s()\n", __PRETTY_FUNCTION__);
324
325 using Function_object = F_T;
326 const Function_object* f = static_cast<const Function_object*> (func_obj);
327 (*f) ();
328 }
329
330template<typename F_T>
331 void
332 thread::delete_function_object (const void* func_obj)
333 {
334 os::trace::printf ("%s()\n", __PRETTY_FUNCTION__);
335
336 using Function_object = F_T;
337 const Function_object* f = static_cast<const Function_object*> (func_obj);
338
339 // The delete now has the knowledge required to
340 // correctly delete the object (i.e. the object size).
341 delete f;
342 }
343
344#pragma GCC diagnostic push
345#if defined(__clang__)
346#elif defined(__GNUC__)
347#pragma GCC diagnostic ignored "-Waggregate-return"
348#endif
349
350template<typename Callable_T, typename ... Args_T>
351 thread::thread (Callable_T&& f, Args_T&&... args)
352 {
353 // static_assert(std::is_same<Attr_T, os::rtos::thread::attr_t>::value, "first param must be thread_attr_t*");
354
355 os::trace::printf ("%s() @%p\n", __PRETTY_FUNCTION__, this);
356
357 using Function_object = decltype(std::bind (std::forward<Callable_T> (f),
358 std::forward<Args_T>(args)...));
359
360 // Dynamic allocation! The size depends on the number of arguments.
361 // This creates a small problem, since both running the function
362 // and deleting the object requires the type. It is passes as
363 // template functions.
364 Function_object* funct_obj = new Function_object (
365 std::bind (std::forward<Callable_T> (f),
366 std::forward<Args_T>(args)...));
367
368 // The function to start the thread is a custom proxy that
369 // knows how to get the variadic arguments.
370#pragma GCC diagnostic push
371#if defined(__clang__)
372#pragma clang diagnostic ignored "-Wcast-function-type"
373#elif defined(__GNUC__)
374#pragma GCC diagnostic ignored "-Wcast-function-type"
375#endif
376 id_ = id
377 { new os::rtos::thread (
378 reinterpret_cast<os::rtos::thread::func_t> (&run_function_object<
379 Function_object> ),
380 reinterpret_cast<os::rtos::thread::func_args_t> (funct_obj)) };
381#pragma GCC diagnostic pop
382
383 // The deleter, to be used during destruction.
384 function_object_deleter_ =
385 reinterpret_cast<function_object_deleter_t> (&delete_function_object<
386 Function_object> );
387 }
388
389#pragma GCC diagnostic pop
390
391// ========================================================================
392
393namespace this_thread
394{
395
396 inline void
397 __attribute__((always_inline))
398 yield () noexcept
399 {
400 os::rtos::this_thread::yield ();
401 }
402
403#pragma GCC diagnostic push
404#if defined(__clang__)
405#elif defined(__GNUC__)
406#pragma GCC diagnostic ignored "-Waggregate-return"
407#endif
408
409 inline thread::id
410 get_id () noexcept
411 {
412 return thread::id (&os::rtos::this_thread::thread ());
413 }
414
415#pragma GCC diagnostic pop
416
417 // This implementation currently supports only short
418 // delays, since it uses the ticks timer.
419
420 // Note: there is no absolute guarantee that the
421 // sleep will not return earlier, so the application
422 // might need to retry.
423
424 // Note the constexpr return type, which tries to compute everything at
425 // compile time. And, for constant durations, it succeeds.
426#if 0
427 template<class Rep_T, class Period_T>
428 constexpr void
429 sleep_for (const std::chrono::duration<Rep_T, Period_T>& rel_time)
430 {
431 using namespace std::chrono;
432
433 if (rel_time > duration<Rep_T, Period_T>::zero ())
434 {
435 // Round up to micros, in case of nanos.
436 microseconds micros =
437 os::estd::chrono::ceil<microseconds> (rel_time);
438
439 // Round up to ticks.
440#if 0
441
442#pragma GCC diagnostic push
443#if defined(__clang__)
444#elif defined(__GNUC__)
445#pragma GCC diagnostic ignored "-Waggregate-return"
446#endif
447 os::rtos::thread::sleep (
448 (os::rtos::systicks_t) (os::estd::chrono::ceil<
449 systicks> (micros).count ()));
450#pragma GCC diagnostic pop
451
452#else
453 // The code seems better with this variant, otherwise it
454 // does not optimise constant calls.
455 os::rtos::Systick_clock::sleep_for (
456 os::rtos::Systick_clock::ticks_cast (
457 micros.count ()));
458#endif
459
460 }
461 }
462#endif
463
464#pragma GCC diagnostic push
465#if defined(__clang__)
466#elif defined(__GNUC__)
467#pragma GCC diagnostic ignored "-Waggregate-return"
468#endif
469
470 template<typename Clock_T, class Rep_T, class Period_T>
471 constexpr void
472 sleep_for (const std::chrono::duration<Rep_T, Period_T>& rel_time)
473 {
474 using namespace std::chrono;
475
476 using clock = Clock_T;
477 using sleep_rep = typename clock::sleep_rep;
478
479 if (rel_time > duration<Rep_T, Period_T>::zero ())
480 {
481 sleep_rep d = static_cast<sleep_rep> (os::estd::chrono::ceil<
482 typename clock::duration> (rel_time).count ());
483
484 clock::sleep_for (d);
485 }
486 }
487
488#pragma GCC diagnostic pop
489
490 template<typename Clock_T, typename Duration_T>
491 void
492 sleep_until (const std::chrono::time_point<Clock_T, Duration_T>& abs_time)
493 {
494 using clock = Clock_T;
495
496#pragma GCC diagnostic push
497#if defined(__clang__)
498#elif defined(__GNUC__)
499#pragma GCC diagnostic ignored "-Waggregate-return"
500#endif
501
502 auto now = clock::now ();
503
504 while (now < abs_time)
505 {
506 sleep_for (abs_time - now);
507 now = clock::now ();
508 }
509
510#pragma GCC diagnostic pop
511
512 }
513
514 template<typename Duration_T>
515 void
516 sleep_until (
517 const std::chrono::time_point<os::estd::chrono::realtime_clock,
518 Duration_T>& abs_time)
519 {
520 using clock = os::estd::chrono::realtime_clock;
521
522#pragma GCC diagnostic push
523#if defined(__clang__)
524#elif defined(__GNUC__)
525#pragma GCC diagnostic ignored "-Waggregate-return"
526#endif
527
528 auto now = clock::now ();
529 while (now < abs_time)
530 {
531 typename clock::sleep_rep d = (os::estd::chrono::ceil<
532 typename clock::sleep_duration> (abs_time - now)).count ();
533 clock::sleep_for (d);
534 now = clock::now ();
535 }
536
537#pragma GCC diagnostic pop
538
539 }
540
541 template<typename Duration_T>
542 void
543 sleep_until (
544 const std::chrono::time_point<os::estd::chrono::systick_clock,
545 Duration_T>& abs_time)
546 {
547 using clock = os::estd::chrono::systick_clock;
548
549#pragma GCC diagnostic push
550#if defined(__clang__)
551#elif defined(__GNUC__)
552#pragma GCC diagnostic ignored "-Waggregate-return"
553#endif
554
555 auto now = clock::now ();
556 while (now < abs_time)
557 {
558 typename clock::sleep_rep d = (os::estd::chrono::ceil<
559 typename clock::sleep_duration> (abs_time - now)).count ();
560 clock::sleep_for (d);
561 now = clock::now ();
562 }
563
564#pragma GCC diagnostic pop
565
566 }
567} /* namespace this_thread */
568
569#pragma GCC diagnostic pop
POSIX compliant thread, using the default RTOS allocator.
Definition os-thread.h:250
Thread unique id.
id() noexcept
id & operator=(const id &)=default
id(const id &)=default
~id()=default
Standard thread.