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 "pw_thread/id.h" 17 #include "pw_thread/thread_core.h" 18 19 // clang-format off 20 // The backend's thread_native header must provide PW_THREAD_JOINING_ENABLED. 21 #include "pw_thread_backend/thread_native.h" 22 // clang-format on 23 24 namespace pw::thread { 25 26 // The Options contains the parameters needed for a thread to start. 27 // 28 // Options are backend specific and ergo the generic base class cannot be 29 // directly instantiated. 30 // 31 // The attributes which can be set through the options are backend specific 32 // but may contain things like the thread name, priority, scheduling policy, 33 // core/processor affinity, and/or an optional reference to a pre-allocated 34 // Context (the collection of memory allocations needed for a thread to run). 35 // 36 // Options shall NOT have an attribute to start threads as detached vs joinable. 37 // All `pw::thread::Thread` instances must be explicitly `join()`'d or 38 // `detach()`'d through the run-time Thread API. 39 // 40 // Note that if backends set `PW_THREAD_JOINING_ENABLED` to false, backends may 41 // use native OS specific APIs to create native detached threads because the 42 // `join()` API would be compiled out. However, users must still explicitly 43 // invoke `detach()`. 44 // 45 // Options must not contain any memory needed for a thread to run (TCB, 46 // stack, etc.). The Options may be deleted or re-used immediately after 47 // starting a thread. 48 class Options { 49 protected: 50 // We can't use `= default` here, because it allows to create an Options 51 // instance in C++17 with `pw::thread::Options{}` syntax. Options()52 constexpr Options() {} 53 }; 54 55 // The class Thread can represent a single thread of execution. Threads allow 56 // multiple functions to execute concurrently. 57 // 58 // Threads may begin execution immediately upon construction of the associated 59 // thread object (pending any OS scheduling delays), starting at the top-level 60 // function provided as a constructor argument. The return value of the 61 // top-level function is ignored. The top-level function may communicate its 62 // return value by modifying shared variables (which may require 63 // synchronization, see pw_sync and std::atomic) 64 // 65 // Thread objects may also be in the state that does not represent any thread 66 // (after default construction, move from, detach, or join), and a thread of 67 // execution may be not associated with any thread objects (after detach). 68 // 69 // No two Thread objects may represent the same thread of execution; Thread is 70 // not CopyConstructible or CopyAssignable, although it is MoveConstructible and 71 // MoveAssignable. 72 class Thread { 73 public: 74 using native_handle_type = backend::NativeThreadHandle; 75 76 // Creates a new thread object which does not represent a thread of execution 77 // yet. 78 Thread(); 79 80 // Creates a new thread object which represents a thread of execution. 81 // 82 // Thread functions are permitted to return and must have the following 83 // ThreadRoutine signature: 84 // void example_function(void *arg); 85 // 86 // To invoke a member method of a class a static lambda closure can be used 87 // to ensure the dispatching closure is not destructed before the thread is 88 // done executing. For example: 89 // class Foo { 90 // public: 91 // void DoBar() {} 92 // }; 93 // Foo foo; 94 // 95 // static auto invoke_foo_do_bar = [](void *void_foo_ptr) { 96 // // If needed, additional arguments could be set here. 97 // static_cast<Foo*>(void_foo_ptr)->DoBar(); 98 // }; 99 // 100 // // Now use the lambda closure as the thread entry, passing the foo's 101 // // this as the argument. 102 // Thread thread(options, invoke_foo_do_bar, &foo); 103 // thread.detach(); 104 // 105 // Alternatively a helper ThreadCore interface can be implemented by an object 106 // so that a static lambda closure or function is not needed to dispatch to 107 // a member function without arguments. For example: 108 // class Foo : public ThreadCore { 109 // private: 110 // void Run() override {} 111 // }; 112 // Foo foo; 113 // 114 // // Now create the thread, using foo directly. 115 // Thread(options, foo).detach(); 116 // 117 // Postcondition: The thread get EITHER detached or joined. 118 // 119 // NOTE: Options have a default constructor, however default options are not 120 // portable! Default options can only work if threads are dynamically 121 // allocated by default, meaning default options cannot work on backends which 122 // require static thread allocations. In addition on some schedulers 123 // default options may not work for other reasons. 124 using ThreadRoutine = void (*)(void* arg); 125 Thread(const Options& options, ThreadRoutine entry, void* arg = nullptr); 126 Thread(const Options& options, ThreadCore& thread_core); 127 128 // Postcondition: The other thread no longer represents a thread of execution. 129 Thread& operator=(Thread&& other); 130 131 // Precondition: The thread must have been EITHER detached or joined. 132 ~Thread(); 133 134 Thread(const Thread&) = delete; 135 Thread(Thread&&) = delete; 136 Thread& operator=(const Thread&) = delete; 137 138 // Returns a value of Thread::id identifying the thread associated with *this. 139 // If there is no thread associated, default constructed Thread::id is 140 // returned. 141 Id get_id() const; 142 143 // Checks if the Thread object identifies an active thread of execution which 144 // has not yet been detached. Specifically, returns true if get_id() != 145 // pw::Thread::id() && detach() has NOT been invoked. So a default 146 // constructed thread is not joinable and neither is one which was detached. 147 // 148 // A thread that has not started or has finished executing code which was 149 // never detached, but has not yet been joined is still considered an active 150 // thread of execution and is therefore joinable. joinable()151 bool joinable() const { return get_id() != Id(); } 152 153 #if PW_THREAD_JOINING_ENABLED 154 // Blocks the current thread until the thread identified by *this finishes its 155 // execution. 156 // 157 // The completion of the thread identified by *this synchronizes with the 158 // corresponding successful return from join(). 159 // 160 // No synchronization is performed on *this itself. Concurrently calling 161 // join() on the same thread object from multiple threads constitutes a data 162 // race that results in undefined behavior. 163 // 164 // Precondition: The thread must have been NEITHER detached nor joined. 165 // 166 // Postcondition: After calling detach *this no longer owns any thread. 167 void join(); 168 #else 169 template <typename kUnusedType = void> join()170 void join() { 171 static_assert(kJoiningEnabled<kUnusedType>, 172 "The selected pw_thread_THREAD backend does not have join() " 173 "enabled (AKA PW_THREAD_JOINING_ENABLED = 1)"); 174 } 175 #endif // PW_THREAD_JOINING_ENABLED 176 177 // Separates the thread of execution from the thread object, allowing 178 // execution to continue independently. Any allocated resources will be freed 179 // once the thread exits. 180 // 181 // Precondition: The thread must have been NEITHER detached nor joined. 182 // 183 // Postcondition: After calling detach *this no longer owns any thread. 184 void detach(); 185 186 // Exchanges the underlying handles of two thread objects. 187 void swap(Thread& other); 188 189 native_handle_type native_handle(); 190 191 private: 192 template <typename...> 193 static constexpr std::bool_constant<PW_THREAD_JOINING_ENABLED> 194 kJoiningEnabled = {}; 195 196 // Note that just like std::thread, this is effectively just a pointer or 197 // reference to the native thread -- this does not contain any memory needed 198 // for the thread to execute. 199 // 200 // This may contain more than the native thread handle to enable functionality 201 // which is not always available such as joining, which may require a 202 // reference to a binary semaphore, or passing arguments to the thread's 203 // function. 204 backend::NativeThread native_type_; 205 }; 206 207 } // namespace pw::thread 208 209 #include "pw_thread_backend/thread_inline.h" 210