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 18 #include "FreeRTOS.h" 19 #include "pw_function/function.h" 20 #include "pw_span/span.h" 21 #include "pw_thread_freertos/config.h" 22 #include "task.h" 23 #if PW_THREAD_JOINING_ENABLED 24 #include "event_groups.h" 25 #endif // PW_THREAD_JOINING_ENABLED 26 27 namespace pw::thread { 28 29 class Thread; // Forward declare Thread which depends on Context. 30 31 } // namespace pw::thread 32 33 namespace pw::thread::freertos { 34 35 class Options; 36 37 // FreeRTOS may be used for dynamic thread TCB and stack allocation, but 38 // because we need some additional context beyond that the concept of a 39 // thread's context is split into two halves: 40 // 41 // 1) Context which just contains the additional Context pw::Thread requires. 42 // This is used for both static and dynamic thread allocations. 43 // 44 // 2) StaticContext which contains the TCB and a span to the stack which is 45 // used only for static allocations. 46 class Context { 47 public: 48 Context() = default; 49 Context(const Context&) = delete; 50 Context& operator=(const Context&) = delete; 51 52 private: 53 friend Thread; 54 static void CreateThread(const freertos::Options& options, 55 Function<void()>&& thread_fn, 56 Context*& native_type_out); 57 void AddToEventGroup(); 58 task_handle()59 TaskHandle_t task_handle() const { return task_handle_; } set_task_handle(const TaskHandle_t task_handle)60 void set_task_handle(const TaskHandle_t task_handle) { 61 task_handle_ = task_handle; 62 } 63 set_thread_routine(Function<void ()> && rvalue)64 void set_thread_routine(Function<void()>&& rvalue) { 65 fn_ = std::move(rvalue); 66 } 67 detached()68 bool detached() const { return detached_; } 69 void set_detached(bool value = true) { detached_ = value; } 70 thread_done()71 bool thread_done() const { return thread_done_; } 72 void set_thread_done(bool value = true) { thread_done_ = value; } 73 74 #if PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED dynamically_allocated()75 bool dynamically_allocated() const { return dynamically_allocated_; } set_dynamically_allocated()76 void set_dynamically_allocated() { dynamically_allocated_ = true; } 77 #endif // PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED 78 79 #if PW_THREAD_JOINING_ENABLED join_event_group()80 StaticEventGroup_t& join_event_group() { return event_group_; } 81 #endif // PW_THREAD_JOINING_ENABLED 82 83 static void ThreadEntryPoint(void* void_context_ptr); 84 static void TerminateThread(Context& context); 85 86 TaskHandle_t task_handle_ = nullptr; 87 Function<void()> fn_; 88 #if PW_THREAD_JOINING_ENABLED 89 // Note that the FreeRTOS life cycle of this event group is managed together 90 // with the task life cycle, not this object's life cycle. 91 StaticEventGroup_t event_group_; 92 #endif // PW_THREAD_JOINING_ENABLED 93 bool detached_ = false; 94 bool dynamically_allocated_ = false; 95 bool thread_done_ = false; 96 }; 97 98 // Static thread context allocation including the TCB, an event group for 99 // joining if enabled, and an external statically allocated stack. 100 // 101 // Example usage: 102 // 103 // std::array<StackType_t, kFooStackSizeWords> example_thread_stack; 104 // pw::thread::freertos::Context example_thread_context(example_thread_stack); 105 // void StartExampleThread() { 106 // pw::Thread( 107 // pw::thread::freertos::Options() 108 // .set_name("static_example_thread") 109 // .set_priority(kFooPriority) 110 // .set_static_context(example_thread_context), 111 // example_thread_function).detach(); 112 // } 113 class StaticContext : public Context { 114 public: StaticContext(span<StackType_t> stack_span)115 explicit StaticContext(span<StackType_t> stack_span) 116 : tcb_{}, stack_span_(stack_span) {} 117 118 private: 119 friend Context; 120 tcb()121 StaticTask_t& tcb() { return tcb_; } stack()122 span<StackType_t> stack() { return stack_span_; } 123 124 StaticTask_t tcb_; 125 span<StackType_t> stack_span_; 126 }; 127 128 // Static thread context allocation including the stack along with the Context. 129 // 130 // Example usage: 131 // 132 // pw::thread::freertos::ContextWithStack<kFooStackSizeWords> 133 // example_thread_context; 134 // void StartExampleThread() { 135 // pw::Thread( 136 // pw::thread::freertos::Options() 137 // .set_name("static_example_thread") 138 // .set_priority(kFooPriority) 139 // .set_static_context(example_thread_context), 140 // example_thread_function).detach(); 141 // } 142 template <size_t kStackSizeWords = config::kDefaultStackSizeWords> 143 class StaticContextWithStack final : public StaticContext { 144 public: StaticContextWithStack()145 constexpr StaticContextWithStack() : StaticContext(stack_storage_) { 146 static_assert(kStackSizeWords >= config::kMinimumStackSizeWords); 147 } 148 149 private: 150 std::array<StackType_t, kStackSizeWords> stack_storage_; 151 }; 152 153 } // namespace pw::thread::freertos 154