xref: /aosp_15_r20/external/pigweed/pw_thread/public/pw_thread/thread.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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