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 <type_traits> 17 18 #include "pw_function/function.h" 19 #include "pw_thread/id.h" 20 #include "pw_thread/options.h" 21 #include "pw_thread/thread_core.h" 22 23 // clang-format off 24 // The backend's thread_native header must provide PW_THREAD_JOINING_ENABLED. 25 #include "pw_thread_backend/thread_native.h" // IWYU pragma: export 26 // clang-format on 27 28 namespace pw { 29 namespace thread { 30 31 /// The class `Thread` can represent a single thread of execution. Threads allow 32 /// multiple functions to execute concurrently. 33 /// 34 /// Threads may begin execution immediately upon construction of the associated 35 /// thread object (pending any OS scheduling delays), starting at the top-level 36 /// function provided as a constructor argument. The return value of the 37 /// top-level function is ignored. The top-level function may communicate its 38 /// return value by modifying shared variables (which may require 39 /// synchronization, see `pw_sync` and `std::atomic`) 40 /// 41 /// `Thread` objects may also be in the state that does not represent any thread 42 /// (after default construction, move from, detach, or join), and a thread of 43 /// execution may be not associated with any thread objects (after detach). 44 /// 45 /// No two `Thread` objects may represent the same thread of execution; `Thread` 46 /// is not CopyConstructible or CopyAssignable, although it is MoveConstructible 47 /// and MoveAssignable. 48 class Thread { 49 public: 50 /// The type of the native handle for the thread. As with `std::thread`, use 51 /// is inherently non-portable. 52 using native_handle_type = backend::NativeThreadHandle; 53 54 /// The class id is a lightweight, trivially copyable class that serves as a 55 /// unique identifier of Thread objects. 56 /// 57 /// Instances of this class may also hold the special distinct value that does 58 /// not represent any thread. Once a thread has finished, the value of its 59 /// `Thread::id` may be reused by another thread. 60 /// 61 /// This class is designed for use as key in associative containers, both 62 /// ordered and unordered. 63 /// 64 /// The backend must ensure that: 65 /// 66 /// 1. There is a default construct which does not represent a thread. 67 /// 2. Compare operators (`==`, `!=`, `<`, `<=`, `>`, `>=`) are provided to 68 /// compare and sort IDs. 69 using id = ::pw::thread::backend::NativeId; 70 71 /// Creates a new thread object which does not represent a thread of execution 72 /// yet. 73 Thread(); 74 75 /// Creates a thread from a void-returning function or lambda. 76 /// 77 /// This function accepts any callable (including lambdas) which returns 78 /// ``void``. When using a lambda, the captures must not exceed the inline 79 /// size of ``pw::Function`` (usually a single pointer) unless dynamic 80 /// allocation is enabled. 81 /// 82 /// To invoke a member method of a class a static lambda closure can be used 83 /// to ensure the dispatching closure is not destructed before the thread is 84 /// done executing. For example: 85 /// 86 /// @code{.cpp} 87 /// class Foo { 88 /// public: 89 /// void DoBar() {} 90 /// }; 91 /// Foo foo; 92 /// 93 /// // Now use the lambda closure as the thread entry, passing the foo's 94 /// // this as the argument. 95 /// Thread thread(options, [&foo]() { foo.DoBar(); }); 96 /// thread.detach(); 97 /// @endcode 98 /// 99 /// @post The thread get EITHER detached or joined. 100 Thread(const Options& options, Function<void()>&& entry); 101 102 /// Creates a thread from a `ThreadCore` subclass. `ThreadCore` is not 103 /// recommended for new code; use the `pw::Function<void()>` constructor 104 /// instead. 105 /// 106 /// For example: 107 /// 108 /// @code{.cpp} 109 /// class Foo : public ThreadCore { 110 /// private: 111 /// void Run() override {} 112 /// }; 113 /// Foo foo; 114 /// 115 /// // Now create the thread, using foo directly. 116 /// Thread(options, foo).detach(); 117 /// @endcode 118 /// 119 /// @post The thread get EITHER detached or joined. 120 Thread(const Options& options, ThreadCore& thread_core); 121 122 /// @post The other thread no longer represents a thread of execution. 123 Thread& operator=(Thread&& other); 124 125 /// @pre The thread must have been EITHER detached or joined. 126 ~Thread(); 127 128 Thread(const Thread&) = delete; 129 Thread(Thread&&) = delete; 130 Thread& operator=(const Thread&) = delete; 131 132 /// Returns a value of `Thread::id` identifying the thread associated with 133 /// `*this`. If there is no thread associated, default constructed 134 /// `Thread::id` is returned. 135 id get_id() const; 136 137 /// Checks if the `Thread` object identifies an active thread of execution 138 /// which has not yet been detached. Specifically, returns true if `get_id() 139 /// != pw::Thread::id()` and `detach()` has NOT been invoked. So a default 140 /// constructed thread is not joinable and neither is one which was detached. 141 /// 142 /// A thread that has not started or has finished executing code which was 143 /// never detached, but has not yet been joined is still considered an active 144 /// thread of execution and is therefore joinable. joinable()145 bool joinable() const { return get_id() != id(); } 146 147 #if PW_THREAD_JOINING_ENABLED 148 /// Blocks the current thread until the thread identified by `*this` finishes 149 /// its execution. 150 /// 151 /// The completion of the thread identified by *this synchronizes with the 152 /// corresponding successful return from join(). 153 /// 154 /// No synchronization is performed on `*this` itself. Concurrently calling 155 /// `join()` on the same thread object from multiple threads constitutes a 156 /// data race that results in undefined behavior. 157 /// 158 /// @pre The thread must have been NEITHER detached nor joined. 159 /// 160 /// @post After calling detach `*this` no longer owns any thread. 161 void join(); 162 #else 163 template <typename kUnusedType = void> join()164 void join() { 165 static_assert(kJoiningEnabled<kUnusedType>, 166 "The selected pw_thread_THREAD backend does not have join() " 167 "enabled (AKA PW_THREAD_JOINING_ENABLED = 1)"); 168 } 169 #endif // PW_THREAD_JOINING_ENABLED 170 171 /// Separates the thread of execution from the thread object, allowing 172 /// execution to continue independently. Any allocated resources will be freed 173 /// once the thread exits. 174 /// 175 /// @pre The thread must have been NEITHER detached nor joined. 176 /// 177 /// @post After calling detach *this no longer owns any thread. 178 void detach(); 179 180 /// Exchanges the underlying handles of two thread objects. 181 void swap(Thread& other); 182 183 /// Returns the native handle for the thread. As with `std::thread`, use is 184 /// inherently non-portable. 185 native_handle_type native_handle(); 186 187 private: 188 template <typename...> 189 static constexpr std::bool_constant<PW_THREAD_JOINING_ENABLED> 190 kJoiningEnabled = {}; 191 192 // Note that just like std::thread, this is effectively just a pointer or 193 // reference to the native thread -- this does not contain any memory needed 194 // for the thread to execute. 195 // 196 // This may contain more than the native thread handle to enable functionality 197 // which is not always available such as joining, which may require a 198 // reference to a binary semaphore, or passing arguments to the thread's 199 // function. 200 backend::NativeThread native_type_; 201 }; 202 203 } // namespace thread 204 205 /// `pw::thread::Thread` will be renamed to `pw::Thread`. New code should refer 206 /// to `pw::Thread`. 207 using Thread = ::pw::thread::Thread; // Must use `=` for Doxygen to find this. 208 209 } // namespace pw 210 211 #include "pw_thread_backend/thread_inline.h" 212