• 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 <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