• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_thread:
2
3=========
4pw_thread
5=========
6The ``pw_thread`` module contains utilities for thread creation and thread
7execution.
8
9.. Warning::
10  This module is still under construction, the API is not yet stable.
11
12---------------
13Thread Sleeping
14---------------
15C++
16===
17.. cpp:function:: void pw::this_thread::sleep_for(chrono::SystemClock::duration sleep_duration)
18
19   Blocks the execution of the current thread for at least the specified
20   duration. This function may block for longer due to scheduling or resource
21   contention delays.
22
23   A sleep duration of 0 will at minimum yield, meaning it will provide a hint
24   to the implementation to reschedule the execution of threads, allowing other
25   threads to run.
26
27   **Precondition:** This can only be called from a thread, meaning the
28   scheduler is running.
29
30.. cpp:function:: void pw::this_thread::sleep_until(chrono::SystemClock::time_point wakeup_time)
31
32   Blocks the execution of the current thread until at least the specified
33   time has been reached. This function may block for longer due to scheduling
34   or resource contention delays.
35
36   A sleep deadline in the past up to the current time will at minimum yield
37   meaning it will provide a hint to the implementation to reschedule the
38   execution of threads, allowing other threads to run.
39
40   **Precondition:** This can only be called from a thread, meaning the
41   scheduler is running.
42
43Examples in C++
44---------------
45.. code-block:: cpp
46
47   #include <chrono>
48
49   #include "pw_chrono/system_clock.h"
50   #include "pw_thread/sleep.h"
51
52   using std::literals::chrono_literals::ms;
53
54   void FunctionInvokedByThread() {
55     pw::this_thread::sleep_for(42ms);
56   }
57
58   void AnotherFunctionInvokedByThread() {
59     pw::this_thread::sleep_until(pw::chrono::SystemClock::now() + 42ms);
60   }
61
62C
63=
64.. cpp:function:: void pw_this_thread_SleepFor(pw_chrono_SystemClock_Duration sleep_duration)
65
66   Invokes ``pw::this_thread::sleep_until(sleep_duration)``.
67
68.. cpp:function:: void pw_this_thread_SleepUntil(pw_chrono_SystemClock_TimePoint wakeup_time)
69
70   Invokes ``pw::this_thread::sleep_until(wakeup_time)``.
71
72
73---------------
74Thread Yielding
75---------------
76C++
77===
78.. cpp:function:: void pw::this_thread::yield() noexcept
79
80   Provides a hint to the implementation to reschedule the execution of threads,
81   allowing other threads to run.
82
83   The exact behavior of this function depends on the implementation, in
84   particular on the mechanics of the OS scheduler in use and the state of the
85   system.
86
87   **Precondition:** This can only be called from a thread, meaning the
88   scheduler is running.
89
90Example in C++
91---------------
92.. code-block:: cpp
93
94   #include "pw_thread/yield.h"
95
96   void FunctionInvokedByThread() {
97     pw::this_thread::yield();
98   }
99
100C
101=
102.. cpp:function:: void pw_this_thread_Yield(void)
103
104   Invokes ``pw::this_thread::yield()``.
105
106---------------------
107Thread Identification
108---------------------
109The class ``pw::thread::Id`` is a lightweight, trivially copyable class that
110serves as a unique identifier of Thread objects.
111
112Instances of this class may also hold the special distinct value that does
113not represent any thread. Once a thread has finished, the value of its
114Thread::id may be reused by another thread.
115
116This class is designed for use as key in associative containers, both ordered
117and unordered.
118
119Although the current API is similar to C++11 STL
120`std::thread::id <https://en.cppreference.com/w/cpp/thread/thread/id>`_, it is
121missing the required hashing and streaming operators and may diverge further in
122the future.
123
124A thread's identification (``pw::thread::Id``) can be acquired only in C++ in
125one of two ways:
126
1271) Using the ``pw::thread::Thread`` handle's ``pw::thread::Id get_id() const``
128   method.
1292) While executing the thread using
130   ``pw::thread::Id pw::this_thread::get_id() noexcept``.
131
132.. cpp:function:: pw::thread::Id pw::this_thread::get_id() noexcept
133
134   This is thread safe, not IRQ safe. It is implementation defined whether this
135   is safe before the scheduler has started.
136
137
138Example
139=======
140.. code-block:: cpp
141
142   #include "pw_thread/id.h"
143
144   void FunctionInvokedByThread() {
145     const pw::thread::Id my_id = pw::this_thread::get_id();
146   }
147
148
149.. _module-pw_thread-thread-creation:
150
151---------------
152Thread creation
153---------------
154The ``pw::thread::Thread`` can be used to create a thread, allowing
155multiple functions to execute concurrently.
156
157API reference
158=============
159.. doxygenclass:: pw::thread::Thread
160   :members:
161
162.. doxygenclass:: pw::thread::Options
163   :members:
164
165Differences from ``std::thread``
166================================
167The ``pw::thread:Thread`` API is similar to the C++11 STL
168`std::thread <https://en.cppreference.com/w/cpp/thread/thread>`_ class, meaning
169the object is effectively a thread handle and not an object which contains the
170thread's context. Unlike ``std::thread``, the API requires
171``pw::thread::Options`` as an argument. These options are platform-specific,
172and allow the user to specify details such as the thread's name, priority,
173stack size, and where the thread's memory will be stored.
174
175We recognize that the C++11's STL ``std::thread`` API has some drawbacks where
176it is easy to forget to join or detach the thread handle. Because of this, we
177offer helper wrappers like the ``pw::thread::DetachedThread``. Soon we will
178extend this by also adding a ``pw::thread::JoiningThread`` helper wrapper which
179will also have a lighter weight C++20 ``std::jthread`` like cooperative
180cancellation contract to make joining safer and easier.
181
182Execution order
183===============
184Threads may begin execution immediately upon construction of the associated
185thread object (pending any OS scheduling delays), starting at the top-level
186function provided as a constructor argument. The top-level function may
187communicate its return value by modifying shared variables (which may require
188synchronization, see :ref:`module-pw_sync`)
189
190Thread objects may also be in the state that does not represent any thread
191(after default construction, move from, detach, or join), and a thread of
192execution may be not associated with any thread objects (after detach).
193
194No two Thread objects may represent the same thread of execution; Thread is
195not CopyConstructible or CopyAssignable, although it is MoveConstructible and
196MoveAssignable.
197
198.. list-table::
199
200  * - *Supported on*
201    - *Backend module*
202  * - FreeRTOS
203    - :ref:`module-pw_thread_freertos`
204  * - ThreadX
205    - :ref:`module-pw_thread_threadx`
206  * - embOS
207    - :ref:`module-pw_thread_embos`
208  * - STL
209    - :ref:`module-pw_thread_stl`
210  * - Zephyr
211    - Planned
212  * - CMSIS-RTOS API v2 & RTX5
213    - Planned
214
215Module Configuration Options
216============================
217The following configurations can be adjusted via compile-time configuration of
218this module, see the
219:ref:`module documentation <module-structure-compile-time-configuration>` for
220more details.
221
222.. c:macro:: PW_THREAD_CONFIG_LOG_LEVEL
223
224  The log level to use for this module. Logs below this level are omitted.
225
226Options
227=======
228The ``pw::thread::Options`` contains the parameters or attributes needed for a
229thread to start.
230
231Pigweed does not generalize options, instead we strive to give you full control
232where we provide helpers to do this.
233
234Options are backend specific and ergo the generic base class cannot be
235directly instantiated.
236
237The attributes which can be set through the options are backend specific
238but may contain things like the thread name, priority, scheduling policy,
239core/processor affinity, and/or an optional reference to a pre-allocated
240Context (the collection of memory allocations needed for a thread to run).
241
242Options shall NOT have an attribute to start threads as detached vs joinable.
243All ``pw::thread::Thread`` instances must be explicitly ``join()``'d or
244``detach()``'d through the run-time Thread API.
245
246Note that if backends set ``PW_THREAD_JOINING_ENABLED`` to false, backends
247may use native OS specific APIs to create native detached threads because the
248``join()`` API would be compiled out. However, users must still explicitly
249invoke ``detach()``.
250
251Options must not contain any memory needed for a thread to run (TCB,
252stack, etc.). The Options may be deleted or re-used immediately after
253starting a thread.
254
255Options subclass must contain non-default explicit constructor (parametrized or
256not), e.g. ``constexpr Options() {}``. It is not enough to have them as
257``= default`` ones, because C++17 considers subclasses like ``stl::Options`` as
258aggregate classes if they have a default constructor and requires base class
259constructor to be public (which is not the case for the ``thread::Options``) for
260``Options{}`` syntax.
261
262Please see the thread creation backend documentation for how their Options work.
263
264Portable Thread Creation
265========================
266Due to the fact that ``pw::thread::Options`` cannot be created in portable code,
267some extra work must be done in order to permit portable thread creation.
268Namely, a reference to the portable ``pw::thread::Options`` base class interface
269must be provided through a header or extern which points to an instantiation in
270non-portable code.
271
272This can be most easily done through a facade and set of backends. This approach
273can be powerful; enabling multithreaded unit/integration testing which can run
274on both the host and on a device with the device's exact thread options.
275
276Alternatively, it can also be be injected at build time by instantiating backend
277specific build rule which share the same common portable source file(s) but
278select backend specific source files and/or dependencies which provide the
279non-portable option instantiations.
280
281As an example, let's say we want to create a thread on the host and on a device
282running FreeRTOS. They could use a facade which contains a ``threads.h`` header
283with the following contents:
284
285.. code-block:: cpp
286
287   // Contents of my_app/threads.h
288   #pragma once
289
290   #include "pw_thread/thread.h"
291
292   namespace my_app {
293
294   const pw::thread::Options& HellowWorldThreadOptions();
295
296   }  // namespace my_app
297
298This could then be backed by two different backend implementations based on
299the thread backend. For example for the STL the backend's ``stl_threads.cc``
300source file may look something like:
301
302.. code-block:: cpp
303
304   // Contents of my_app/stl_threads.cc
305   #include "my_app/threads.h"
306   #include "pw_thread_stl/options.h"
307
308   namespace my_app {
309
310   const pw::thread::Options& HelloWorldThreadOptions() {
311     static constexpr auto options = pw::thread::stl::Options();
312     return options;
313   }
314
315   }  // namespace my_app
316
317While for FreeRTOS the backend's ``freertos_threads.cc`` source file may look
318something like:
319
320.. code-block:: cpp
321
322   // Contents of my_app/freertos_threads.cc
323   #include "FreeRTOS.h"
324   #include "my_app/threads.h"
325   #include "pw_thread_freertos/context.h"
326   #include "pw_thread_freertos/options.h"
327   #include "task.h"
328
329   namespace my_app {
330
331   StaticContextWithStack<kHelloWorldStackWords> hello_world_thread_context;
332   const pw::thread::Options& HelloWorldThreadOptions() {
333     static constexpr auto options =
334         pw::thread::freertos::Options()
335             .set_name("HelloWorld")
336             .set_static_context(hello_world_thread_context)
337             .set_priority(kHelloWorldThreadPriority);
338     return options;
339   }
340
341   }  // namespace my_app
342
343Detaching & Joining
344===================
345The ``Thread::detach()`` API is always available, to let you separate the
346thread of execution from the thread object, allowing execution to continue
347independently.
348
349The joining API, more specifically ``Thread::join()``, is conditionally
350available depending on the selected backend for thread creation and how it is
351configured. The backend is responsible for providing the
352``PW_THREAD_JOINING_ENABLED`` macro through
353``pw_thread_backend/thread_native.h``. This ensures that any users which include
354``pw_thread/thread.h`` can use this macro if needed.
355
356Please see the selected thread creation backend documentation for how to
357enable joining if it's not already enabled by default.
358
359.. Warning::
360  A constructed ``pw::thread::Thread`` which represents a thread of execution
361  must be EITHER detached or joined, else the destructor will assert!
362
363DetachedThread
364==============
365To make it slightly easier and cleaner to spawn detached threads without having
366to worry about thread handles, a wrapper ``DetachedThread()`` function is
367provided which creates a ``Thread`` and immediately detaches it. For example
368instead of:
369
370.. code-block:: cpp
371
372   Thread(options, foo).detach();
373
374You can instead use this helper wrapper to:
375
376.. code-block:: cpp
377
378   DetachedThread(options, foo);
379
380The arguments are directly forwarded to the Thread constructor and ergo exactly
381match the Thread constuctor arguments for creating a thread of execution.
382
383
384Thread functions and ThreadCore
385===============================
386Thread functions may be provided using either a ``pw::Function<void()>``
387(which may be a lambda or function pointer) or an implementation of the
388``pw::thread::ThreadCore`` interface.
389
390To use the ``pw::Function<void()>`` interface, provide a no-argument,
391void-returning lambda or other callable:
392
393.. code-block:: cpp
394
395   Thread thread(options, []() {
396     // do some work in a thread.
397   });
398
399Note that lambdas can capture up to one pointer-sized argument (or more if
400dynamic allocation is enabled). This can be used to call methods on existing
401objects (though be sure that the objects' lifetime will outlive the thread,
402and note that synchronization may be needed).
403
404.. code-block:: cpp
405
406   class Foo {
407    public:
408     void DoBar() {}
409   };
410   Foo foo;
411
412   Thread thread(options, [&foo] {
413     foo.DoBar();
414   });
415
416Alternatively, you can extend the ``ThreadCore`` class in order to use a more
417explicit construction. For example:
418
419.. code-block:: cpp
420
421   class Foo : public ThreadCore {
422    private:
423     void Run() override {}
424   };
425   Foo foo;
426
427   // Now create the thread, using foo directly.
428   Thread(options, foo).detach();
429
430.. Warning::
431  Because the thread may start after the pw::Thread creation, an object which
432  implements the ThreadCore MUST meet or exceed the lifetime of its thread of
433  execution!
434
435-------------------------
436Unit testing with threads
437-------------------------
438.. doxygenclass:: pw::thread::test::TestThreadContext
439   :members:
440
441As an example, the STL :cpp:class:`TestThreadContext` backend implementation in
442``test_thread_context_native.h`` is shown below.
443
444.. literalinclude:: ../pw_thread_stl/public/pw_thread_stl/test_thread_context_native.h
445   :language: cpp
446   :lines: 18-36
447
448----------------
449Thread Iteration
450----------------
451C++
452===
453.. cpp:function:: Status ForEachThread(const ThreadCallback& cb)
454
455   Calls the provided callback for each thread that has not been joined/deleted.
456
457   This function provides a generalized subset of information that a TCB might
458   contain to make it easier to introspect system state. Depending on the RTOS
459   and its configuration, some of these fields may not be populated, so it is
460   important to check that they have values before attempting to access them.
461
462   **Warning:**  The function may disable the scheduler to perform
463   a runtime capture of thread information.
464
465-----------------------
466Thread Snapshot Service
467-----------------------
468``pw_thread`` offers an optional RPC service library
469(``:thread_snapshot_service``) that enables thread info capture of
470running threads on a device at runtime via RPC. The service will guide
471optimization of stack usage by providing an overview of thread information,
472including thread name, stack bounds, and peak stack usage.
473
474``ThreadSnapshotService`` currently supports peak stack usage capture for
475all running threads (``ThreadSnapshotService::GetPeakStackUsage()``) as well as
476for a specific thread, filtering by name
477(``ThreadSnapshotService::GetPeakStackUsage(name=b"/* thread name */")``).
478Thread information capture relies on the thread iteration facade which will
479**momentarily halt your RTOS**, collect information about running threads, and
480return this information through the service.
481
482RPC service setup
483=================
484To expose a ``ThreadSnapshotService`` in your application, do the following:
485
4861. Create an instance of ``pw::thread::proto::ThreadSnapshotServiceBuffer``.
487   This template takes the number of expected threads, and uses it to properly
488   size buffers required for a ``ThreadSnapshotService``. If no thread count
489   argument is provided, this defaults to ``PW_THREAD_MAXIMUM_THREADS``.
4902. Register the service with your RPC server.
491
492For example:
493
494.. code-block::
495
496   #include "pw_rpc/server.h"
497   #include "pw_thread/thread_snapshot_service.h"
498
499   // Note: You must customize the RPC server setup; see pw_rpc.
500   pw::rpc::Channel channels[] = {
501    pw::rpc::Channel::Create<1>(&uart_output),
502   };
503   Server server(channels);
504
505  // Thread snapshot service builder instance.
506  pw::thread::proto::ThreadSnapshotServiceBuffer</*num threads*/>
507      thread_snapshot_service;
508
509   void RegisterServices() {
510     server.RegisterService(thread_snapshot_service);
511     // Register other services here.
512   }
513
514   void main() {
515     // ... system initialization ...
516
517     RegisterServices();
518
519     // ... start your application ...
520   }
521
522.. c:macro:: PW_THREAD_MAXIMUM_THREADS
523
524  The max number of threads to use by default for thread snapshot service.
525
526.. cpp:function:: constexpr size_t RequiredServiceBufferSize(const size_t num_threads)
527
528  Function provided through the service to calculate buffer sizing. If no
529  argument ``num_threads`` is specified, the function will take ``num_threads``
530  to be ``PW_THREAD_MAXIMUM_THREADS``.
531
532.. attention::
533    Some platforms may only support limited subsets of this service
534    depending on RTOS configuration. **Ensure that your RTOS is configured
535    properly before using this service.** Please see the thread iteration
536    documentation for your backend for more detail on RTOS support.
537
538-----------------------
539pw_snapshot integration
540-----------------------
541``pw_thread`` provides some light, optional integration with pw_snapshot through
542helper functions for populating a ``pw::thread::Thread`` proto. Some of these
543are directly integrated into the RTOS thread backends to simplify the thread
544state capturing for snapshots.
545
546SnapshotStack()
547===============
548The ``SnapshotStack()`` helper captures stack metadata (stack pointer and
549bounds) into a ``pw::thread::Thread`` proto. After the stack bounds are
550captured, execution is passed off to the thread stack collection callback to
551capture a backtrace or stack dump. Note that this function does NOT capture the
552thread name: that metadata is only required in cases where a stack overflow or
553underflow is detected.
554
555Python processor
556================
557Threads captured as a Thread proto message can be dumped or further analyzed
558using using ``pw_thread``'s Python module. This is directly integrated into
559pw_snapshot's processor tool to automatically provide rich thread state dumps.
560
561The ``ThreadSnapshotAnalyzer`` class may also be used directly to identify the
562currently running thread and produce symbolized thread dumps.
563
564.. Warning::
565  Snapshot integration is a work-in-progress and may see significant API
566  changes.
567
568
569.. toctree::
570   :hidden:
571   :maxdepth: 1
572
573   Backends <backends>
574