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