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