aboutsummaryrefslogtreecommitdiff
path: root/pw_thread_threadx/docs.rst
blob: d49c133ca4341761e85b6f777d0cc8a9c0760eb9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
.. _module-pw_thread_threadx:

=================
pw_thread_threadx
=================
This is a set of backends for pw_thread based on ThreadX.

.. Warning::
   This module is still under construction, the API is not yet stable.

-----------------------
Thread Creation Backend
-----------------------
A backend for ``pw::thread::Thread`` is offered using ``tx_thread_create``.
Optional joining support is enabled via an ``TX_EVENT_FLAGS_GROUP`` in each
thread's context.

This backend permits users to start threads where contexts must be explicitly
allocated and passed in as an option. As a quick example, a detached thread can
be created as follows:

.. code-block:: cpp

   #include "pw_thread/detached_thread.h"
   #include "pw_thread_threadx/config.h"
   #include "pw_thread_threadx/context.h"
   #include "pw_thread_threadx/options.h"
   #include "tx_api.h"

   constexpr UINT kFooPriority =
       pw::thread::threadx::config::kDefaultPriority;
   constexpr ULONG kFooTimeSliceInterval =
       pw::thread::threadx::config::kDefaultTimeSliceInterval;
   constexpr size_t kFooStackSizeWords =
       pw::thread::threadx::config::kDefaultStackSizeWords;

   pw::thread::threadx::ContextWithStack<kFooStackSizeWords>
       example_thread_context;
   void StartExampleThread() {
     pw::thread::DetachedThread(
         pw::thread::threadx::Options()
             .set_name("example_thread")
             .set_priority(kFooPriority)
             .set_time_slice_interval(kFooTimeSliceInterval)
             .set_context(example_thread_context),
         example_thread_function);
   }

.. list-table::

   * - :ref:`module-pw_thread` Facade
     - Backend Target
     - Description
   * - ``pw_thread:id``
     - ``pw_thread_threadx:id``
     - Thread identification.
   * - ``pw_thread:yield``
     - ``pw_thread_threadx:yield``
     - Thread scheduler yielding.
   * - ``pw_thread:sleep``
     - ``pw_thread_threadx:sleep``
     - Thread scheduler sleeping.
   * - ``pw_thread:thread``
     - ``pw_thread_threadx:thread``
     - Thread creation.

Module Configuration Options
============================
The following configurations can be adjusted via compile-time configuration of
this module, see the
:ref:`module documentation <module-structure-compile-time-configuration>` for
more details.

.. c:macro:: PW_THREAD_THREADX_CONFIG_JOINING_ENABLED

   Whether thread joining is enabled. By default this is disabled.

   We suggest only enabling this when thread joining is required to minimize
   the RAM and ROM cost of threads.

   Enabling this grows the RAM footprint of every pw::thread::Thread as it adds
   a TX_EVENT_FLAGS_GROUP to every thread's pw::thread::threadx::Context. In
   addition, there is a minute ROM cost to construct and destroy this added
   object.

   PW_THREAD_JOINING_ENABLED gets set to this value.

.. c:macro:: PW_THREAD_THREADX_CONFIG_DEFAULT_STACK_SIZE_WORDS

   The default stack size in words. By default this uses the minimal ThreadX
   stack size.

.. c:macro:: PW_THREAD_THREADX_CONFIG_MAX_THREAD_NAME_LEN

   The maximum length of a thread's name, not including null termination. By
   default this is arbitrarily set to 15. This results in an array of characters
   which is this length + 1 bytes in every pw::thread::Thread's context.

.. c:macro:: PW_THREAD_THREADX_CONFIG_DEFAULT_TIME_SLICE_INTERVAL

   The round robin time slice tick interval for threads at the same priority.
   By default this is disabled as not all ports support this, using a value of 0
   ticks.

.. c:macro:: PW_THREAD_THREADX_CONFIG_MIN_PRIORITY

   The minimum priority level, this is normally based on the number of priority
   levels.

.. c:macro:: PW_THREAD_THREADX_CONFIG_DEFAULT_PRIORITY

   The default priority level. By default this uses the minimal ThreadX
   priority level, given that 0 is the highest priority.

.. c:macro:: PW_THREAD_THREADX_CONFIG_LOG_LEVEL

   The log level to use for this module. Logs below this level are omitted.

