255 lines
9.2 KiB
ReStructuredText
255 lines
9.2 KiB
ReStructuredText
.. _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 ``std::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:: cpp
|
|
|
|
// Capture PSP.
|
|
void* stack_ptr = 0;
|
|
asm volatile("mrs %0, psp\n" : "=r"(stack_ptr));
|
|
pw::thread::ProcessThreadStackCallback cb =
|
|
[](pw::thread::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::SnapshotThreadInfo`` message. This proto
|
|
message overlays a snapshot, so it is safe to static cast a
|
|
``pw::snapshot::Snapshot::StreamEncoder`` to a
|
|
``pw::thread::SnapshotThreadInfo::StreamEncoder`` when calling this function.
|