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