ThreadX Thread Options
======================
.. cpp:class:: pw::thread::threadx::Options

   .. cpp:function:: set_name(const char* name)

      Sets the name for the ThreadX thread, note that this will be deep copied
      into the context and may be truncated based on
      ``PW_THREAD_THREADX_CONFIG_MAX_THREAD_NAME_LEN``.

   .. cpp:function:: set_priority(UINT priority)

      Sets the priority for the ThreadX thread from 0 through 31, where a value
      of 0 represents the highest priority, see ThreadX tx_thread_create for
      more detail.

      **Precondition**: priority <= ``PW_THREAD_THREADX_CONFIG_MIN_PRIORITY``.

   .. cpp:function:: set_preemption_threshold(UINT preemption_threshold)

      Optionally sets the preemption threshold for the ThreadX thread from 0
      through 31.

      Only priorities higher than this level (i.e. lower number) are allowed to
      preempt this thread. In other words this allows the thread to specify the
      priority ceiling for disabling preemption. Threads that have a higher
      priority than the ceiling are still allowed to preempt while those with
      less than the ceiling are not allowed to preempt.

      Not setting the preemption threshold or explicitly specifying a value
      equal to the priority disables preemption threshold.

      Time slicing is disabled while the preemption threshold is enabled, i.e.
      not equal to the priority, even if a time slice interval was specified.

      The preemption threshold can be adjusted at run time, this only sets the
      initial threshold.

      **Precondition**: preemption_threshold <= priority

   .. cpp:function:: set_time_slice_interval(UINT time_slice_interval)

      Sets the number of ticks this thread is allowed to run before other ready
      threads of the same priority are given a chance to run.

      Time slicing is disabled while the preemption threshold is enabled, i.e.
      not equal to the priority, even if a time slice interval was specified.

      A value of ``TX_NO_TIME_SLICE`` (a value of 0) disables time-slicing of
      this thread.

      Using time slicing results in a slight amount of system overhead, threads
      with a unique priority should consider ``TX_NO_TIME_SLICE``.


   .. cpp:function:: set_context(pw::thread::embos::Context& context)

      Set the pre-allocated context (all memory needed to run a thread). Note
      that this is required for this thread creation backend! The Context can
      either be constructed with an externally provided ``pw::span<ULONG>``
      stack or the templated form of ``ContextWihtStack<kStackSizeWords`` can be
      used.

-----------------------------
Thread Identification Backend
-----------------------------
A backend for ``pw::thread::Id`` and ``pw::thread::get_id()`` is offerred using
``tx_thread_identify()``. It uses ``DASSERT`` to ensure that a thread is
executing via ``TX_THREAD_GET_SYSTEM_STATE()``.

--------------------
Thread Sleep Backend
--------------------
A backend for ``pw::thread::sleep_for()`` and ``pw::thread::sleep_until()`` is
offerred using ``tx_thread_sleep()`` if the duration is at least one tick, else
``tx_thread_relinquish()`` is used. It uses
``pw::this_thread::get_id() != thread::Id()`` to ensure it invoked only from a
thread.

--------------------
Thread Yield Backend
--------------------
A backend for ``pw::thread::yield()`` is offered using via
``tx_thread_relinquish()``. It uses
``pw::this_thread::get_id() != thread::Id()`` to ensure it invoked only from a
thread.

---------
Utilities
---------
``ForEachThread()``
===================
In cases where an operation must be performed for every thread,
``ForEachThread()`` can be used to iterate over all the created thread TCBs.
Note that it's only safe to use this while the scheduler is disabled.

An ``Aborted`` error status is returned if the provided callback returns
``false`` to request an early termination of thread iteration.

*Return values*

* ``Aborted``: The callback requested an early-termination of thread iteration.
* ``OkStatus``: The callback has been successfully run with every thread.

--------------------
Snapshot integration
--------------------
This ``pw_thread`` backend provides helper functions that capture ThreadX thread
state to a ``pw::thread::Thread`` proto.

``SnapshotThreads()``
=====================
``SnapshotThread()`` captures the thread name, state, and stack information for
the provided ThreadX TCB to a ``pw::thread::Thread`` protobuf encoder. To ensure
the most up-to-date information is captured, the stack pointer for the currently
running thread must be provided for cases where the running thread is being
captured. For ARM Cortex-M CPUs, you can do something like this:

.. code-block:: cpp

   // Capture PSP.
   void* stack_ptr = 0;
   asm volatile("mrs %0, psp\n" : "=r"(stack_ptr));
   pw::thread::ProcessThreadStackCallback cb =
       [](pw::thread::proto::Thread::StreamEncoder& encoder,
          pw::ConstByteSpan stack) -> pw::Status {
     return encoder.WriteRawStack(stack);
   };
   pw::thread::threadx::SnapshotThread(my_thread, stack_ptr,
                                       snapshot_encoder, cb);

``SnapshotThreads()`` wraps the singular thread capture to instead captures
all created threads to a ``pw::thread::proto::SnapshotThreadInfo`` message.
This proto message overlays a snapshot, so it is safe to static cast a
``pw::snapshot::Snapshot::StreamEncoder`` to a
``pw::thread::proto::SnapshotThreadInfo::StreamEncoder`` when calling this
function.