• 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 "pw_thread/id.h"
17 #include "pw_thread/thread_core.h"
18 
19 // clang-format off
20 // The backend's thread_native header must provide PW_THREAD_JOINING_ENABLED.
21 #include "pw_thread_backend/thread_native.h"
22 // clang-format on
23 
24 namespace pw::thread {
25 
26 // The Options contains the parameters needed for a thread to start.
27 //
28 // Options are backend specific and ergo the generic base class cannot be
29 // directly instantiated.
30 //
31 // The attributes which can be set through the options are backend specific
32 // but may contain things like the thread name, priority, scheduling policy,
33 // core/processor affinity, and/or an optional reference to a pre-allocated
34 // Context (the collection of memory allocations needed for a thread to run).
35 //
36 // Options shall NOT have an attribute to start threads as detached vs joinable.
37 // All `pw::thread::Thread` instances must be explicitly `join()`'d or
38 // `detach()`'d through the run-time Thread API.
39 //
40 // Note that if backends set `PW_THREAD_JOINING_ENABLED` to false, backends may
41 // use native OS specific APIs to create native detached threads because the
42 // `join()` API would be compiled out. However, users must still explicitly
43 // invoke `detach()`.
44 //
45 // Options must not contain any memory needed for a thread to run (TCB,
46 // stack, etc.). The Options may be deleted or re-used immediately after
47 // starting a thread.
48 class Options {
49  protected:
50   // We can't use `= default` here, because it allows to create an Options
51   // instance in C++17 with `pw::thread::Options{}` syntax.
Options()52   constexpr Options() {}
53 };
54 
55 // The class Thread can represent a single thread of execution. Threads allow
56 // multiple functions to execute concurrently.
57 //
58 // Threads may begin execution immediately upon construction of the associated
59 // thread object (pending any OS scheduling delays), starting at the top-level
60 // function provided as a constructor argument. The return value of the
61 // top-level function is ignored. The top-level function may communicate its
62 // return value by modifying shared variables (which may require
63 // synchronization, see pw_sync and std::atomic)
64 //
65 // Thread objects may also be in the state that does not represent any thread
66 // (after default construction, move from, detach, or join), and a thread of
67 // execution may be not associated with any thread objects (after detach).
68 //
69 // No two Thread objects may represent the same thread of execution; Thread is
70 // not CopyConstructible or CopyAssignable, although it is MoveConstructible and
71 // MoveAssignable.
72 class Thread {
73  public:
74   using native_handle_type = backend::NativeThreadHandle;
75 
76   // Creates a new thread object which does not represent a thread of execution
77   // yet.
78   Thread();
79 
80   // Creates a new thread object which represents a thread of execution.
81   //
82   // Thread functions are permitted to return and must have the following
83   // ThreadRoutine signature:
84   //   void example_function(void *arg);
85   //
86   // To invoke a member method of a class a static lambda closure can be used
87   // to ensure the dispatching closure is not destructed before the thread is
88   // done executing. For example:
89   //   class Foo {
90   //    public:
91   //     void DoBar() {}
92   //   };
93   //   Foo foo;
94   //
95   //   static auto invoke_foo_do_bar = [](void *void_foo_ptr) {
96   //       //  If needed, additional arguments could be set here.
97   //       static_cast<Foo*>(void_foo_ptr)->DoBar();
98   //   };
99   //
100   //   // Now use the lambda closure as the thread entry, passing the foo's
101   //   // this as the argument.
102   //   Thread thread(options, invoke_foo_do_bar, &foo);
103   //   thread.detach();
104   //
105   // Alternatively a helper ThreadCore interface can be implemented by an object
106   // so that a static lambda closure or function is not needed to dispatch to
107   // a member function without arguments. For example:
108   //   class Foo : public ThreadCore {
109   //    private:
110   //     void Run() override {}
111   //   };
112   //   Foo foo;
113   //
114   //   // Now create the thread, using foo directly.
115   //   Thread(options, foo).detach();
116   //
117   // Postcondition: The thread get EITHER detached or joined.
118   //
119   // NOTE: Options have a default constructor, however default options are not
120   // portable! Default options can only work if threads are dynamically
121   // allocated by default, meaning default options cannot work on backends which
122   // require static thread allocations. In addition on some schedulers
123   // default options may not work for other reasons.
124   using ThreadRoutine = void (*)(void* arg);
125   Thread(const Options& options, ThreadRoutine entry, void* arg = nullptr);
126   Thread(const Options& options, ThreadCore& thread_core);
127 
128   // Postcondition: The other thread no longer represents a thread of execution.
129   Thread& operator=(Thread&& other);
130 
131   // Precondition: The thread must have been EITHER detached or joined.
132   ~Thread();
133 
134   Thread(const Thread&) = delete;
135   Thread(Thread&&) = delete;
136   Thread& operator=(const Thread&) = delete;
137 
138   // Returns a value of Thread::id identifying the thread associated with *this.
139   // If there is no thread associated, default constructed Thread::id is
140   // returned.
141   Id get_id() const;
142 
143   // Checks if the Thread object identifies an active thread of execution which
144   // has not yet been detached. Specifically, returns true if get_id() !=
145   // pw::Thread::id() && detach() has NOT been invoked. So a default
146   // constructed thread is not joinable and neither is one which was detached.
147   //
148   // A thread that has not started or has finished executing code which was
149   // never detached, but has not yet been joined is still considered an active
150   // thread of execution and is therefore joinable.
joinable()151   bool joinable() const { return get_id() != Id(); }
152 
153 #if PW_THREAD_JOINING_ENABLED
154   // Blocks the current thread until the thread identified by *this finishes its
155   // execution.
156   //
157   // The completion of the thread identified by *this synchronizes with the
158   // corresponding successful return from join().
159   //
160   // No synchronization is performed on *this itself. Concurrently calling
161   // join() on the same thread object from multiple threads constitutes a data
162   // race that results in undefined behavior.
163   //
164   // Precondition: The thread must have been NEITHER detached nor joined.
165   //
166   // Postcondition: After calling detach *this no longer owns any thread.
167   void join();
168 #else
169   template <typename kUnusedType = void>
join()170   void join() {
171     static_assert(kJoiningEnabled<kUnusedType>,
172                   "The selected pw_thread_THREAD backend does not have join() "
173                   "enabled (AKA PW_THREAD_JOINING_ENABLED = 1)");
174   }
175 #endif  // PW_THREAD_JOINING_ENABLED
176 
177   // Separates the thread of execution from the thread object, allowing
178   // execution to continue independently. Any allocated resources will be freed
179   // once the thread exits.
180   //
181   // Precondition: The thread must have been NEITHER detached nor joined.
182   //
183   // Postcondition: After calling detach *this no longer owns any thread.
184   void detach();
185 
186   // Exchanges the underlying handles of two thread objects.
187   void swap(Thread& other);
188 
189   native_handle_type native_handle();
190 
191  private:
192   template <typename...>
193   static constexpr std::bool_constant<PW_THREAD_JOINING_ENABLED>
194       kJoiningEnabled = {};
195 
196   // Note that just like std::thread, this is effectively just a pointer or
197   // reference to the native thread -- this does not contain any memory needed
198   // for the thread to execute.
199   //
200   // This may contain more than the native thread handle to enable functionality
201   // which is not always available such as joining, which may require a
202   // reference to a binary semaphore, or passing arguments to the thread's
203   // function.
204   backend::NativeThread native_type_;
205 };
206 
207 }  // namespace pw::thread
208 
209 #include "pw_thread_backend/thread_inline.h"
210