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