roo_threads
API Documentation for roo_threads
Loading...
Searching...
No Matches
thread.cpp
Go to the documentation of this file.
2
3#ifdef ROO_THREADS_USE_FREERTOS
4
5#include <assert.h>
6
7#include "freertos/FreeRTOS.h"
8#include "freertos/semphr.h"
9#include "freertos/task.h"
12
13namespace roo_threads {
14namespace freertos {
15
16thread::attributes::attributes()
19 joinable_(true),
20 name_("roo") {}
21
22namespace {
23
24struct thread_state {
25 thread::attributes attr;
26
27 std::unique_ptr<VirtualCallable> start = nullptr;
28 TaskHandle_t task;
29 StaticSemaphore_t join_barrier;
30 StaticSemaphore_t join_mutex;
31};
32
33} // namespace
34
35void thread::swap(thread& other) noexcept { std::swap(state_, other.state_); }
36
37bool thread::joinable() const noexcept {
38 thread_state* state = (thread_state*)state_;
39 return state != nullptr;
40}
41
42thread::~thread() { assert(!joinable()); }
43
44thread& thread::operator=(thread&& other) noexcept {
45 assert(!joinable());
46 swap(other);
47 return *this;
48}
49
50thread::id thread::get_id() const noexcept {
51 thread_state* state = (thread_state*)state_;
52 if (state_ == nullptr) {
53 return thread::id(nullptr);
54 }
55 return thread::id(state->task);
56}
57
58static void run_thread(void* arg) {
59 thread_state* p = (thread_state*)arg;
60 std::unique_ptr<VirtualCallable> start = std::move(p->start);
61 start->call();
62 start = nullptr;
63 if (p->attr.joinable()) {
64 xSemaphoreGive((SemaphoreHandle_t)&p->join_barrier);
65 vTaskSuspend(nullptr);
66 } else {
67 delete p;
68 vTaskDelete(nullptr);
69 }
70}
71
72void thread::start(const attributes& attributes,
73 std::unique_ptr<VirtualCallable> start) {
74 thread_state* state = new thread_state;
75 assert(state != nullptr);
76 state->attr = attributes;
77 state->start = std::move(start);
78 if (state->attr.joinable()) {
79 xSemaphoreCreateMutexStatic(&state->join_mutex);
80 xSemaphoreCreateBinaryStatic(&state->join_barrier);
81 }
82 uint32_t stack_size_freertos =
83 (uint32_t)(state->attr.stack_size() / sizeof(portSTACK_TYPE));
84 // Note: we expect xTaskCreate to make a memory barrier, so that if the task
85 // gets scheduled on a different core, it still sees fully initialized state.
86 if (xTaskCreate(run_thread, state->attr.name(), stack_size_freertos,
87 (void*)state, state->attr.priority(),
88 &state->task) != pdPASS) {
89 delete state;
90 assert(false);
91 }
92 state_ = state;
93}
94
95void thread::join() {
96 thread_state* state = (thread_state*)state_;
97 assert(state != nullptr); // Attempting to join a null thread
98 assert(state->attr.joinable()); // Attempting to join a non-joinable thread
99 if (xSemaphoreTake((SemaphoreHandle_t)&state->join_mutex, 0) != pdPASS) {
100 assert(false); // Another thread has already joined the requested thread
101 }
102 if (this_thread::get_id() == state->task) {
103 assert(false); // Thread attempting to join itself
104 }
105
106 // Wait for the joined thread to finish.
107 xSemaphoreTake((SemaphoreHandle_t)&state->join_barrier, portMAX_DELAY);
108 xSemaphoreGive((SemaphoreHandle_t)&state->join_barrier);
109 // At this point, the joined thread does nothing else but suspends itself. It
110 // is safe to delete resources.
111 vSemaphoreDelete((SemaphoreHandle_t)&state->join_barrier);
112 state_ = 0;
113
114 xSemaphoreGive((SemaphoreHandle_t)&state->join_mutex);
115 vSemaphoreDelete((SemaphoreHandle_t)&state->join_mutex);
116 vTaskDelete(state->task);
117
118 // Nothing else should be dereferencing state anymoore.
119 delete state;
120}
121
122namespace this_thread {
123
124thread::id get_id() noexcept { return thread::id(xTaskGetCurrentTaskHandle()); }
125
126void yield() noexcept { vPortYield(); }
127
128void sleep_for(const roo_time::Duration& duration) {
129 sleep_until(roo_time::Uptime::Now() + duration);
130}
131
132void sleep_until(const roo_time::Uptime& when) {
133 while (true) {
134 roo_time::Uptime now = roo_time::Uptime::Now();
135 if (when <= now) return;
136 roo_time::Duration delta = when - now;
137 vTaskDelay((delta.inMillisRoundedUp() + portTICK_PERIOD_MS - 1) /
138 portTICK_PERIOD_MS);
139 }
140}
141
142} // namespace this_thread
143
144} // namespace freertos
145} // namespace roo_threads
146
147#endif // ROO_THREADS_USE_FREERTOS
#define ROO_THREADS_FREERTOS_DEFAULT_THREAD_PRIORITY
Definition config.h:15
#define ROO_THREADS_FREERTOS_DEFAULT_THREAD_STACK_SIZE
Definition config.h:12
thread::id get_id() noexcept
Returns identifier of the current thread.
void yield() noexcept
Hints the scheduler to run another thread.
void sleep_until(const roo_time::Uptime &when)
Blocks the current thread until the specified time point.
void sleep_for(const roo_time::Duration &duration)
Blocks the current thread for at least the given duration.