xref: /aosp_15_r20/external/pigweed/pw_thread_threadx/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_thread_threadx:
2
3=================
4pw_thread_threadx
5=================
6This is a set of backends for pw_thread based on ThreadX.
7
8.. Warning::
9   This module is still under construction, the API is not yet stable.
10
11-----------------------
12Thread Creation Backend
13-----------------------
14A backend for ``pw::Thread`` is offered using ``tx_thread_create``.  Optional
15joining support is enabled via an ``TX_EVENT_FLAGS_GROUP`` in each thread's
16context.
17
18This backend permits users to start threads where contexts must be explicitly
19allocated and passed in as an option. As a quick example, a detached thread can
20be created as follows:
21
22.. code-block:: cpp
23
24   #include "pw_thread/detached_thread.h"
25   #include "pw_thread_threadx/config.h"
26   #include "pw_thread_threadx/context.h"
27   #include "pw_thread_threadx/options.h"
28   #include "tx_api.h"
29
30   constexpr UINT kFooPriority =
31       pw::thread::threadx::config::kDefaultPriority;
32   constexpr ULONG kFooTimeSliceInterval =
33       pw::thread::threadx::config::kDefaultTimeSliceInterval;
34   constexpr size_t kFooStackSizeWords =
35       pw::thread::threadx::config::kDefaultStackSizeWords;
36
37   pw::thread::threadx::ContextWithStack<kFooStackSizeWords>
38       example_thread_context;
39   void StartExampleThread() {
40     pw::thread::DetachedThread(
41         pw::thread::threadx::Options()
42             .set_name("example_thread")
43             .set_priority(kFooPriority)
44             .set_time_slice_interval(kFooTimeSliceInterval)
45             .set_context(example_thread_context),
46         example_thread_function);
47   }
48
49.. list-table::
50
51   * - :ref:`module-pw_thread` Facade
52     - Backend Target
53     - Description
54   * - ``pw_thread:id``
55     - ``pw_thread_threadx:id``
56     - Thread identification.
57   * - ``pw_thread:yield``
58     - ``pw_thread_threadx:yield``
59     - Thread scheduler yielding.
60   * - ``pw_thread:sleep``
61     - ``pw_thread_threadx:sleep``
62     - Thread scheduler sleeping.
63   * - ``pw_thread:thread``
64     - ``pw_thread_threadx:thread``
65     - Thread creation.
66
67Module Configuration Options
68============================
69The following configurations can be adjusted via compile-time configuration of
70this module, see the
71:ref:`module documentation <module-structure-compile-time-configuration>` for
72more details.
73
74.. c:macro:: PW_THREAD_THREADX_CONFIG_JOINING_ENABLED
75
76   Whether thread joining is enabled. By default this is disabled.
77
78   We suggest only enabling this when thread joining is required to minimize
79   the RAM and ROM cost of threads.
80
81   Enabling this grows the RAM footprint of every pw::Thread as it adds a
82   TX_EVENT_FLAGS_GROUP to every thread's pw::thread::threadx::Context. In
83   addition, there is a minute ROM cost to construct and destroy this added
84   object.
85
86   PW_THREAD_JOINING_ENABLED gets set to this value.
87
88.. c:macro:: PW_THREAD_THREADX_CONFIG_DEFAULT_STACK_SIZE_WORDS
89
90   The default stack size in words. By default this uses the minimal ThreadX
91   stack size.
92
93.. c:macro:: PW_THREAD_THREADX_CONFIG_MAX_THREAD_NAME_LEN
94
95   The maximum length of a thread's name, not including null termination. By
96   default this is arbitrarily set to 15. This results in an array of characters
97   which is this length + 1 bytes in every pw::Thread's context.
98
99.. c:macro:: PW_THREAD_THREADX_CONFIG_DEFAULT_TIME_SLICE_INTERVAL
100
101   The round robin time slice tick interval for threads at the same priority.
102   By default this is disabled as not all ports support this, using a value of 0
103   ticks.
104
105.. c:macro:: PW_THREAD_THREADX_CONFIG_MIN_PRIORITY
106
107   The minimum priority level, this is normally based on the number of priority
108   levels.
109
110.. c:macro:: PW_THREAD_THREADX_CONFIG_DEFAULT_PRIORITY
111
112   The default priority level. By default this uses the minimal ThreadX
113   priority level, given that 0 is the highest priority.
114
115.. c:macro:: PW_THREAD_THREADX_CONFIG_LOG_LEVEL
116
117   The log level to use for this module. Logs below this level are omitted.
118
119ThreadX Thread Options
120======================
121.. cpp:class:: pw::thread::threadx::Options
122
123   .. cpp:function:: set_name(const char* name)
124
125      Sets the name for the ThreadX thread, note that this will be deep copied
126      into the context and may be truncated based on
127      ``PW_THREAD_THREADX_CONFIG_MAX_THREAD_NAME_LEN``.
128
129   .. cpp:function:: set_priority(UINT priority)
130
131      Sets the priority for the ThreadX thread from 0 through 31, where a value
132      of 0 represents the highest priority, see ThreadX tx_thread_create for
133      more detail.
134
135      **Precondition**: priority <= ``PW_THREAD_THREADX_CONFIG_MIN_PRIORITY``.
136
137   .. cpp:function:: set_preemption_threshold(UINT preemption_threshold)
138
139      Optionally sets the preemption threshold for the ThreadX thread from 0
140      through 31.
141
142      Only priorities higher than this level (i.e. lower number) are allowed to
143      preempt this thread. In other words this allows the thread to specify the
144      priority ceiling for disabling preemption. Threads that have a higher
145      priority than the ceiling are still allowed to preempt while those with
146      less than the ceiling are not allowed to preempt.
147
148      Not setting the preemption threshold or explicitly specifying a value
149      equal to the priority disables preemption threshold.
150
151      Time slicing is disabled while the preemption threshold is enabled, i.e.
152      not equal to the priority, even if a time slice interval was specified.
153
154      The preemption threshold can be adjusted at run time, this only sets the
155      initial threshold.
156
157      **Precondition**: preemption_threshold <= priority
158
159   .. cpp:function:: set_time_slice_interval(UINT time_slice_interval)
160
161      Sets the number of ticks this thread is allowed to run before other ready
162      threads of the same priority are given a chance to run.
163
164      Time slicing is disabled while the preemption threshold is enabled, i.e.
165      not equal to the priority, even if a time slice interval was specified.
166
167      A value of ``TX_NO_TIME_SLICE`` (a value of 0) disables time-slicing of
168      this thread.
169
170      Using time slicing results in a slight amount of system overhead, threads
171      with a unique priority should consider ``TX_NO_TIME_SLICE``.
172
173
174   .. cpp:function:: set_context(pw::thread::embos::Context& context)
175
176      Set the pre-allocated context (all memory needed to run a thread). Note
177      that this is required for this thread creation backend! The Context can
178      either be constructed with an externally provided ``pw::span<ULONG>``
179      stack or the templated form of ``ContextWihtStack<kStackSizeWords`` can be
180      used.
181
182-----------------------------
183Thread Identification Backend
184-----------------------------
185A backend for ``pw::Thread::id`` and ``pw::thread::get_id()`` is offerred using
186``tx_thread_identify()``. It uses ``DASSERT`` to ensure that a thread is
187executing via ``TX_THREAD_GET_SYSTEM_STATE()``.
188
189--------------------
190Thread Sleep Backend
191--------------------
192A backend for ``pw::thread::sleep_for()`` and ``pw::thread::sleep_until()`` is
193offerred using ``tx_thread_sleep()`` if the duration is at least one tick, else
194``tx_thread_relinquish()`` is used. It uses
195``pw::this_thread::get_id() != Thread::id()`` to ensure it invoked only from a
196thread.
197
198--------------------
199Thread Yield Backend
200--------------------
201A backend for ``pw::thread::yield()`` is offered using via
202``tx_thread_relinquish()``. It uses
203``pw::this_thread::get_id() != Thread::id()`` to ensure it invoked only from a
204thread.
205
206---------
207Utilities
208---------
209``ForEachThread()``
210===================
211In cases where an operation must be performed for every thread,
212``ForEachThread()`` can be used to iterate over all the created thread TCBs.
213Note that it's only safe to use this while the scheduler is disabled.
214
215An ``Aborted`` error status is returned if the provided callback returns
216``false`` to request an early termination of thread iteration.
217
218*Return values*
219
220* ``Aborted``: The callback requested an early-termination of thread iteration.
221* ``OkStatus``: The callback has been successfully run with every thread.
222
223--------------------
224Snapshot integration
225--------------------
226This ``pw_thread`` backend provides helper functions that capture ThreadX thread
227state to a ``pw::Thread`` proto.
228
229``SnapshotThreads()``
230=====================
231``SnapshotThreads()`` captures the thread name, state, and stack information for
232the provided ThreadX TCB to a ``pw::Thread`` protobuf encoder. To ensure the
233most up-to-date information is captured, the stack pointer for the currently
234running thread must be provided for cases where the running thread is being
235captured. For ARM Cortex-M CPUs, you can do something like this:
236
237.. code-block:: cpp
238
239   // Capture PSP.
240   void* stack_ptr = 0;
241   asm volatile("mrs %0, psp\n" : "=r"(stack_ptr));
242   pw::thread::ProcessThreadStackCallback cb =
243       [](pw::thread::proto::Thread::StreamEncoder& encoder,
244          pw::ConstByteSpan stack) -> pw::Status {
245     return encoder.WriteRawStack(stack);
246   };
247   pw::thread::threadx::SnapshotThread(my_thread, stack_ptr,
248                                       snapshot_encoder, cb);
249
250``SnapshotThreads()`` wraps the singular thread capture to instead captures
251all created threads to a ``pw::thread::proto::SnapshotThreadInfo`` message.
252This proto message overlays a snapshot, so it is safe to static cast a
253``pw::snapshot::Snapshot::StreamEncoder`` to a
254``pw::thread::proto::SnapshotThreadInfo::StreamEncoder`` when calling this
255function.
256