zpp
Zephyr C++20 Framework
thread.hpp
Go to the documentation of this file.
1 //
2 // Copyright (c) 2019 Erwin Rol <erwin@erwinrol.com>
3 //
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 
7 #ifndef ZPP_INCLUDE_ZPP_THREAD_HPP
8 #define ZPP_INCLUDE_ZPP_THREAD_HPP
9 
10 #include <zpp/thread_prio.hpp>
11 #include <zpp/thread_id.hpp>
12 #include <zpp/thread_attr.hpp>
13 #include <zpp/thread_data.hpp>
14 #include <zpp/thread_stack.hpp>
15 #include <zpp/clock.hpp>
16 #include <zpp/result.hpp>
17 #include <zpp/error_code.hpp>
18 #include <zpp/heap.hpp>
19 
20 #include <zephyr/kernel.h>
21 #include <zephyr/sys/__assert.h>
22 #include <zephyr/sys/util.h>
23 
24 #include <functional>
25 #include <chrono>
26 #include <tuple>
27 #include <utility>
28 #include <memory>
29 
30 namespace zpp {
31 
35 namespace this_thread {
36 
42 inline auto get_id() noexcept
43 {
44  return ::zpp::thread_id(k_current_get());
45 }
46 
50 inline void yield() noexcept
51 {
52  k_yield();
53 }
54 
60 template<class T_Rep, class T_Period>
61 inline void
62 busy_wait_for(const std::chrono::duration<T_Rep, T_Period>& wait_duration)
63 {
64  using namespace std::chrono;
65 
66  microseconds us = duration_cast<microseconds>(wait_duration);
67  k_busy_wait(us.count());
68 }
69 
75 template<class T_Rep, class T_Period >
76 inline auto
77 sleep_for(const std::chrono::duration<T_Rep, T_Period>& sleep_duration)
78 {
79  auto res = k_sleep(to_timeout(sleep_duration));
80 
81  return std::chrono::milliseconds(res);
82 }
83 
89 template<class T_Clock, class T_Duration>
90 inline void
91 sleep_until( const std::chrono::time_point<T_Clock, T_Duration>& sleep_time)
92 {
93  using namespace std::chrono;
94 
95  T_Duration dt;
96  while ( (dt = sleep_time - T_Clock::now()) > T_Duration::zero()) {
97  k_sleep(to_timeout(dt));
98  }
99 }
100 
104 inline void abort() noexcept
105 {
106  k_thread_abort(k_current_get());
107 }
108 
112 inline void suspend() noexcept
113 {
114  k_thread_suspend(k_current_get());
115 }
116 
122 inline thread_prio get_priority() noexcept
123 {
124  return thread_prio( k_thread_priority_get(k_current_get()) );
125 }
126 
132 inline void set_priority(thread_prio prio) noexcept
133 {
134  k_thread_priority_set(k_current_get(), prio.native_value());
135 }
136 
137 } // namespace this_thread
138 
139 template <class T> typename std::decay<T>::type decay_copy(T&& v) noexcept
140 {
141  return std::forward<T>(v);
142 }
143 
147 class thread {
148 private:
149  template<typename T_CallInfo>
150  static void callback_helper(void* a1, void* a2, void* a3) noexcept
151  {
152  (void)a2;
153  (void)a3;
154 
155  auto cip = reinterpret_cast<T_CallInfo*>(a1);
156  __ASSERT_NO_MSG(cip != nullptr);
157 
158  auto f = std::move(cip->m_f);
159  auto args = std::move(cip->m_args);
160 
161  auto heap = cip->m_heap;
162 
163  std::destroy_at(cip);
164  heap->deallocate(cip);
165 
166  std::apply(f, std::move(args));
167  }
168 
169  template<typename T_Callback, typename T_CallbackArg>
170  static void callback_helper(void* a1, void* a2, void* a3) noexcept
171  {
172  (void)a3;
173 
174  auto f = reinterpret_cast<T_Callback>(a1);
175  __ASSERT_NO_MSG(f != nullptr);
176 
177  auto arg = reinterpret_cast<T_CallbackArg*>(&a2);
178  __ASSERT_NO_MSG(arg != nullptr);
179 
180  std::invoke(*f, *arg);
181  }
182 
183  template<typename T_Callback>
184  static void callback_helper_void(void* a1, void* a2, void* a3) noexcept
185  {
186  (void)a2;
187  (void)a3;
188 
189  auto f = reinterpret_cast<T_Callback>(a1);
190  __ASSERT_NO_MSG(f != nullptr);
191 
192  std::invoke(*f);
193  }
194 
195 public:
199  constexpr thread() noexcept
200  {
201  }
202 
208  constexpr explicit thread(thread_id tid) noexcept
209  : m_tid(tid)
210  {
211  }
212 
221  template<typename T_Heap, typename T_Callback, typename... T_CallbackArgs,
222  std::enable_if_t<std::is_nothrow_invocable_v<T_Callback, T_CallbackArgs...>, bool> = true
223  >
224  constexpr thread(
225  thread_data& td,
226  thread_stack&& tstack,
227  const thread_attr& attr,
228  T_Heap* heap,
229  T_Callback&& f,
230  T_CallbackArgs&&... args) noexcept
231  {
232  typedef typename std::decay<T_Heap>::type CallInfoHeap;
233  typedef typename std::decay<T_Callback>::type CallInfoF;
234  typedef std::tuple<typename std::decay<T_CallbackArgs>::type...> CallInfoArgs;
235 
236  static_assert(std::is_invocable_v<T_Callback, T_CallbackArgs...>);
237  static_assert(std::is_nothrow_invocable_v<T_Callback, T_CallbackArgs...>);
238 
239  struct call_info {
240  CallInfoHeap* m_heap;
241  CallInfoF m_f;
242  CallInfoArgs m_args;
243  };
244 
245  void* vp = heap->try_allocate(sizeof(call_info), alignof(call_info));
246 
247  if (vp != nullptr) {
248 
249  auto cip = std::construct_at(reinterpret_cast<call_info*>(vp),
250  heap,
251  decay_copy(std::forward<T_Callback>(f)),
252  decay_copy(std::forward_as_tuple(args...)));
253 
254  __ASSERT_NO_MSG(cip != nullptr);
255 
256  auto tid = k_thread_create(
257  td.native_handle(),
258  tstack.data(),
259  tstack.size(),
260  &callback_helper<call_info>,
261  reinterpret_cast<void*>(cip),
262  nullptr,
263  nullptr,
264  attr.native_prio(),
265  attr.native_options(),
266  attr.native_delay());
267 
268  m_tid = thread_id(tid);
269  }
270  }
271 
272 
281  template<typename T_Callback, typename T_CallbackArg,
282  std::enable_if_t<std::is_nothrow_invocable_v<T_Callback, T_CallbackArg>, bool> = true
283  >
284  constexpr thread(
285  thread_data& td,
286  thread_stack&& tstack,
287  const thread_attr& attr,
288  T_Callback f,
289  T_CallbackArg arg) noexcept
290  {
291  using func_t = void (*)(T_CallbackArg) noexcept;
292 
293  static_assert(sizeof(T_CallbackArg) <= sizeof(void*));
294  static_assert(std::is_invocable_v<T_Callback, T_CallbackArg>);
295  static_assert(std::is_nothrow_invocable_v<T_Callback, T_CallbackArg>);
296  static_assert(std::is_invocable_v<func_t, T_CallbackArg>);
297  static_assert(std::is_nothrow_invocable_v<func_t, T_CallbackArg>);
298  static_assert(sizeof(func_t) <= sizeof(void*));
299  static_assert(alignof(T_CallbackArg) <= alignof(void*));
300  static_assert(std::is_trivial_v<T_CallbackArg>);
301 
302  func_t fp = f;
303 
304  void* arg_vp{};
305  std::construct_at(reinterpret_cast<T_CallbackArg*>(&arg_vp), arg);
306 
307  auto tid = k_thread_create(
308  td.native_handle(),
309  tstack.data(),
310  tstack.size(),
311  &callback_helper<func_t, T_CallbackArg>,
312  reinterpret_cast<void*>(fp),
313  arg_vp,
314  nullptr,
315  attr.native_prio(),
316  attr.native_options(),
317  attr.native_delay());
318 
319  m_tid = thread_id(tid);
320  }
321 
322 
331  template<typename T_Callback,
332  std::enable_if_t<std::is_nothrow_invocable_v<T_Callback>, bool> = true
333  >
334  constexpr thread(
335  thread_data& td,
336  thread_stack&& tstack,
337  const thread_attr& attr,
338  T_Callback f) noexcept
339  {
340  using func_t = void (*)() noexcept;
341 
342  static_assert(std::is_invocable_v<T_Callback>);
343  static_assert(std::is_nothrow_invocable_v<T_Callback>);
344  static_assert(std::is_invocable_v<func_t>);
345  static_assert(std::is_nothrow_invocable_v<func_t>);
346  static_assert(sizeof(func_t) <= sizeof(void*));
347 
348  func_t fp = f;
349 
350  auto tid = k_thread_create(
351  td.native_handle(),
352  tstack.data(),
353  tstack.size(),
354  &callback_helper_void<func_t>,
355  reinterpret_cast<void*>(fp),
356  nullptr,
357  nullptr,
358  attr.native_prio(),
359  attr.native_options(),
360  attr.native_delay());
361 
362  m_tid = thread_id(tid);
363  }
364 
365 
372  constexpr thread(thread&& other) noexcept
373  : m_tid(other.m_tid)
374  {
375  other.m_tid = thread_id::any();
376  }
377 
384  constexpr thread& operator=(thread&& other) noexcept
385  {
386  m_tid = other.m_tid;
387  other.m_tid = thread_id::any();
388 
389  return *this;
390  }
391 
395  ~thread() noexcept
396  {
397  if (m_tid) {
398  k_thread_abort(m_tid.native_handle());
399  }
400  }
401 
407  constexpr explicit operator bool() const noexcept {
408  return !!m_tid;
409  }
410 
414  constexpr void detach() noexcept
415  {
416  m_tid = thread_id::any();
417  }
418 
422  [[nodiscard]] auto wakeup() noexcept
423  {
425 
426  if (m_tid) {
427  k_wakeup(m_tid.native_handle());
428  res.assign_value();
429  }
430 
431  return res;
432  }
433 
437  [[nodiscard]] auto start() noexcept
438  {
440 
441  if (m_tid) {
442  k_thread_start(m_tid.native_handle());
443  res.assign_value();
444  }
445 
446  return res;
447  }
448 
452  [[nodiscard]] auto abort() noexcept
453  {
455 
456  if (m_tid) {
457  k_thread_abort(m_tid.native_handle());
458  m_tid = thread_id::any();
459  res.assign_value();
460  }
461 
462  return res;
463  }
464 
468  [[nodiscard]] auto resume() noexcept
469  {
471 
472  if (m_tid) {
473  k_thread_resume(m_tid.native_handle());
474  res.assign_value();
475  }
476 
477  return res;
478  }
479 
483  [[nodiscard]] auto join() noexcept
484  {
486 
487  if (m_tid) {
488  auto rc = k_thread_join(m_tid.native_handle(), K_FOREVER);
489  if (rc == 0) {
490  res.assign_value();
491  } else {
492  res.assign_error(to_error_code(-rc));
493  }
494  }
495 
496  return res;
497  }
498 
502  [[nodiscard]] auto suspend() noexcept
503  {
505 
506  if (m_tid) {
507  k_thread_suspend(m_tid.native_handle());
508  res.assign_value();
509  }
510 
511  return res;
512  }
513 
519  [[nodiscard]] auto priority() noexcept
520  {
522 
523  if (m_tid) {
524  res = thread_prio( k_thread_priority_get(m_tid.native_handle()) );
525  }
526 
527  return res;
528  }
529 
535  [[nodiscard]] auto set_priority(thread_prio prio) const noexcept
536  {
538 
539  if (m_tid) {
540  k_thread_priority_set(m_tid.native_handle(), prio.native_value());
541  res.assign_value();
542  }
543 
544  return res;
545  }
546 
552  [[nodiscard]] auto set_name(const char* name) noexcept
553  {
555 
556  if (m_tid) {
557  auto rc = k_thread_name_set(m_tid.native_handle(), name);
558  if (rc == 0) {
559  res.assign_value();
560  } else {
561  res.assign_error(to_error_code(-rc));
562  }
563  }
564 
565  return res;
566  }
567 
573  [[nodiscard]] auto name() const noexcept
574  {
576 
577  if (m_tid) {
578  auto rc = k_thread_name_get(m_tid.native_handle());
579  if (rc == nullptr) {
580  res.assign_error(error_code::k_notsup); // TODO
581  } else {
582  res.assign_value(rc);
583  }
584  }
585 
586  return res;
587  }
588 private:
589  thread_id m_tid;
590 public:
591  thread(const thread&) = delete;
592  thread& operator=(const thread&) = delete;
593 };
594 
595 } // namespace zpp
596 
597 #endif // ZPP_INCLUDE_ZPP_THREAD_HPP
void * try_allocate(size_t bytes) noexcept
Allocate memory from this heap without waiting.
Definition: heap.hpp:69
void deallocate(void *mem) noexcept
Deallocate memory previously allocated.
Definition: heap.hpp:124
helper class for error result
Definition: result.hpp:25
heap class
Definition: heap.hpp:159
result class
Definition: result.hpp:89
void assign_value(const T_Ok &v) noexcept
Definition: result.hpp:197
void assign_error(const T_Error &e) noexcept
Definition: result.hpp:219
Thread creation attributes.
Definition: thread_attr.hpp:35
thread_data holds the stack and thread control block memory
Definition: thread_data.hpp:19
Thead ID.
Definition: thread_id.hpp:18
constexpr static thread_id any() noexcept
Create an ID with value K_ANY.
Definition: thread_id.hpp:57
Thread priority.
Definition: thread_prio.hpp:18
thread_stack holds the stack and thread control block memory
The class thread repecents a single Zephyr thread.
Definition: thread.hpp:147
auto set_priority(thread_prio prio) const noexcept
Set priority of the thread this object mamages.
Definition: thread.hpp:535
auto suspend() noexcept
suspend the thread this object mamages.
Definition: thread.hpp:502
constexpr thread(thread_data &td, thread_stack &&tstack, const thread_attr &attr, T_Callback f, T_CallbackArg arg) noexcept
Creates a object which represents a new Zephyr thread.
Definition: thread.hpp:284
constexpr thread(thread_data &td, thread_stack &&tstack, const thread_attr &attr, T_Callback f) noexcept
Creates a object which represents a new Zephyr thread.
Definition: thread.hpp:334
auto name() const noexcept
Get name of the thread this object mamages.
Definition: thread.hpp:573
constexpr thread(thread &&other) noexcept
Move constructor.
Definition: thread.hpp:372
auto join() noexcept
join the thread this object mamages.
Definition: thread.hpp:483
constexpr thread(thread_data &td, thread_stack &&tstack, const thread_attr &attr, T_Heap *heap, T_Callback &&f, T_CallbackArgs &&... args) noexcept
Creates a object which represents a new Zephyr thread.
Definition: thread.hpp:224
constexpr thread() noexcept
Creates a object which doesn't represent a Zephyr thread.
Definition: thread.hpp:199
auto priority() noexcept
Get priority of the thread this object mamages.
Definition: thread.hpp:519
auto start() noexcept
start the thread this object mamages.
Definition: thread.hpp:437
auto wakeup() noexcept
wakeup the thread this object mamages.
Definition: thread.hpp:422
auto abort() noexcept
abort the thread this object mamages.
Definition: thread.hpp:452
constexpr thread(thread_id tid) noexcept
Creates a object which represents Zephyr thread with tid.
Definition: thread.hpp:208
auto set_name(const char *name) noexcept
Set name of the thread this object mamages.
Definition: thread.hpp:552
thread & operator=(const thread &)=delete
auto resume() noexcept
resume the thread this object mamages.
Definition: thread.hpp:468
constexpr void detach() noexcept
Detach this object from the thread.
Definition: thread.hpp:414
~thread() noexcept
Destructor, will abort the thread.
Definition: thread.hpp:395
constexpr thread & operator=(thread &&other) noexcept
Move assignment operator.
Definition: thread.hpp:384
thread(const thread &)=delete
void sleep_until(const std::chrono::time_point< T_Clock, T_Duration > &sleep_time)
Suspend the current thread until a specified time point.
Definition: thread.hpp:91
void set_priority(thread_prio prio) noexcept
Set the current thread's priority.
Definition: thread.hpp:132
auto get_id() noexcept
Get the thread ID of the current thread.
Definition: thread.hpp:42
void suspend() noexcept
Suspend the current thread.
Definition: thread.hpp:112
auto sleep_for(const std::chrono::duration< T_Rep, T_Period > &sleep_duration)
Suspend the current thread for a specified time duration.
Definition: thread.hpp:77
void abort() noexcept
Abort the current thread.
Definition: thread.hpp:104
void yield() noexcept
Yield the current thread.
Definition: thread.hpp:50
thread_prio get_priority() noexcept
Get the current thread's priority.
Definition: thread.hpp:122
void busy_wait_for(const std::chrono::duration< T_Rep, T_Period > &wait_duration)
Busy wait for a specified time duration.
Definition: thread.hpp:62
constexpr error_code to_error_code(int v) noexcept
Definition: error_code.hpp:102
constexpr k_timeout_t to_timeout(const std::chrono::duration< T_Rep, T_Period > &d) noexcept
convert a duration to tick
Definition: clock.hpp:88
std::decay< T >::type decay_copy(T &&v) noexcept
Definition: thread.hpp:139
@ k_notsup
Unsupported value.
@ k_inval
Invalid argument.