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