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