1 // Copyright 2021 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 <cstring> 18 #include <span> 19 20 #include "pw_thread_threadx/config.h" 21 #include "tx_api.h" 22 #include "tx_thread.h" 23 24 namespace pw::thread { 25 26 class Thread; // Forward declare Thread which depends on Context. 27 28 } // namespace pw::thread 29 30 namespace pw::thread::threadx { 31 32 // Static thread context allocation including the TCB, an event group for 33 // joining if enabled, and an external statically allocated stack. 34 // 35 // Example usage: 36 // 37 // std::array<ULONG, 42> example_thread_stack; 38 // pw::thread::threadx::Context example_thread_context(example_thread_stack); 39 // void StartExampleThread() { 40 // pw::thread::Thread( 41 // pw::thread::threadx::Options() 42 // .set_name("static_example_thread") 43 // .set_priority(kFooPriority) 44 // .set_static_context(example_thread_context), 45 // example_thread_function).detach(); 46 // } 47 class Context { 48 public: Context(std::span<ULONG> stack_span)49 explicit Context(std::span<ULONG> stack_span) 50 : tcb_{}, stack_span_(stack_span) {} 51 Context(const Context&) = delete; 52 Context& operator=(const Context&) = delete; 53 54 // Intended for unit test & Thread use only. tcb()55 TX_THREAD& tcb() { return tcb_; } 56 57 private: 58 friend Thread; 59 stack()60 std::span<ULONG> stack() { return stack_span_; } 61 in_use()62 bool in_use() const { return in_use_; } 63 void set_in_use(bool in_use = true) { in_use_ = in_use; } 64 name()65 const char* name() const { return name_.data(); } set_name(const char * name)66 void set_name(const char* name) { 67 strncpy(name_.data(), name, name_.size() - 1); 68 // Forcefully NULL terminate as strncpy does not NULL terminate if the count 69 // limit is hit. 70 name_[name_.size() - 1] = '\0'; 71 } 72 73 using ThreadRoutine = void (*)(void* arg); set_thread_routine(ThreadRoutine entry,void * arg)74 void set_thread_routine(ThreadRoutine entry, void* arg) { 75 entry_ = entry; 76 arg_ = arg; 77 } 78 detached()79 bool detached() const { return detached_; } 80 void set_detached(bool value = true) { detached_ = value; } 81 thread_done()82 bool thread_done() const { return thread_done_; } 83 void set_thread_done(bool value = true) { thread_done_ = value; } 84 85 #if PW_THREAD_JOINING_ENABLED join_event_group()86 TX_EVENT_FLAGS_GROUP& join_event_group() { return event_group_; } 87 #endif // PW_THREAD_JOINING_ENABLED 88 89 static void RunThread(ULONG void_context_ptr); 90 static void DeleteThread(Context& context); 91 92 TX_THREAD tcb_; 93 std::span<ULONG> stack_span_; 94 95 ThreadRoutine entry_ = nullptr; 96 void* arg_ = nullptr; 97 #if PW_THREAD_JOINING_ENABLED 98 // Note that the ThreadX life cycle of this event group is managed together 99 // with the thread life cycle, not this object's life cycle. 100 TX_EVENT_FLAGS_GROUP event_group_; 101 #endif // PW_THREAD_JOINING_ENABLED 102 bool in_use_ = false; 103 bool detached_ = false; 104 bool thread_done_ = false; 105 106 // The TCB does not have storage for the name, ergo we provide storage for 107 // the thread's name which can be truncated down to just a null delimeter. 108 std::array<char, config::kMaximumNameLength> name_; 109 }; 110 111 // Static thread context allocation including the stack along with the Context. 112 // 113 // Example usage: 114 // 115 // pw::thread::threadx::ContextWithStack<42> example_thread_context; 116 // void StartExampleThread() { 117 // pw::thread::Thread( 118 // pw::thread::threadx::Options() 119 // .set_name("static_example_thread") 120 // .set_priority(kFooPriority) 121 // .set_static_context(example_thread_context), 122 // example_thread_function).detach(); 123 // } 124 template <size_t kStackSizeWords = config::kDefaultStackSizeWords> 125 class ContextWithStack final : public Context { 126 public: ContextWithStack()127 constexpr ContextWithStack() : Context(stack_storage_) { 128 static_assert(kStackSizeWords >= config::kMinimumStackSizeWords); 129 } 130 131 private: 132 std::array<ULONG, kStackSizeWords> stack_storage_; 133 }; 134 135 } // namespace pw::thread::threadx 136