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