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