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