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