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