oveRTOS C++ API
C++20 RAII wrappers for the oveRTOS C API
Loading...
Searching...
No Matches
thread.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 *
6 * This file is part of oveRTOS.
7 */
8
14#pragma once
15
16#include <ove/thread.h>
17#include <ove/types.hpp>
18
19#ifndef CONFIG_OVE_ZERO_HEAP
20#include <memory> /* std::unique_ptr in the capturing trampoline */
21#endif
22
23namespace ove
24{
25
26/* ── Cooperative cancellation token ─────────────────────────────────── */
27
57{
58 public:
59 constexpr stop_token() noexcept = default;
60 constexpr explicit stop_token(ove_thread_t h) noexcept : handle_(h)
61 {
62 }
63
65 [[nodiscard]] bool stop_requested() const noexcept
66 {
67 return handle_ != nullptr && ove_thread_should_stop(handle_);
68 }
69
71 [[nodiscard]] bool stop_possible() const noexcept
72 {
73 return handle_ != nullptr;
74 }
75
82 explicit operator bool() const noexcept
83 {
84 return stop_requested();
85 }
86
88 [[nodiscard]] ove_thread_t handle() const noexcept
89 {
90 return handle_;
91 }
92
93 private:
94 ove_thread_t handle_ = nullptr;
95};
96
128{
129 public:
130 constexpr stop_source() noexcept = default;
131 constexpr explicit stop_source(ove_thread_t h) noexcept : handle_(h)
132 {
133 }
134
144 bool request_stop() noexcept
145 {
146 if (handle_ == nullptr)
147 return false;
148 const bool was_stopped = ove_thread_should_stop(handle_);
149 if (!was_stopped)
150 ove_thread_request_stop(handle_);
151 return !was_stopped;
152 }
153
155 [[nodiscard]] bool stop_requested() const noexcept
156 {
157 return handle_ != nullptr && ove_thread_should_stop(handle_);
158 }
159
161 [[nodiscard]] bool stop_possible() const noexcept
162 {
163 return handle_ != nullptr;
164 }
165
170 explicit operator bool() const noexcept
171 {
172 return stop_requested();
173 }
174
176 [[nodiscard]] stop_token get_token() const noexcept
177 {
178 return stop_token{handle_};
179 }
180
182 [[nodiscard]] ove_thread_t handle() const noexcept
183 {
184 return handle_;
185 }
186
187 friend constexpr bool operator==(const stop_source &,
188 const stop_source &) noexcept = default;
189
190 private:
191 ove_thread_t handle_ = nullptr;
192};
193
203template <typename F>
204concept CooperativeThreadEntry = std::convertible_to<F, void (*)(stop_token)>;
205
206#ifndef CONFIG_OVE_ZERO_HEAP
221template <typename F>
223 std::invocable<F &, stop_token> && !std::convertible_to<F, void (*)(stop_token)>;
224#endif
225
251{
252 public:
253 constexpr thread_id() noexcept = default;
254 constexpr explicit thread_id(ove_thread_t h) noexcept : handle_(h)
255 {
256 }
257
258 friend constexpr bool operator==(const thread_id &, const thread_id &) noexcept = default;
259 friend constexpr auto operator<=>(const thread_id &, const thread_id &) noexcept = default;
260
262 [[nodiscard]] ove_thread_t native_handle() const noexcept
263 {
264 return handle_;
265 }
266
267 private:
268 ove_thread_t handle_ = nullptr;
269};
270
271namespace detail
272{
273
274/* Trampoline bridging the substrate's `void(void*)` entry signature to
275 * the cooperative `void(stop_token)` user signature.
276 *
277 * The user's function pointer is passed verbatim through `ctx` via
278 * reinterpret_cast — guarded by a compile-time size check on every
279 * supported target (function pointer and data pointer share width on
280 * POSIX, ARM Cortex-M, and Xtensa).
281 *
282 * Token construction uses `ove_thread_get_self()` after a brief poll
283 * loop: on some backends (notably FreeRTOS) the task-tag lookup that
284 * powers `get_self` is populated by the spawning thread AFTER the new
285 * thread starts running, so the first call may return NULL. Yielding
286 * lets the spawner finish the tag publish. */
287inline void stop_token_trampoline(void *ctx)
288{
289 static_assert(sizeof(void *) == sizeof(void (*)(stop_token)),
290 "ove::stop_token trampoline requires function and data pointers "
291 "to share width — fix by adding a per-target trampoline shim");
292 auto user_fn = reinterpret_cast<void (*)(stop_token)>(ctx);
293 ove_thread_t self;
294 while ((self = ove_thread_get_self()) == nullptr) {
295 ove_thread_yield();
296 }
297 user_fn(stop_token{self});
298}
299
300#ifndef CONFIG_OVE_ZERO_HEAP
301/* Trampoline for the heap-mode capturing cooperative constructor.
302 *
303 * `ctx` is a `new`-allocated closure of type F that the constructor
304 * minted; the trampoline reclaims it via std::unique_ptr so the box
305 * is delete'd after the entry returns (or if the entry throws,
306 * though our binding requires noexcept user code in practice).
307 *
308 * One template instantiation per F — the closure's exact type is
309 * preserved, no type erasure or virtual dispatch. Each instantiation
310 * is small (typically 1-2 cache lines after LLVM inlining), so the
311 * code-size cost scales with the number of distinct capturing
312 * lambdas, not with the number of spawn sites. */
313template <typename F> inline void capturing_trampoline(void *ctx)
314{
315 std::unique_ptr<F> f{static_cast<F *>(ctx)};
316 ove_thread_t self;
317 while ((self = ove_thread_get_self()) == nullptr) {
318 ove_thread_yield();
319 }
320 (*f)(stop_token{self});
321}
322#endif
323
324} // namespace detail
325
342template <size_t StackSize = 0> class Thread
343{
344 public:
347 using id = thread_id;
348
364 template <typename F>
365 Thread(F entry, void *ctx, ove_prio_t prio, const char *name)
366 requires(StackSize > 0) && ThreadEntry<F>
367 {
368#ifdef CONFIG_OVE_ZERO_HEAP
369 static_assert(StackSize > 0, "StackSize must be > 0 in zero-heap mode");
370 int err = ove_thread_init(&handle_, &storage_, name, entry, ctx, prio, StackSize,
371 stack_);
372#else
373 int err = ove_thread_create(&handle_, name, entry, ctx, prio, StackSize);
374#endif
375 OVE_STATIC_INIT_ASSERT(err == OVE_OK);
376 }
377
404 template <typename F>
405 Thread(F entry, ove_prio_t prio, const char *name)
406 requires(StackSize > 0) && CooperativeThreadEntry<F>
407 {
408 void (*fn)(stop_token) = entry;
409 void *ctx = reinterpret_cast<void *>(fn);
410#ifdef CONFIG_OVE_ZERO_HEAP
411 static_assert(StackSize > 0, "StackSize must be > 0 in zero-heap mode");
412 int err = ove_thread_init(&handle_, &storage_, name, &detail::stop_token_trampoline,
413 ctx, prio, StackSize, stack_);
414#else
415 int err = ove_thread_create(&handle_, name, &detail::stop_token_trampoline, ctx,
416 prio, StackSize);
417#endif
418 OVE_STATIC_INIT_ASSERT(err == OVE_OK);
419 }
420
421#ifndef CONFIG_OVE_ZERO_HEAP
459 template <typename F>
460 Thread(F &&entry, ove_prio_t prio, const char *name)
461 requires(StackSize > 0) && CapturingCooperativeEntry<std::decay_t<F>>
462 {
463 using FT = std::decay_t<F>;
464 FT *boxed = new FT(std::forward<F>(entry));
465 int err = ove_thread_create(&handle_, name, &detail::capturing_trampoline<FT>,
466 boxed, prio, StackSize);
467 if (err != OVE_OK) {
468 delete boxed; /* trampoline never runs; reclaim the box */
469 OVE_STATIC_INIT_ASSERT(err == OVE_OK);
470 }
471 }
472#endif
473
484 ~Thread() noexcept
485 {
486 if (!handle_)
487 return;
488 ove_thread_request_stop(handle_);
489#ifdef CONFIG_OVE_ZERO_HEAP
490 ove_thread_deinit(handle_);
491#else
492 ove_thread_destroy(handle_);
493#endif
494 }
495
496 Thread(const Thread &) = delete;
497 Thread &operator=(const Thread &) = delete;
498
499#ifdef CONFIG_OVE_ZERO_HEAP
500 Thread(Thread &&) = delete;
501 Thread &operator=(Thread &&) = delete;
502#else
507 Thread(Thread &&other) noexcept : handle_(other.handle_)
508 {
509 other.handle_ = nullptr;
510 }
511
517 Thread &operator=(Thread &&other) noexcept
518 {
519 if (this != &other) {
520 if (handle_)
521 ove_thread_destroy(handle_);
522 handle_ = other.handle_;
523 other.handle_ = nullptr;
524 }
525 return *this;
526 }
527#endif
528
551 void join() noexcept
552 {
553 if (!handle_)
554 return;
555#ifdef CONFIG_OVE_ZERO_HEAP
556 ove_thread_deinit(handle_);
557#else
558 ove_thread_destroy(handle_);
559#endif
560 handle_ = nullptr;
561 }
562
563#ifdef CONFIG_OVE_ZERO_HEAP
575 void detach() = delete;
576#else
597 void detach() noexcept
598 {
599 handle_ = nullptr;
600 }
601#endif
602
607 void set_priority(ove_prio_t prio)
608 {
609 ove_thread_set_priority(handle_, prio);
610 }
611
617 void suspend()
618 {
619 ove_thread_suspend(handle_);
620 }
621
625 void resume()
626 {
627 ove_thread_resume(handle_);
628 }
629
634 ove_thread_state_t get_state() const
635 {
636 return ove_thread_get_state(handle_);
637 }
638
643 size_t get_stack_usage() const
644 {
645 return ove_thread_get_stack_usage(handle_);
646 }
647
656 {
657 struct ove_thread_stats stats {
658 };
659 const int rc = ove_thread_get_runtime_stats(handle_, &stats);
660 return from_rc(rc, stats);
661 }
662
676 void request_stop() noexcept
677 {
678 if (handle_)
679 ove_thread_request_stop(handle_);
680 }
681
689 [[nodiscard]] stop_token get_stop_token() const noexcept
690 {
691 return stop_token{handle_};
692 }
693
707 [[nodiscard]] stop_source get_stop_source() const noexcept
708 {
709 return stop_source{handle_};
710 }
711
713 [[nodiscard]] bool stop_requested() const noexcept
714 {
715 return handle_ != nullptr && ove_thread_should_stop(handle_);
716 }
717
722 bool valid() const
723 {
724 return handle_ != nullptr;
725 }
726
735 [[nodiscard]] bool joinable() const noexcept
736 {
737 return handle_ != nullptr;
738 }
739
746 [[nodiscard]] id get_id() const noexcept
747 {
748 return id{handle_};
749 }
750
755 ove_thread_t handle() const
756 {
757 return handle_;
758 }
759
760 private:
761 ove_thread_t handle_ = nullptr;
762#ifdef CONFIG_OVE_ZERO_HEAP
763 /* `stack_` precedes `storage_` so the latter (whose underlying C
764 * struct ends in a flexible-array `stack[]`) sits at the end of the
765 * C++ class. C99 allows FAMs anywhere in a struct, but C++ rejects
766 * another member after one — see
767 * backends/freertos/include/ove_storage_freertos.h FAM commentary. */
768 OVE_THREAD_STACK_MEMBER_(stack_, StackSize > 0 ? StackSize : 1);
769 ove_thread_storage_t storage_ = {};
770#endif
771};
772
773/* ── System memory statistics ──────────────────────────────────── */
774
779struct MemStats {
780 size_t total{};
781 size_t free{};
782 size_t used{};
783 size_t peak_used{};
784};
785
791[[nodiscard]] inline Result<MemStats> get_mem_stats() noexcept
792{
793 MemStats stats{};
794 struct ove_mem_stats ms {
795 };
796 const int rc = ove_sys_get_mem_stats(&ms);
797 if (rc == OVE_OK) {
798 stats.total = ms.total;
799 stats.free = ms.free;
800 stats.used = ms.used;
801 stats.peak_used = ms.peak_used;
802 }
803 return from_rc(rc, stats);
804}
805
806/* ── Thread enumeration ───────────────────────────────────────── */
807
823using ThreadInfo = struct ove_thread_info;
824
841[[nodiscard]] inline Result<size_t> thread_list(ThreadInfo *out, size_t max) noexcept
842{
843 size_t n = 0;
844 const int rc = ove_thread_list(out, max, &n);
845 return from_rc(rc, n);
846}
847
848} /* namespace ove */
849
859namespace ove::this_thread
860{
861
867inline void sleep_ms(uint32_t ms) noexcept
868{
869 ove_thread_sleep_ms(ms);
870}
871
880template <typename Rep, typename Period>
881inline void sleep_for(std::chrono::duration<Rep, Period> d) noexcept
882{
883 const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
884 ove_thread_sleep_ms(ms > 0 ? static_cast<uint32_t>(ms) : 0u);
885}
886
891inline void yield() noexcept
892{
893 ove_thread_yield();
894}
895
897inline ove_thread_t self() noexcept
898{
899 return ove_thread_get_self();
900}
901
903inline thread_id get_id() noexcept
904{
905 return thread_id{ove_thread_get_self()};
906}
907
908} /* namespace ove::this_thread */
RAII wrapper around an oveRTOS thread (task).
Definition thread.hpp:343
Result< struct ove_thread_stats > get_runtime_stats() const noexcept
Retrieves runtime statistics for the thread.
Definition thread.hpp:655
void set_priority(ove_prio_t prio)
Changes the priority of the thread at runtime.
Definition thread.hpp:607
id get_id() const noexcept
Get the id for this thread. std::thread::get_id analog.
Definition thread.hpp:746
Thread & operator=(Thread &&other) noexcept
Move-assignment operator — transfers ownership of the kernel handle.
Definition thread.hpp:517
Thread(F entry, void *ctx, ove_prio_t prio, const char *name)
Constructs and starts the thread.
Definition thread.hpp:365
bool joinable() const noexcept
std::thread::joinable analog — true if this wrapper is associated with an active kernel thread.
Definition thread.hpp:735
ove_thread_t handle() const
Returns the raw oveRTOS thread handle.
Definition thread.hpp:755
~Thread() noexcept
Destroys the thread wrapper, terminating and releasing the kernel thread.
Definition thread.hpp:484
void resume()
Resumes a previously suspended thread.
Definition thread.hpp:625
Thread(Thread &&other) noexcept
Move constructor — transfers ownership of the kernel handle.
Definition thread.hpp:507
bool valid() const
Returns true if the underlying kernel handle is non-null.
Definition thread.hpp:722
ove_thread_state_t get_state() const
Returns the current execution state of the thread.
Definition thread.hpp:634
void request_stop() noexcept
Requests the thread to stop cooperatively.
Definition thread.hpp:676
stop_source get_stop_source() const noexcept
Get a writable stop_source for this thread. Analog of std::jthread::get_stop_source.
Definition thread.hpp:707
size_t get_stack_usage() const
Returns the number of bytes used by the thread's stack so far.
Definition thread.hpp:643
stop_token get_stop_token() const noexcept
Get a token that observes this thread's cancellation flag.
Definition thread.hpp:689
void suspend()
Suspends execution of the thread.
Definition thread.hpp:617
bool stop_requested() const noexcept
Definition thread.hpp:713
void detach() noexcept
Release ownership of the kernel thread without waiting. Analog of std::thread::detach.
Definition thread.hpp:597
Thread(F entry, ove_prio_t prio, const char *name)
Cooperative-cancellation constructor — std::jthread analog.
Definition thread.hpp:405
void join() noexcept
Block the caller until the worker thread exits, then release the kernel handle. Analog of std::thread...
Definition thread.hpp:551
Thread(F &&entry, ove_prio_t prio, const char *name)
Cooperative-cancellation constructor for capturing entries. Heap-mode only. std::jthread-shaped.
Definition thread.hpp:460
Writable counterpart to stop_token. std::stop_source analog.
Definition thread.hpp:128
stop_token get_token() const noexcept
Definition thread.hpp:176
bool request_stop() noexcept
Set the stop flag on the associated thread.
Definition thread.hpp:144
bool stop_requested() const noexcept
Definition thread.hpp:155
ove_thread_t handle() const noexcept
Definition thread.hpp:182
bool stop_possible() const noexcept
Definition thread.hpp:161
Lightweight read-only handle to a thread's cooperative cancellation flag.
Definition thread.hpp:57
bool stop_requested() const noexcept
Definition thread.hpp:65
ove_thread_t handle() const noexcept
Definition thread.hpp:88
bool stop_possible() const noexcept
Definition thread.hpp:71
Opaque identity for an oveRTOS thread. std::thread::id analog.
Definition thread.hpp:251
ove_thread_t native_handle() const noexcept
Definition thread.hpp:262
Capturing callable invocable as void(stop_token).
Definition thread.hpp:222
Stateless callable invocable as void(stop_token).
Definition thread.hpp:204
Concept satisfied by any callable convertible to void(*)(void*).
Definition types.hpp:56
Free functions that operate on the currently running thread.
ove_thread_t self() noexcept
Definition thread.hpp:897
void yield() noexcept
Voluntarily relinquish the CPU to another ready thread of equal or higher priority....
Definition thread.hpp:891
thread_id get_id() noexcept
Definition thread.hpp:903
void sleep_ms(uint32_t ms) noexcept
Suspend the calling thread for at least ms milliseconds. Plain-millisecond analog of std::this_thread...
Definition thread.hpp:867
void sleep_for(std::chrono::duration< Rep, Period > d) noexcept
Suspend the calling thread for the given chrono duration. std::this_thread::sleep_for analog.
Definition thread.hpp:881
Top-level namespace for all oveRTOS C++ abstractions.
Definition app.hpp:20
Result< void > from_rc(int rc) noexcept
Lifts a substrate rc-code into a Result<void>.
Definition error.hpp:254
Result< MemStats > get_mem_stats() noexcept
Query system heap statistics.
Definition thread.hpp:791
Result< size_t > thread_list(ThreadInfo *out, size_t max) noexcept
List all threads in the system.
Definition thread.hpp:841
std::expected< T, Error > Result
std::expected-based result alias.
Definition error.hpp:139
ove_thread_info ThreadInfo
Snapshot of a single thread.
Definition thread.hpp:823
System heap statistics snapshot.
Definition thread.hpp:779
size_t used
Definition thread.hpp:782
size_t total
Definition thread.hpp:780
size_t peak_used
Definition thread.hpp:783
size_t free
Definition thread.hpp:781
Common type definitions and concepts for the C++ wrapper layer.