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--------------- 149Thread Creation 150--------------- 151The class ``pw::thread::Thread`` can represent a single thread of execution. 152Threads allow multiple functions to execute concurrently. 153 154The Thread's API is C++11 STL 155`std::thread <https://en.cppreference.com/w/cpp/thread/thread>`_ like, meaning 156the object is effectively a thread handle and not an object which contains the 157thread's context. Unlike ``std::thread``, the API requires 158``pw::thread::Options`` as an argument and is limited to only work with 159``pw::thread::ThreadCore`` objects and functions which match the 160``pw::thread::Thread::ThreadRoutine`` signature. 161 162We recognize that the C++11's STL ``std::thread``` API has some drawbacks where 163it is easy to forget to join or detach the thread handle. Because of this, we 164offer helper wrappers like the ``pw::thread::DetachedThread``. Soon we will 165extend this by also adding a ``pw::thread::JoiningThread`` helper wrapper which 166will also have a lighter weight C++20 ``std::jthread`` like cooperative 167cancellation contract to make joining safer and easier. 168 169Threads may begin execution immediately upon construction of the associated 170thread object (pending any OS scheduling delays), starting at the top-level 171function provided as a constructor argument. The return value of the 172top-level function is ignored. The top-level function may communicate its 173return value by modifying shared variables (which may require 174synchronization, see :ref:`module-pw_sync`) 175 176Thread objects may also be in the state that does not represent any thread 177(after default construction, move from, detach, or join), and a thread of 178execution may be not associated with any thread objects (after detach). 179 180No two Thread objects may represent the same thread of execution; Thread is 181not CopyConstructible or CopyAssignable, although it is MoveConstructible and 182MoveAssignable. 183 184.. list-table:: 185 186 * - *Supported on* 187 - *Backend module* 188 * - FreeRTOS 189 - :ref:`module-pw_thread_freertos` 190 * - ThreadX 191 - :ref:`module-pw_thread_threadx` 192 * - embOS 193 - :ref:`module-pw_thread_embos` 194 * - STL 195 - :ref:`module-pw_thread_stl` 196 * - Zephyr 197 - Planned 198 * - CMSIS-RTOS API v2 & RTX5 199 - Planned 200 201Module Configuration Options 202============================ 203The following configurations can be adjusted via compile-time configuration of 204this module, see the 205:ref:`module documentation <module-structure-compile-time-configuration>` for 206more details. 207 208.. c:macro:: PW_THREAD_CONFIG_LOG_LEVEL 209 210 The log level to use for this module. Logs below this level are omitted. 211 212Options 213======= 214The ``pw::thread::Options`` contains the parameters or attributes needed for a 215thread to start. 216 217Pigweed does not generalize options, instead we strive to give you full control 218where we provide helpers to do this. 219 220Options are backend specific and ergo the generic base class cannot be 221directly instantiated. 222 223The attributes which can be set through the options are backend specific 224but may contain things like the thread name, priority, scheduling policy, 225core/processor affinity, and/or an optional reference to a pre-allocated 226Context (the collection of memory allocations needed for a thread to run). 227 228Options shall NOT permit starting as detached, this must be done explicitly 229through the Thread API. 230 231Options must not contain any memory needed for a thread to run (TCB, 232stack, etc.). The Options may be deleted or re-used immediately after 233starting a thread. 234 235Please see the thread creation backend documentation for how their Options work. 236 237Portable Thread Creation 238======================== 239Due to the fact that ``pw::thread::Options`` cannot be created in portable code, 240some extra work must be done in order to permit portable thread creation. 241Namely, a reference to the portable ``pw::thread::Options`` base class interface 242must be provided through a header or extern which points to an instantiation in 243non-portable code. 244 245This can be most easily done through a facade and set of backends. This approach 246can be powerful; enabling multithreaded unit/integration testing which can run 247on both the host and on a device with the device's exact thread options. 248 249Alternatively, it can also be be injected at build time by instantiating backend 250specific build rule which share the same common portable source file(s) but 251select backend specific source files and/or dependencies which provide the 252non-portable option instantiations. 253 254As an example, let's say we want to create a thread on the host and on a device 255running FreeRTOS. They could use a facade which contains a ``threads.h`` header 256with the following contents: 257 258.. code-block:: cpp 259 260 // Contents of my_app/threads.h 261 #pragma once 262 263 #include "pw_thread/options.h" 264 265 namespace my_app { 266 267 const pw::thread::Options& HellowWorldThreadOptions(); 268 269 } // namespace my_app 270 271This could then be backed by two different backend implementations based on 272the thread backend. For example for the STL the backend's ``stl_threads.cc`` 273source file may look something like: 274 275.. code-block:: cpp 276 277 // Contents of my_app/stl_threads.cc 278 #include "my_app/threads.h" 279 #include "pw_thread_stl/options.h" 280 281 namespace my_app { 282 283 const pw::thread::Options& HelloWorldThreadOptions() { 284 static constexpr auto options = pw::thread::stl::Options(); 285 return options; 286 } 287 288 } // namespace my_app 289 290While for FreeRTOS the backend's ``freertos_threads.cc`` source file may look 291something like: 292 293.. code-block:: cpp 294 295 // Contents of my_app/freertos_threads.cc 296 #include "FreeRTOS.h" 297 #include "my_app/threads.h" 298 #include "pw_thread_freertos/context.h" 299 #include "pw_thread_freertos/options.h" 300 #include "task.h" 301 302 namespace my_app { 303 304 StaticContextWithStack<kHelloWorldStackWords> hello_world_thread_context; 305 const pw::thread::Options& HelloWorldThreadOptions() { 306 static constexpr auto options = 307 pw::thread::freertos::Options() 308 .set_name("HelloWorld") 309 .set_static_context(hello_world_thread_context) 310 .set_priority(kHelloWorldThreadPriority); 311 return options; 312 } 313 314 } // namespace my_app 315 316 317Detaching & Joining 318=================== 319The ``Thread::detach()`` API is always available, to let you separate the 320thread of execution from the thread object, allowing execution to continue 321independently. 322 323The joining API, more specifically ``Thread::join()``, is conditionally 324available depending on the selected backend for thread creation and how it is 325configured. The backend is responsible for providing the 326``PW_THREAD_JOINING_ENABLED`` macro through 327``pw_thread_backend/thread_native.h``. This ensures that any users which include 328``pw_thread/thread.h`` can use this macro if needed. 329 330Please see the selected thread creation backend documentation for how to 331enable joining if it's not already enabled by default. 332 333.. Warning:: 334 A constructed ``pw::thread::Thread`` which represents a thread of execution 335 must be EITHER detached or joined, else the destructor will assert! 336 337DetachedThread 338============== 339To make it slightly easier and cleaner to spawn detached threads without having 340to worry about thread handles, a wrapper ``DetachedThread()`` function is 341provided which creates a ``Thread`` and immediately detaches it. For example 342instead of: 343 344.. code-block:: cpp 345 346 Thread(options, foo).detach(); 347 348You can instead use this helper wrapper to: 349 350.. code-block:: cpp 351 352 DetachedThread(options, foo); 353 354The arguments are directly forwarded to the Thread constructor and ergo exactly 355match the Thread constuctor arguments for creating a thread of execution. 356 357 358ThreadRoutine & ThreadCore 359========================== 360Threads must either be invoked through a 361``pw::thread::Thread::ThreadRoutine``` style function or implement the 362``pw::thread::ThreadCore`` interface. 363 364.. code-block:: cpp 365 366 namespace pw::thread { 367 368 // This function may return. 369 using Thread::ThreadRoutine = void (*)(void* arg); 370 371 class ThreadCore { 372 public: 373 virtual ~ThreadCore() = default; 374 375 // The public API to start a ThreadCore, note that this may return. 376 void Start() { Run(); } 377 378 private: 379 // This function may return. 380 virtual void Run() = 0; 381 }; 382 383 } // namespace pw::thread; 384 385 386To use the ``pw::thread::Thread::ThreadRoutine``, your function must have the 387following signature: 388 389.. code-block:: cpp 390 391 void example_thread_entry_function(void *arg); 392 393 394To invoke a member method of a class a static lambda closure can be used 395to ensure the dispatching closure is not destructed before the thread is 396done executing. For example: 397 398.. code-block:: cpp 399 400 class Foo { 401 public: 402 void DoBar() {} 403 }; 404 Foo foo; 405 406 static auto invoke_foo_do_bar = [](void *void_foo_ptr) { 407 // If needed, additional arguments could be set here. 408 static_cast<Foo*>(void_foo_ptr)->DoBar(); 409 }; 410 411 // Now use the lambda closure as the thread entry, passing the foo's 412 // this as the argument. 413 Thread thread(options, invoke_foo_do_bar, &foo); 414 thread.detach(); 415 416 417Alternatively, the aforementioned ``pw::thread::ThreadCore`` interface can be 418be implemented by an object by overriding the private 419``void ThreadCore::Run();`` method. This makes it easier to create a thread, as 420a static lambda closure or function is not needed to dispatch to a member 421function without arguments. For example: 422 423.. code-block:: cpp 424 425 class Foo : public ThreadCore { 426 private: 427 void Run() override {} 428 }; 429 Foo foo; 430 431 // Now create the thread, using foo directly. 432 Thread(options, foo).detach(); 433 434.. Warning:: 435 Because the thread may start after the pw::Thread creation, an object which 436 implements the ThreadCore MUST meet or exceed the lifetime of its thread of 437 execution! 438 439----------------------- 440pw_snapshot integration 441----------------------- 442``pw_thread`` provides some light, optional integration with pw_snapshot through 443helper functions for populating a ``pw::thread::Thread`` proto. Some of these 444are directly integrated into the RTOS thread backends to simplify the thread 445state capturing for snapshots. 446 447SnapshotStack() 448=============== 449The ``SnapshotStack()`` helper captures stack metadata (stack pointer and 450bounds) into a ``pw::thread::Thread`` proto. After the stack bounds are 451captured, execution is passed off to the thread stack collection callback to 452capture a backtrace or stack dump. Note that this function does NOT capture the 453thread name: that metadata is only required in cases where a stack overflow or 454underflow is detected. 455 456Python processor 457================ 458Threads captured as a Thread proto message can be dumped or further analyzed 459using using ``pw_thread``'s Python module. This is directly integrated into 460pw_snapshot's processor tool to automatically provide rich thread state dumps. 461 462The ``ThreadSnapshotAnalyzer`` class may also be used directly to identify the 463currently running thread and produce symbolized thread dumps. 464 465.. Warning:: 466 Snapshot integration is a work-in-progress and may see significant API 467 changes. 468