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