• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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