1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstdint> 17 #include <span> 18 19 #include "FreeRTOS.h" 20 #include "pw_thread_freertos/config.h" 21 #include "task.h" 22 #if PW_THREAD_JOINING_ENABLED 23 #include "event_groups.h" 24 #endif // PW_THREAD_JOINING_ENABLED 25 26 namespace pw::thread { 27 28 class Thread; // Forward declare Thread which depends on Context. 29 30 } // namespace pw::thread 31 32 namespace pw::thread::freertos { 33 34 // FreeRTOS may be used for dynamic thread TCB and stack allocation, but 35 // because we need some additional context beyond that the concept of a 36 // thread's context is split into two halves: 37 // 38 // 1) Context which just contains the additional Context pw::thread::Thread 39 // requires. This is used for both static and dynamic thread allocations. 40 // 41 // 2) StaticContext which contains the TCB and a span to the stack which is 42 // used only for static allocations. 43 class Context { 44 public: 45 Context() = default; 46 Context(const Context&) = delete; 47 Context& operator=(const Context&) = delete; 48 49 private: 50 friend Thread; 51 task_handle()52 TaskHandle_t task_handle() const { return task_handle_; } set_task_handle(const TaskHandle_t task_handle)53 void set_task_handle(const TaskHandle_t task_handle) { 54 task_handle_ = task_handle; 55 } 56 57 using ThreadRoutine = void (*)(void* arg); set_thread_routine(ThreadRoutine entry,void * arg)58 void set_thread_routine(ThreadRoutine entry, void* arg) { 59 entry_ = entry; 60 arg_ = arg; 61 } 62 detached()63 bool detached() const { return detached_; } 64 void set_detached(bool value = true) { detached_ = value; } 65 thread_done()66 bool thread_done() const { return thread_done_; } 67 void set_thread_done(bool value = true) { thread_done_ = value; } 68 69 #if PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED dynamically_allocated()70 bool dynamically_allocated() const { return dynamically_allocated_; } set_dynamically_allocated()71 void set_dynamically_allocated() { dynamically_allocated_ = true; } 72 #endif // PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED 73 74 #if PW_THREAD_JOINING_ENABLED join_event_group()75 StaticEventGroup_t& join_event_group() { return event_group_; } 76 #endif // PW_THREAD_JOINING_ENABLED 77 78 static void RunThread(void* void_context_ptr); 79 static void TerminateThread(Context& context); 80 81 TaskHandle_t task_handle_ = nullptr; 82 ThreadRoutine entry_ = nullptr; 83 void* arg_ = nullptr; 84 #if PW_THREAD_JOINING_ENABLED 85 // Note that the FreeRTOS life cycle of this event group is managed together 86 // with the task life cycle, not this object's life cycle. 87 StaticEventGroup_t event_group_; 88 #endif // PW_THREAD_JOINING_ENABLED 89 bool detached_ = false; 90 bool dynamically_allocated_ = false; 91 bool thread_done_ = false; 92 }; 93 94 // Static thread context allocation including the TCB, an event group for 95 // joining if enabled, and an external statically allocated stack. 96 // 97 // Example usage: 98 // 99 // std::array<StackType_t, 42> example_thread_stack; 100 // pw::thread::freertos::Context example_thread_context(example_thread_stack); 101 // void StartExampleThread() { 102 // pw::thread::Thread( 103 // pw::thread::freertos::Options() 104 // .set_name("static_example_thread") 105 // .set_priority(kFooPriority) 106 // .set_static_context(example_thread_context), 107 // example_thread_function).detach(); 108 // } 109 class StaticContext : public Context { 110 public: StaticContext(std::span<StackType_t> stack_span)111 explicit StaticContext(std::span<StackType_t> stack_span) 112 : tcb_{}, stack_span_(stack_span) {} 113 114 private: 115 friend Thread; 116 tcb()117 StaticTask_t& tcb() { return tcb_; } stack()118 std::span<StackType_t> stack() { return stack_span_; } 119 120 StaticTask_t tcb_; 121 std::span<StackType_t> stack_span_; 122 }; 123 124 // Static thread context allocation including the stack along with the Context. 125 // 126 // Example usage: 127 // 128 // pw::thread::freertos::ContextWithStack<42> example_thread_context; 129 // void StartExampleThread() { 130 // pw::thread::Thread( 131 // pw::thread::freertos::Options() 132 // .set_name("static_example_thread") 133 // .set_priority(kFooPriority) 134 // .set_static_context(example_thread_context), 135 // example_thread_function).detach(); 136 // } 137 template <size_t kStackSizeWords = config::kDefaultStackSizeWords> 138 class StaticContextWithStack final : public StaticContext { 139 public: StaticContextWithStack()140 constexpr StaticContextWithStack() : StaticContext(stack_storage_) { 141 static_assert(kStackSizeWords >= config::kMinimumStackSizeWords); 142 } 143 144 private: 145 std::array<StackType_t, kStackSizeWords> stack_storage_; 146 }; 147 148 } // namespace pw::thread::freertos 149