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