blob: c2f664713cc78c8b030130094be0c869a6dc9e16 [file] [log] [blame]
Ewout van Bekkume3b56032020-12-22 12:00:18 -08001.. _module-pw_thread:
2
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -07003=========
Ewout van Bekkume3b56032020-12-22 12:00:18 -08004pw_thread
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -07005=========
6The ``pw_thread`` module contains utilities for thread creation and thread
7execution.
Ewout van Bekkume3b56032020-12-22 12:00:18 -08008
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -07009.. contents::
10 :local:
11 :depth: 2
12
13.. Warning::
14 This module is still under construction, the API is not yet stable.
15
16---------------
17Thread Creation
18---------------
19The class ``pw::thread::Thread`` can represent a single thread of execution.
20Threads allow multiple functions to execute concurrently.
21
22The Thread's API is C++11 STL
23`std::thread <https://en.cppreference.com/w/cpp/thread/thread>`_ like, meaning
24the object is effectively a thread handle and not an object which contains the
25thread's context. Unlike ``std::thread``, the API requires
26``pw::thread::Options`` as an argument and is limited to only work with
27``pw::thread::ThreadCore`` objects and functions which match the
28``pw::thread::Thread::ThreadRoutine`` signature.
29
Ewout van Bekkumcc756c82021-05-12 07:57:43 -070030We recognize that the C++11's STL ``std::thread``` API has some drawbacks where
31it is easy to forget to join or detach the thread handle. Because of this, we
32offer helper wrappers like the ``pw::thread::DetachedThread``. Soon we will
33extend this by also adding a ``pw::thread::JoiningThread`` helper wrapper which
34will also have a lighter weight C++20 ``std::jthread`` like cooperative
35cancellation contract to make joining safer and easier.
36
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -070037Threads may begin execution immediately upon construction of the associated
38thread object (pending any OS scheduling delays), starting at the top-level
39function provided as a constructor argument. The return value of the
40top-level function is ignored. The top-level function may communicate its
41return value by modifying shared variables (which may require
42synchronization, see :ref:`module-pw_sync`)
43
44Thread objects may also be in the state that does not represent any thread
45(after default construction, move from, detach, or join), and a thread of
46execution may be not associated with any thread objects (after detach).
47
48No two Thread objects may represent the same thread of execution; Thread is
49not CopyConstructible or CopyAssignable, although it is MoveConstructible and
50MoveAssignable.
51
52.. list-table::
53
54 * - *Supported on*
55 - *Backend module*
56 * - FreeRTOS
57 - :ref:`module-pw_thread_freertos`
58 * - ThreadX
59 - :ref:`module-pw_thread_threadx`
60 * - embOS
Ewout van Bekkum6e5d43e2021-05-06 15:29:44 -070061 - :ref:`module-pw_thread_embos`
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -070062 * - STL
63 - :ref:`module-pw_thread_stl`
64 * - Zephyr
65 - Planned
66 * - CMSIS-RTOS API v2 & RTX5
67 - Planned
68
69
70Options
71=======
72The ``pw::thread::Options`` contains the parameters or attributes needed for a
73thread to start.
74
75Pigweed does not generalize options, instead we strive to give you full control
76where we provide helpers to do this.
77
78Options are backend specific and ergo the generic base class cannot be
79directly instantiated.
80
81The attributes which can be set through the options are backend specific
82but may contain things like the thread name, priority, scheduling policy,
83core/processor affinity, and/or an optional reference to a pre-allocated
84Context (the collection of memory allocations needed for a thread to run).
85
86Options shall NOT permit starting as detached, this must be done explicitly
87through the Thread API.
88
89Options must not contain any memory needed for a thread to run (TCB,
90stack, etc.). The Options may be deleted or re-used immediately after
91starting a thread.
92
93Please see the thread creation backend documentation for how their Options work.
94
95.. Note::
96 Options have a default constructor, however default options are not portable!
97 Default options can only work if threads are dynamically allocated by default,
98 meaning default options cannot work on backends which require static thread
99 allocations. In addition on some schedulers, default options will not work
100 for other reasons.
101
102Detaching & Joining
103===================
104The ``Thread::detach()`` API is always available, to let you separate the
105thread of execution from the thread object, allowing execution to continue
106independently.
107
108The joining API, more specifically ``Thread::join()``, is conditionally
109available depending on the selected backend for thread creation and how it is
110configured. The backend is responsible for providing the
111``PW_THREAD_JOINING_ENABLED`` macro through
112``pw_thread_backend/thread_native.h``. This ensures that any users which include
113``pw_thread/thread.h`` can use this macro if needed.
114
115Please see the selected thread creation backend documentation for how to
116enable joining if it's not already enabled by default.
117
118.. Warning::
119 A constructed ``pw::thread::Thread`` which represents a thread of execution
120 must be EITHER detached or joined, else the destructor will assert!
121
Ewout van Bekkum19e753a2021-04-28 18:06:55 -0700122DetachedThread
123==============
124To make it slightly easier and cleaner to spawn detached threads without having
125to worry about thread handles, a wrapper ``DetachedThread()`` function is
126provided which creates a ``Thread`` and immediately detaches it. For example
127instead of:
128
129.. code-block:: cpp
130
131 Thread(options, foo).detach();
132
133You can instead use this helper wrapper to:
134
135.. code-block:: cpp
136
137 DetachedThread(options, foo);
138
139The arguments are directly forwarded to the Thread constructor and ergo exactly
140match the Thread constuctor arguments for creating a thread of execution.
141
142
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -0700143ThreadRoutine & ThreadCore
144==========================
145Threads must either be invoked through a
146``pw::thread::Thread::ThreadRoutine``` style function or implement the
147``pw::thread::ThreadCore`` interface.
148
149.. code-block:: cpp
150
151 namespace pw::thread {
152
153 // This function may return.
154 using Thread::ThreadRoutine = void (*)(void* arg);
155
156 class ThreadCore {
157 public:
158 virtual ~ThreadCore() = default;
159
160 // The public API to start a ThreadCore, note that this may return.
161 void Start() { Run(); }
162
163 private:
164 // This function may return.
165 virtual void Run() = 0;
166 };
167
168 } // namespace pw::thread;
169
170
171To use the ``pw::thread::Thread::ThreadRoutine``, your function must have the
172following signature:
173
174.. code-block:: cpp
175
176 void example_thread_entry_function(void *arg);
177
178
179To invoke a member method of a class a static lambda closure can be used
180to ensure the dispatching closure is not destructed before the thread is
181done executing. For example:
182
183.. code-block:: cpp
184
185 class Foo {
186 public:
187 void DoBar() {}
188 };
189 Foo foo;
190
191 static auto invoke_foo_do_bar = [](void *void_foo_ptr) {
192 // If needed, additional arguments could be set here.
193 static_cast<Foo*>(void_foo_ptr)->DoBar();
194 };
195
196 // Now use the lambda closure as the thread entry, passing the foo's
197 // this as the argument.
198 Thread thread(options, invoke_foo_do_bar, &foo);
199 thread.detach();
200
201
202Alternatively, the aforementioned ``pw::thread::ThreadCore`` interface can be
203be implemented by an object by overriding the private
204``void ThreadCore::Run();`` method. This makes it easier to create a thread, as
205a static lambda closure or function is not needed to dispatch to a member
206function without arguments. For example:
207
208.. code-block:: cpp
209
210 class Foo : public ThreadCore {
211 private:
212 void Run() override {}
213 };
214 Foo foo;
215
216 // Now create the thread, using foo directly.
217 Thread(options, foo).detach();
218
219.. Warning::
220 Because the thread may start after the pw::Thread creation, an object which
221 implements the ThreadCore MUST meet or exceed the lifetime of its thread of
222 execution!
Armando Montanezfec572b2021-06-28 12:13:57 -0700223
224-----------------------
225pw_snapshot integration
226-----------------------
227``pw_thread`` provides some light, optional integration with pw_snapshot through
228helper functions for populating a ``pw::thread::Thread`` proto. Some of these
229are directly integrated into the RTOS thread backends to simplify the thread
230state capturing for snapshots.
231
232SnapshotStack()
233===============
234The ``SnapshotStack()`` helper captures stack metadata (stack pointer and
235bounds) into a ``pw::thread::Thread`` proto. After the stack bounds are
236captured, execution is passed off to the thread stack collection callback to
237capture a backtrace or stack dump. Note that this function does NOT capture the
238thread name: that metadata is only required in cases where a stack overflow or
239underflow is detected.
240
241.. Warning::
242 Snapshot integration is a work-in-progress and may see significant API
243 changes.