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