xref: /aosp_15_r20/external/pigweed/seed/0128.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _seed-0128:
2
3=================================
40128: Abstracting Thread Creation
5=================================
6.. seed::
7   :number: 128
8   :name: Abstracting Thread Creation
9   :status: Accepted
10   :proposal_date: 2024-04-25
11   :cl: 206670
12   :authors: Wyatt Hepler
13   :facilitator: Taylor Cramer
14
15-------
16Summary
17-------
18This SEED proposes supporting cross-platform thread creation with ``pw_thread``.
19It introduces APIs for creating a thread without referring to the specific OS /
20``pw_thread`` backend. This dramatically simplifies thread creation for the
21:ref:`vast majority <seed-0128-thread-config-survey>` of production use cases.
22It does so without sacrificing configurability or limiting users in any way.
23
24Key new features
25================
26- ``pw::ThreadAttrs`` describes cross-platform thread attributes:
27
28  - Thread name.
29  - Stack size.
30  - ``pw::ThreadPriority`` to represent a thread's priority.
31
32- ``pw::ThreadContext`` represents the resources required to run one thread.
33- ``pw::Thread`` can be started from ``ThreadAttrs`` and ``ThreadContext``.
34- Additions to the ``pw_thread`` facade to support the new functionality.
35
36pw_thread API overview
37======================
38With these changes, the key pw_thread features are as follows:
39
40.. topic:: Thread creation API
41
42   Key Types
43
44   - ``pw::Thread`` -- Thread handle. The thread might be unstarted, running, or
45     completed.
46   - ``pw::thread::Options`` -- Base class for platform-specific thread options.
47     priority.
48   - ``pw::ThreadAttrs`` -- Generic thread attributes: name, size, priority.
49     may include stack.
50   - ``pw::ThreadPriority`` -- Generic thread priority, with relative modifiers.
51   - ``pw::ThreadContext`` -- Generic thread resources. Depending on backend,
52   - ``pw::ThreadStack`` -- Optionally specify a thread stack separately from
53     the context.
54
55   Key methods
56
57   - ``pw::Thread`` -- Constructor, ``join()``.
58   - ``pw::ThreadAttrs`` -- ``set_name(name)``, ``set_priority(priority)``,
59     ``set_stack_size_bytes(bytes)``.
60   - ``pw::ThreadPriority`` -- ``Low()``, ``Medium()``, ``High()``,
61     ``NextHigher()``, ``NextLower()``, etc.
62
63Example
64=======
65.. code-block:: c++
66
67   // "example_project/threads.h"
68
69   // Define thread attributes for the main thread.
70   constexpr pw::ThreadAttrs kMainThread = pw::ThreadAttrs()
71         .set_name("app")
72         .set_priority(pw::ThreadPriority::Medium()),
73         .set_stack_size_bytes(MY_PROJECT_MAIN_STACK_SIZE_BYTES);
74
75   // Define attributes for another thread, based on kMainThread.
76   constexpr pw::ThreadAttrs kLogThread = pw::ThreadAttrs(kMainThread)
77         .set_name("logging")
78         .set_priority_next_lower();
79
80.. code-block:: c++
81
82   // "example_project/main.cc"
83
84   #include "example_project/threads.h"
85
86   // Declare a thread context that can be used to start a thread.
87   pw::ThreadContext<MY_PROJECT_APP_STACK_SIZE_BYTES> app_thread_context;
88
89   // Declare thread contexts associated with specific ThreadAttrs.
90   pw::ThreadContext<kMainThread> main_thread_context;
91   pw::ThreadContext<kLogThread> log_thread_context;
92
93   // Thread handle for a non-detached thread.
94   pw::Thread app_thread;
95
96   void StartThreads() {
97     // Start the main and logging threads.
98     pw::Thread(main_thread_context, MainThreadBody).detach();
99     pw::Thread(log_thread_context, LoggingThreadBody).detach();
100
101     // Start an app thread that uses the app_thread_context. Since the stack size
102     // is not specified, the full stack provided by app_thread_context is used.
103     app_thread = pw::Thread(
104         app_thread_context, pw::ThreadAttrs().set_name("app 1"), AppThreadBody1);
105   }
106
107   void MainThreadBody() {
108     // Join the "app 1" thread and reuse the app_thread_context for a new thread.
109     app_thread.join();
110     app_thread = pw::Thread(
111         app_thread_context, pw::ThreadAttrs().set_name("app 2"), AppThreadBody2);
112     ...
113   }
114
115----------
116Motivation
117----------
118Pigweed's ``pw_thread`` module does not support cross-platform thread creation.
119Instead, threads must be created by instantiating a
120:cpp:class:`pw::thread::Options` specific to the thread backend. For example, to
121create a FreeRTOS thread, one must instantiate a
122:cpp:class:`pw::thread::freertos::Options` and configure it with a
123:cpp:class:`pw::thread::freertos::Context`
124
125Cross-platform thread creation was intentionally avoided in the ``pw_thread``
126API. It is not possible to specify thread attributes in a truly generic,
127portable way. Every OS/RTOS exposes a different set of thread parameters, and
128settings for one platform may behave completely differently or not exist on
129another.
130
131Cross-platform thread creation may not be possible to do perfectly, but avoiding
132it has significant downsides.
133
134- The current APIs optimize for control at the expense of usability. Thread
135  creation is complex.
136- Developers always have to deal with the full complexity of thread creation,
137  even for simple cases or when just getting started.
138- Users must learn a slightly different API for each RTOS. The full ``Thread``
139  API cannot be documented in one place.
140- Cross-platform code that creates threads must call functions that return
141  ``pw::thread::Options``. Each platform implements the functions as needed.
142  This requires exposing threads in the public API. Libraries such as
143  :ref:`module-pw_system` cannot add internal threads without breaking their
144  users.
145- Code for creating ``pw::thread::Options`` must be duplicated for each
146  platform.
147- Projects avoid writing cross-platform code and tests due to the complexity of
148  thread creation.
149
150``pw_system`` and threads
151=========================
152Currently, running :ref:`module-pw_system` requires writing custom low-level
153code that is aware of both ``pw_system`` and the RTOS it is running on
154(see e.g. `boot.cc
155<https://cs.opensource.google/pigweed/pigweed/+/4d23123c37a33638b2f1ce611423e74d385623ff:targets/stm32f429i_disc1_stm32cube/boot.cc;l=133>`_
156and `target_hooks.cc
157<https://cs.opensource.google/pigweed/pigweed/+/4d23123c37a33638b2f1ce611423e74d385623ff:pw_system/zephyr_target_hooks.cc>`_).
158Enabling cross-platform thread creation would make it easier to use
159``pw_system``. The code for running ``pw_system`` on any target would be the
160same: a single function call in ``main``. The user would no longer have to
161allocate stacks or create :cpp:class:`pw::thread::Options` for ``pw_system``
162threads; this could be managed by ``pw_system`` itself and configured with
163generic ``pw_system`` options if needed.
164
165Cross-platform thread creation also makes it easier for ``pw_system`` users to
166write their own code. Setting up a thread takes just two lines of code and no
167interactions with RTOS-specific APIs. A ``pw_system`` application created this
168way can run on any platform out of the box.
169
170---------------------
171Problem investigation
172---------------------
173Various cross-platform threading APIs exist today.
174
175C++ Standard Library
176====================
177The C++ Standard Library currently provides a limited cross-platform thread
178creation API in ``<thread>``. No thread attributes are exposed; threads are
179created with platform defaults.
180
181An effort is underway to standardize some thread attributes, giving users more
182control over threads while maintaining portability. See `P2019 -- Thread
183attributes
184<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2019r6.pdf>`_ for
185details. The latest proposal exposes the thread name and stack size. Some
186alternatives have also been proposed (`P3072
187<https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3072r2.html>`_).
188
189POSIX
190=====
191POSIX is a portable operating system API. The POSIX thread creation function
192``pthread_create`` takes a pointer to a ``pthread_attr_t`` struct. This struct
193may a support a wide variety thread options that are configured with functions
194such as ``pthread_attr_setstacksize``, ``pthread_attr_setschedpolicy``, and
195others. A thread's name can be set with ``pthread_setname_np``. See `man
196pthreads <https://man7.org/linux/man-pages/man7/pthreads.7.html>`_ for details.
197
198CMSIS-RTOS
199==========
200The `CMSIS-RTOS2 API
201<https://www.keil.com/pack/doc/CMSIS/RTOS2/html/index.html>`_ provides a generic
202RTOS interface intended for use with Arm Cortex devices. CMSIS-RTOS2 is
203implemented by several operating systems, including FreeRTOS and Arm's own Keil
204RTX5.
205
206CMSIS-RTOS2 provides a comprehensive set of thread attributes in its
207`osThreadAttr_t
208<https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__ThreadMgmt.html#structosThreadAttr__t>`_
209struct. It also provides functions for initializing and controlling the
210scheduler, such as `osKernelStart
211<https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__KernelCtrl.html#ga9ae2cc00f0d89d7b6a307bba942b5221>`_.
212
213--------
214Proposal
215--------
216The new cross-platform API does not replace the existing backend-specific thread
217creation APIs. The new API supports most production use cases, but does not
218expose the full capabilities and configuration of all supported RTOSes. It is
219intended to be easy to adopt, while providing a frictionless pathway to the
220current, fully configurable APIs if needed.
221
222With this proposal, per-target thread creation is simply a matter of setting
223variables differently for each target. This removes the need for duplicated code
224for creating platform-specific thread contexts and ``pw::thread::Options``.
225
226Generic thread attributes
227=========================
228This SEED introduces a limited set of cross-platform thread attributes. These
229generic attributes map to a platform-specific :cpp:class:`pw::thread::Options`.
230
231There are three thread attributes:
232
233- Name
234- Stack size
235- Priority
236
237Other attributes may be added in the future, such as dynamic or static
238resource allocation.
239
240Thread attributes are provided only as hints to the backend. Backends should
241respect thread attributes, if possible, but may ignore or adapt them depending
242on the OS's capabilities. Backends cannot fail to create thread because of how
243thread attributes are set, but users may check the backend's capabilities, such
244as whether thread priorities are supported, as needed.
245
246Examples of acceptable adaptations to thread attributes.
247
248- Ignore the thread name and stack size because the underlying API does not
249  support specifying them (e.g. C++'s ``<thread>``).
250- Silently truncate a thread name because the underlying RTOS only supports
251  shorter names.
252- Round up to the minimum required stack size from a smaller requested stack
253  size.
254- Add a fixed amount to a requested stack size to account for RTOS overhead.
255- Dynamically allocate the thread stack if it is above a certain size;
256  statically allocate it otherwise.
257
258.. _seed-0128-thread-config-survey:
259
260Why these thread attributes?
261----------------------------
262A survey of thread creation with Pigweed across a few large, production projects
263found that 99% of their thread configurations can be exactly represented with
264thread name, priority, stack size. The only exception was a single RTOS feature
265used in a few threads in one project.
266
267The proof is in the pudding: ``pw_thread`` users almost never need low-level,
268RTOS-specific threading features. Abstracting these three thread attributes
269dramatically simplifies thread creation, resulting in more portable,
270easier-to-test code. In the rare cases when more control is needed, the existing
271non-portable ``pw_thread`` API is ready to use.
272
273OS / RTOS support for thread attributes
274---------------------------------------
275Most OS APIs support the proposed thread attributes.
276
277.. list-table::
278   :header-rows: 1
279
280   * - OS / API
281     - function
282     - name
283     - stack size
284     - priority type
285     - priority levels
286   * - C++ ``<thread>``
287     - `std::thread <https://en.cppreference.com/w/cpp/thread/thread/thread>`_
288     - none
289     - none
290     - none
291     - none
292   * - POSIX
293     - `pthread_create
294       <https://man7.org/linux/man-pages/man3/pthread_create.3.html>`_
295     - `C string
296       <https://man7.org/linux/man-pages/man3/pthread_setname_np.3.html>`_
297     - `bytes
298       <https://man7.org/linux/man-pages/man3/pthread_attr_setstacksize.3.html>`_
299     - `pthread_attr_setschedparam <https://man7.org/linux/man-pages/man3/pthread_attr_setschedparam.3.html>`_
300     - `at least 32
301       <https://man7.org/linux/man-pages/man2/sched_get_priority_max.2.html>`_
302   * - `CMSIS-RTOS2 / Keil RTX5 <https://arm-software.github.io/CMSIS_6/latest/RTOS2/group__CMSIS__RTOS__ThreadMgmt.html>`_
303     - `osThreadNew <https://arm-software.github.io/CMSIS_6/latest/RTOS2/group__CMSIS__RTOS__ThreadMgmt.html#ga48d68b8666d99d28fa646ee1d2182b8f>`_
304     - `C string
305       <https://arm-software.github.io/CMSIS_6/latest/RTOS2/group__CMSIS__RTOS__ThreadMgmt.html#structosThreadAttr__t>`__
306     - bytes
307     - `osPriority_t
308       <https://arm-software.github.io/CMSIS_6/latest/RTOS2/group__CMSIS__RTOS__ThreadMgmt.html#gad4e3e0971b41f2d17584a8c6837342ec>`_
309     - 56
310   * - `embOS <https://www.segger.com/downloads/embos/UM01001>`_
311     - ``OS_TASK_Create()``
312     - | C string
313       | uses pointer
314     - bytes
315     - ``unsigned int``
316     - 2³²-2
317   * - `FreeRTOS <https://www.freertos.org>`_
318     - `xTaskCreateStatic <https://www.freertos.org/xTaskCreateStatic.html>`_
319     - | C string
320       | copies `configMAX_TASK_NAME_LEN <https://www.freertos.org/a00110.html#configMAX_TASK_NAME_LEN>`_
321     - words
322     - `unsigned int <https://www.freertos.org/RTOS-task-priority.html>`_
323     - | `configMAX_PRIORITIES <https://www.freertos.org/a00110.html#configMAX_PRIORITIES>`_
324       | `≤32 in some configs <https://www.freertos.org/a00110.html#configUSE_PORT_OPTIMISED_TASK_SELECTION>`_
325   * - `NuttX <https://nuttx.apache.org/docs/latest/index.html>`_
326     - | `task_create <https://nuttx.apache.org/docs/latest/reference/user/01_task_control.html#c.task_create>`_
327       | (also POSIX APIs)
328     - C string
329     - bytes
330     - ``int``
331     - `256 <https://github.com/apache/nuttx/blob/0ed714bba4280f98f35cb0df1f9d668099604f97/include/sys/types.h#L81>`_
332   * - `ThreadX <https://github.com/eclipse-threadx/rtos-docs>`_
333     - `tx_thread_create
334       <https://github.com/eclipse-threadx/rtos-docs/blob/80bd9fe9a33fa79257c75629be1b4438b84db7bc/rtos-docs/threadx/chapter4.md#tx_thread_create>`_
335     - `C string
336       <https://github.com/eclipse-threadx/rtos-docs/blob/80bd9fe9a33fa79257c75629be1b4438b84db7bc/rtos-docs/threadx/chapter4.md#example-54>`__
337     - bytes
338     - ``unsigned int`` (``TX_MAX_PRIORITIES - 1``)–0 (0 highest)
339     - `multiple of 32
340       <https://github.com/eclipse-threadx/threadx/blob/80bd9fe9a33fa79257c75629be1b4438b84db7bc/common/inc/tx_api.h#L2143>`_
341   * - ``pw::ThreadContext``
342     - :cpp:type:`pw::Thread`
343     - C string
344     - bytes
345     - custom class
346     - same as underying OS
347
348Creating threads
349================
350The APIs proposed in this SEED streamline thread creation for common use cases,
351while allowing for full configuration when necessary.
352
353Generally, projects should start with the minimum complexity required and
354increase the complexity only if more control is needed. Threads defined in
355upstream Pigweed should start with some configurability to avoid friction in
356downstream projects.
357
358Dynamic threads: "just give me a thread"
359----------------------------------------
360For simple cases, Pigweed will offer a new static ``pw::Thread::Start``
361function.
362
363.. code-block:: c++
364
365   #include "pw_thread/thread.h"
366
367   void CreateThreads() {
368     pw::Thread::Start([] { /* thread body */ ).detach();
369   }
370
371.. admonition:: When should I use ``pw::Thread::Start``?
372
373   - Experimenting
374   - Prototyping
375
376Declare a default thread
377------------------------
378Create a thread with ``DefaultThreadContext`` and default attributes. The
379``pw_thread`` backend starts a thread with a default name, stack size, and
380priority.
381
382.. code-block:: c++
383
384   #include "pw_thread/thread.h"
385
386   pw::DefaultThreadContext context;
387
388   void CreateThreads() {
389     pw::Thread(context, pw::ThreadAttrs(), [] { /* thread body */ }).detach();
390   }
391
392.. admonition:: When should I use default thread contexts?
393
394   - Experimenting
395   - Prototyping
396   - Testing
397   - Getting started
398
399Configurable thread attributes
400------------------------------
401Define a ``pw::ThreadAttrs`` and use it to create threads with
402``pw::ThreadContext<>``. Attributes are configured as needed using the project's
403configuration pattern.
404
405.. code-block:: c++
406
407   #include "pw_thread/thread.h"
408   #include "project/config.h"
409
410   constexpr auto kMyThread = pw::ThreadAttrs()
411       .set_name("my thread")
412       .set_priority(MY_THREAD_PRIORITY)
413       .set_stack_size_bytes(kMyThreadStackSizeBytes);
414
415   pw::ThreadContext<kMyThread> my_thread_context;
416
417   pw::Thread other_thread;
418   pw::ThreadContext<kOtherThreadStackSizeBytes> other_thread_context;
419
420   void StartThreads() {
421     pw::Thread(my_thread_context, [] { /* thread body */ }).detach();
422
423     other_thread = pw::Thread(other_thread_context,
424                               pw::ThreadAttrs().set_name("other"),
425                               OtherThreadBody);
426   }
427
428Example configuration header:
429
430.. code-block:: c++
431
432   // "project/config.h"
433
434   // Configurable thread priority. Can be changed by defining
435   // MY_THREAD_PRIORITY in the build system.
436   #ifndef MY_THREAD_PRIORITY
437   #define MY_THREAD_PRIORITY pw::ThreadPriority::High()
438   #endif  // MY_THREAD_PRIORITY
439
440   // Configuration may be based on the target platform.
441   #if BUILDING_FOR_PLATFORM_A
442   inline constexpr size_t kMyThreadStackSizeBytes = 2048;
443   inline constexpr size_t kOtherThreadStackSizeBytes = 1024;
444   #else
445   inline constexpr size_t kMyThreadStackSizeBytes = 1536;
446   inline constexpr size_t kOtherThreadStackSizeBytes = 512;
447   #endif  // BUILDING_FOR_PLATFORM_A
448
449.. admonition:: When should I use configurable thread attributes?
450
451   - Pigweed upstream development
452   - Production project development
453
454Platform-specific thread creation
455---------------------------------
456In the rare case that platform-specific thread configuration is required,
457provide a function that returns ``NativeOptions`` or ``const Options&`` and use
458it to create a thread. The function may be a facade, so each target can
459implement it differently. Projects may provide a default implementation of the
460function that uses ``pw::ThreadAttrs``.
461
462This approach is equivalent to the original non-portable ``pw_thread`` creation
463pattern, optionally with a ``pw::ThreadAttrs``-based default implementation of
464the function. This approach is only necessary for threads that specifically
465require non-portable features. Other threads should continue to use
466``pw::ThreadAttrs``.
467
468.. code-block:: c++
469
470   #include "pw_thread/thread.h"
471   #include "project/config.h"
472
473   // This function returns a `pw::thread::Options` for creating a thread.
474   pw::thread::NativeOptions GetThreadOptions();
475
476   // Optionally, provide a default implementation of `GetThreadOptions()` that
477   // uses `pw::ThreadAttrs`.
478   #if !PROJECT_CFG_THREAD_CUSTOM_OPTIONS
479
480   pw::thread::NativeOptions GetThreadOptions() {
481     static constinit pw::ThreadContext<project::cfg::kThreadStackSizeHintBytes> context;
482     return pw::thread::GetNativeOptions(
483         context, pw::ThreadAttrs().set_name("thread name"));
484   }
485
486   #endif  // !PROJECT_CFG_THREAD_CUSTOM_OPTIONS
487
488   // Call `GetThreadOptions()` to create a thread.
489   void CreateThreads() {
490     pw::Thread(GetThreadOptions(), [] { /* thread body */ }).detach();
491   }
492
493Example configuration header:
494
495.. code-block:: c++
496
497   // project/config.h
498
499   // Set to 1 to implement `GetThreadOptions()` and provide fully custom
500   // `pw::thread::Options` for the platform.
501   #ifndef PROJECT_CFG_THREAD_CUSTOM_OPTIONS
502   #define PROJECT_CFG_THREAD_CUSTOM_OPTIONS 0
503   #endif  // PROJECT_CFG_THREAD_CUSTOM_OPTIONS
504
505   // Stack size setting for the default thread options.
506   #ifndef PROJECT_CFG_THREAD_STACKS_SIZE_HINT
507   #define PROJECT_CFG_THREAD_STACKS_SIZE_HINT 2048
508   #endif  // PROJECT_CFG_THREAD_STACKS_SIZE_HINT
509
510   namespace project::cfg {
511
512   inline constexpr size_t kThreadStackSizeHintBytes = PROJECT_CFG_THREAD_STACKS_SIZE_HINT;
513
514   }  // namespace project::cfg
515
516This approach is not recommended as a starting point. It adds complexity that is
517unlikely to be necessary. Most projects should start with configurable
518``ThreadAttrs`` and add switch to platform-specific thread configuration only
519for threads that need it.
520
521.. admonition:: When should I use platform-specific thread creation?
522
523   - Pigweed upstream development, if a downstream user specifically requires
524     platform-specific thread features for a thread defined by Pigweed.
525   - Production project development that requires platform-specific thread
526     features.
527
528C++ implementation details
529==========================
530
531Facade additions
532-----------------
533This proposal adds a few items to the ``pw_thread`` facade:
534
535- Aliases for the native context types wrapped by ``pw::ThreadContext``.
536- Information about the range of supported thread priorities used by
537  ``pw::ThreadPriority``.
538- Alias for the native ``pw::thread::Options`` type.
539- Function that maps ``pw::ThreadContext`` and ``pw::ThreadAttrs`` to native
540  ``pw::thread::Options``.
541
542These features are used by ``pw_thread`` classes, not end users.
543
544.. code-block:: c++
545
546   // pw_thread_backend/thread_native.h
547
548   namespace pw::thread::backend {
549
550   // Native, non-templated context (resources).
551   using NativeContext = /* implementation-defined */;
552
553   // Thread context with a stack size hint. Must derive from or be the same
554   // type as `NativeContext`. Must be default constructible.
555   template <size_t kStackSizeHintBytes>
556   using NativeContextWithStack = /* implementation-defined */;
557
558   // Stack size to use when unspecified; 0 for platforms that do not support
559   // defining the stack size.
560   inline constexpr size_t kDefaultStackSizeBytes = /* implementation-defined */;
561
562   // Define the range of thread priority values. These values may represent a
563   // subset of priorities supported by the OS. The `kHighestPriority` may be
564   // numerically higher or lower than `kLowestPriority`, depending on the OS.
565   // Backends that do not support priorities must set `kLowestPriority` and
566   // `kHighestPriority` to the same value, and should use `int` for
567   // `NativePriority`.
568   using NativePriority = /* implementation-defined */;
569   inline constexpr NativePriority kLowestPriority = /* implementation-defined */;
570   inline constexpr NativePriority kHighestPriority = /* implementation-defined */;
571
572   // Native options class derived from pw::thread::Options.
573   using NativeOptions = /* implementation-defined */;
574
575   // Converts cross-platform ThreadAttrs to NativeOptions. May be defined
576   // in ``pw_thread_backend/thread_inline.h`` or in a .cc file.
577   NativeOptions GetNativeOptions(NativeContext& context,
578                                  const ThreadAttrs& attributes);
579
580   }  // namespace pw::thread::backend
581
582``pw_thread_stl`` example implementation:
583
584.. code-block:: c++
585
586   namespace pw::thread::backend {
587
588   using NativeContext = pw::thread::stl::Context;
589
590   // Ignore the stack size since it's not supported.
591   template <size_t>
592   using NativeContextWithStack = pw::thread::stl::Context;
593
594   inline constexpr size_t kDefaultStackSizeBytes = 0;
595
596   using NativePriority = int;
597   inline constexpr NativePriority kLowestPriority = 0;
598   inline constexpr NativePriority kHighestPriority = 0;
599
600   using NativeOptions = pw::thread::stl::Options;
601
602   inline NativeOptions GetNativeOptions(NativeContext&, const ThreadAttrs&) {
603     return pw::thread::stl::Options();
604   }
605
606   }  // namespace pw::thread::backend
607
608``pw_thread_freertos`` example implementation:
609
610.. code-block:: c++
611
612   namespace pw::thread::backend {
613
614   using NativeContext = pw::thread::freertos::StaticContext;
615
616   // Convert bytes to words, rounding up.
617   template <size_t kStackSizeBytes>
618   using NativeContextWithStack = pw::thread::stl::StaticContextWithStack<
619       (kStackSizeBytes + sizeof(StackType_t) - 1) / sizeof(StackType_t)>;
620
621   inline constexpr size_t kDefaultStackSizeBytes =
622       pw::thread::freertos::config::kDefaultStackSizeWords;
623
624   using NativePriority = UBaseType_t;
625   inline constexpr NativePriority kLowestPriority = tskIDLE_PRIORITY;
626   inline constexpr NativePriority kHighestPriority = configMAX_PRIORITIES - 1;
627
628   using NativeOptions = pw::thread::freertos::Options;
629
630   inline NativeOptions GetNativeOptions(NativeContext& context,
631                                         const ThreadAttrs& attrs) {
632     return pw::thread::freertos::Options()
633         .set_static_context(context),
634         .set_name(attrs.name())
635         .set_priority(attrs.priority().native())
636   }
637
638   }  // namespace pw::thread::backend
639
640``ThreadPriority``
641------------------
642Different OS APIs define priorities very differently. Some support a few
643priority levels, others support the full range of a ``uint32_t``. For some, 0 is
644the lowest priority and for others it is the highest. And changing the OS's
645scheduling policy might changes how threads are scheduled without changing their
646priorities.
647
648``pw::ThreadPriority`` represents thread priority precisely but abstractly. It
649supports the following:
650
651- Represent the full range of priorities supported by the underlying OS.
652- Set priorities in absolute terms that map to OS priority ranges in a
653  reasonable way.
654- Set priorities relative to one another.
655- Check that priorities are actually higher or lower than one another on a given
656  platform at compile time.
657- Check if the backend supports thread priorities at all.
658
659Many projects will be able to define a single priority set for all platforms.
660The priorities may translate differently to each platforms, but this may not
661matter. If a single set of priorities does not work for all platforms,
662priorities can be configured per platform, like other attributes.
663
664Here is a high-level overview of the class:
665
666.. code-block:: c++
667
668   namespace pw {
669
670   class ThreadPriority {
671    public:
672     // True if the backend supports different priority levels.
673     static constexpr bool IsSupported();
674
675     // Named priorities. These priority levels span the backend's supported
676     // priority range.
677     //
678     // The optional `kPlus` template parameter returns a priority the specified
679     // number of levels higher than the named priority, but never exceeding the
680     // priority of the next named level, if supported by the backend.
681     static constexpr ThreadPriority VeryLow<unsigned kPlus = 0>();
682     static constexpr ThreadPriority Low<unsigned kPlus = 0>();
683     static constexpr ThreadPriority MediumLow<unsigned kPlus = 0>();
684     static constexpr ThreadPriority Medium<unsigned kPlus = 0>();
685     static constexpr ThreadPriority MediumHigh<unsigned kPlus = 0>();
686     static constexpr ThreadPriority High<unsigned kPlus = 0>();
687     static constexpr ThreadPriority VeryHigh<unsigned kPlus = 0>();
688
689     // Refers to the lowest or highest priority supported by the OS.
690     static constexpr ThreadPriority Lowest<unsigned kPlus = 0>();
691     static constexpr ThreadPriority Highest();
692
693     // Returns the ThreadPriority with next distinct higher or lower value. If
694     // the priority is already the highest/lowest, returns the same value.
695     constexpr ThreadPriority NextLower();
696     constexpr ThreadPriority NextHigher();
697
698     // Returns the ThreadPriority with next distinct higher or lower value.
699     // Asserts that the priority is not already the highest/lowest.
700     constexpr ThreadPriority NextLowerChecked();
701     constexpr ThreadPriority NextHigherChecked();
702
703     // ThreadPriority supports comparison. This makes it possible, for example,
704     // to static_assert that one priority is higher than another in the
705     // backend.
706     constexpr bool operator==(const ThreadPriority&);
707     ...
708
709     // Access the native thread priority type. These functions may be helpful
710     // when ThreadPriority is configured separately for each platform.
711     using native_type = backend::NativeThreadPriority;
712
713     static constexpr FromNative(native_type native_priority);
714
715     native_type native() const;
716   };
717
718   }  // namespace pw
719
720Example uses:
721
722.. code-block:: c++
723
724   // Named priorities are spread over the backend's supported priority range.
725   constexpr pw::ThreadPriority kThreadOne = ThreadPriority::Low();
726   constexpr pw::ThreadPriority kThreadTwo = ThreadPriority::Medium();
727
728   // Define a priority one higher than Medium, but never equal to or greater
729   // than the next named priority, MediumHigh, if possible in the given
730   // backend.
731   constexpr pw::ThreadPriority kThreadThree = ThreadPriority::Medium<1>();
732
733   // Set the priority exactly one backend priority level higher than
734   // kThreadThree, if supported by the backend.
735   constexpr pw::ThreadPriority kThreadFour = kThreadThree.NextHigher();
736
737   static_assert(!ThreadPriority::IsSupported() || kThreadThree < kThreadFour);
738
739.. tip::
740
741  It is recommended that projects pick a starting priority level (e.g.
742  ``ThreadPriority::Lowest().NextHigher()``) and define all priorities relative
743  to it.
744
745Mapping OS priorities to named priorities
746^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
747If thread priorities are not supported, all named priorities are the same level.
748
749If fewer than 7 levels are supported by the backend, some named levels map to
750the same OS priority. For example, if there are only 3 priority levels
751supported, then ``VeryLow == Low``, ``MediumLow == Medium == MediumHigh``, and
752``High == VeryHigh``.
753
754For backends that support 7 or more priority levels, each named priority level
755is guaranteed to map to a unique OS priority.
756
757``ThreadAttrs``
758---------------
759The ``ThreadAttrs`` class represents generic thread attributes. It is a
760cross-platform version of :cpp:class:`pw::thread::Options`.
761
762.. code-block:: c++
763
764   namespace pw {
765
766   // Generic thread attributes.
767   class ThreadAttrs {
768    public:
769     // Initializes ThreadAttrs to their backend-defined defaults.
770     constexpr ThreadAttrs();
771
772     // ThreadAttrs can be copied to share properties between threads.
773     constexpr ThreadAttrs(const ThreadAttrs&) = default;
774     constexpr ThreadAttrs& operator=(const ThreadAttrs&) = default;
775
776     // Name hint as a null-terminated string; never null.
777     constexpr const char* name() const;
778     constexpr ThreadAttrs& set_name(const char* name);
779
780     constexpr Priority priority() const;
781     constexpr ThreadAttrs& set_priority(Priority priority);
782
783     // Increment or decrement the priority to set task priorities relative to
784     // one another.
785     constexpr ThreadAttrs& set_priority_next_higher();
786     constexpr ThreadAttrs& set_priority_next_lower();
787
788     constexpr size_t stack_size_bytes() const;
789     constexpr ThreadAttrs& set_stack_size_bytes(size_t stack_size_bytes);
790   };
791
792   }  // namespace pw
793
794``ThreadAttrs`` may be defined at runtime or as ``constexpr`` constants.
795Projects may find it helpful to define ``ThreadAttrs`` in a centralized
796location.
797
798.. code-block:: c++
799
800   #include "pw_thread/attrs.h"
801   #include "my_project/config.h"
802
803   namespace my_project {
804
805   // Global list of thread attributes.
806
807   inline constexpr auto kThreadOne = pw::ThreadAttrs()
808       .set_name("thread one")
809       .set_stack_size_bytes(1024)
810       .set_priority(pw::ThreadPriority::Medium());
811
812   inline constexpr auto kThreadTwo = pw::ThreadAttrs(kThreadOne)
813       .set_name("thread two");
814
815   inline constexpr auto kImportantThread = pw::ThreadAttrs()
816       .set_name("important!")
817       .set_stack_size_bytes(IMPORTANT_THREAD_STACK_SIZE_BYTES)
818       .set_priority(IMPORTANT_THREAD_PRIORITY);
819
820   inline constexpr auto kLessImportantThread = pw::ThreadAttrs()
821       .set_name("also important!")
822       .set_stack_size_bytes(IMPORTANT_THREAD_STACK_SIZE_BYTES)
823       .set_priority(kImportantThread.priority().NextLower());
824
825   static_assert(
826       !pw::ThreadPriority::IsSupported() ||
827       kImportantThread.priority() > kLessImportantThread.priority(),
828       "If the platform supports priorities, ImportantThread must be higher "
829       "priority than LessImportantThread");
830
831   }  // namespace my_project
832
833``ThreadContext``
834-----------------
835``pw::ThreadContext`` represents the resources required to run one thread.
836This may include platform-specific handles, a statically allocated thread
837control block (TCB), or the thread's stack. If platforms do not require manual
838allocation for threads, ``pw::ThreadContext`` may be empty.
839
840``ThreadContext`` is a generic wrapper around a backend-defined object. It
841prevents unintentional access of backend-specific features on the native object.
842
843``ThreadContext`` objects may be reused if their associated thread has been
844joined.
845
846``ThreadContext`` takes a few forms:
847
848- ``ThreadContext<kStackSizeHintBytes>`` -- Context with internally allocated
849  thread stack.
850- ``ThreadContext<kThreadAttrs>`` -- Context associated with a set of
851  ``ThreadAttrs``. Uses internally or externally allocated stack based on the
852  ``ThreadAttrs``.
853- ``ThreadContext<>`` -- Context with a runtime-provided ``ThreadStack``.
854
855.. code-block:: c++
856
857   namespace pw {
858
859   // Represents the resources required for one thread. May include OS data
860   // structures, the thread stack, or be empty, depending on the platform.
861   //
862   // ThreadContext may be reused or deleted if the associated thread is
863   // joined.
864   template <auto>
865   class ThreadContext;
866
867   // ThreadContext with integrated stack.
868   template <size_t kStackSizeHintBytes,
869             size_t kAlignmentBytes = alignof(std::max_align_t)>
870   class ThreadContext {
871    public:
872     constexpr ThreadContext() = default;
873
874    private:
875     backend::NativeContextWithStack<kStackSizeHintBytes, kAlignmentBytes> native_context_;
876   };
877
878   // Alias for ThreadContext with the backend's default stack size.
879   using DefaultThreadContext = ThreadContext<backend::kDefaultStackSizeBytes>;
880
881   // Declares a ThreadContext that is associated with a specific set of thread
882   // attributes. Internally allocates the stack if the stack size hint is set.
883   // The ThreadContext may be reused if the associated thread is joined, but
884   // all threads use the same ThreadAttrs.
885   template <const ThreadAttrs& kAttributes>
886   class ThreadContext {
887    private:
888     ThreadContext<kAttributes.stack_size_bytes()> context_;
889   };
890
891   }  // namespace pw
892
893   #include "pw_thread_backend/thread_inline.h"
894
895``ThreadStack``
896---------------
897Represents a thread stack of the specified size. The object may be empty if the
898backends dynamically allocate stacks.
899
900.. code-block:: c++
901
902   namespace pw {
903
904   template <size_t kStackSizeBytes>
905   class ThreadStack {
906    private:
907     backend::NativeThreadStack<kStackSizeBytes> native_stack_;
908   };
909
910   }  // namespace pw
911
912``ThreadStack`` may specified separately from the ``ThreadContext`` if users
913have need to declare stacks in different sections or want to keep them separate
914from other items in the ``ThreadContext``. The ``ThreadStack`` is set on the
915``ThreadAttrs`` instead of the stack size:
916
917.. code-block:: c++
918
919   STACK_SECTION alignas(256) constinit ThreadStack<kAppStackSizeBytes> kMainStack;
920
921   constexpr pw::ThreadAttrs kMainThread = pw::ThreadAttrs()
922       .set_name("MainThread")
923       .set_stack(kMainStack)
924       .set_priority(kMainPriority);
925
926   ThreadContext<kMainThread> kMainThreadContext;
927
928   void RunThread() {
929     pw::Thread(kMainThreadContext, [] { /* thread body */ }).detach();
930   }
931
932``ThreadContext`` objects that are not associated with a ``ThreadAttrs`` work
933similarly:
934
935.. code-block:: c++
936
937   STACK_SECTION alignas(256) constinit ThreadStack<kAppStackSizeBytes> kAppStack;
938
939   ThreadContext<> kAppThreadContext;
940
941   void RunThreads() {
942     pw::Thread thread(kAppThreadContext,
943                       pw::ThreadAttrs().set_stack(kAppStack).set_name("T1"),
944                       [] { /* thread body */ });
945     thread.join()
946
947     pw::Thread thread(kAppThreadContext,
948                       pw::ThreadAttrs().set_stack(kAppStack).set_name("T2"),
949                       [] { /* thread body */ });
950     thread.join();
951   }
952
953The ``STACK_SECTION`` macro would be provided by a config header:
954
955.. code-block:: c++
956
957   #if BUILDING_FOR_DEVICE_A
958   #define STACK_SECTION PW_PLACE_IN_SECTION(".thread_stacks")
959   #else  // building for device B
960   #define STACK_SECTION  // section doesn't matter
961   #endif  // BUILDING_FOR_DEVICE_A
962
963``Thread`` additions
964--------------------
965``pw::Thread`` will accept ``ThreadContext`` and ``ThreadAttrs``.
966
967.. code-block:: c++
968
969   class Thread {
970     // Existing constructor.
971     Thread(const Options& options, Function<void()>&& entry)
972
973     // Creates a thread with a ThreadContext associated with a ThreadAttrs.
974     template <const ThreadAttrs& kAttributes>
975     Thread(ThreadContext<kAttributes>& context, Function<void()>&& entry);
976
977     // Creates a thread from attributes passed in a template parameter.
978     template <const ThreadAttrs& kAttributes, size_t kStackSizeHintBytes>
979     Thread(ThreadContext<kStackSizeHintBytes>& context,
980            Function<void()>&& entry);
981
982     // Creates a thread from context and attributes. Performs a runtime check
983     // that the ThreadContext's stack is large enough, which can be avoided by
984     // using one of the other constructors.
985     template <size_t kStackSizeHintBytes>
986     Thread(ThreadContext<kStackSizeHintBytes>& context,
987            const ThreadAttrs& attributes,
988            Function<void()>&& entry);
989
990     // Creates a thread with the provided context and attributes. The
991     // attributes have a ThreadStack set.
992     Thread(ThreadContext<>& context,
993            const ThreadAttrs& attributes,
994            Function<void()>&& entry);
995
996Dynamic thread creation function
997--------------------------------
998The ``pw::Thread::Start`` function starts a thread as simply as possible.  It
999starts returns a ``pw::Thread`` that runs a user-provided function. Users may
1000optionally provide ``pw::ThreadAttrs``.
1001
1002``pw::Thread::Start`` is implemented with a new, separate facade. The backend
1003may statically or dynamically allocate resources. A default backend that
1004statically allocates resources for a fixed number of threads will be provided in
1005upstream Pigweed.
1006
1007.. code-block:: c++
1008
1009   namespace pw {
1010
1011   class Thread {
1012     ...
1013
1014     // Starts running the thread_body in a separate thread. The thread is
1015     // allocated and managed by the backend.
1016     template <typename Function, typename... Args>
1017     static Thread Start(Function&& thread_body, Args&&... args);
1018
1019     template <typename Function, typename... Args>
1020     static Thread Start(const pw::ThreadAttrs& attributes, Function&& thread_body, Args&&... args);
1021   };
1022
1023   }  // namespace pw
1024