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