pw_thread_embos#
This is a set of backends for pw_thread based on embOS v4.
Warning
This module is still under construction, the API is not yet stable.
Thread Creation Backend#
A backend or pw::thread::Thread
is offered using OS_CreateTaskEx()
.
Optional joining support is enabled via an OS_EVENT
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:
#include "pw_thread/detached_thread.h"
#include "pw_thread_embos/config.h"
#include "pw_thread_embos/context.h"
#include "pw_thread_embos/options.h"
#include "RTOS.h" // For the embOS types.
constexpr OS_PRIO kFooPriority =
pw::thread::embos::config::kDefaultPriority;
constexpr OS_UINT kFooTimeSliceInterval =
pw::thread::embos::config::kDefaultTimeSliceInterval;
constexpr size_t kFooStackSizeWords =
pw::thread::embos::config::kDefaultStackSizeWords;
pw::thread::embos::ContextWithStack<kFooStackSizeWords>
example_thread_context;
void StartExampleThread() {
pw::thread::DetachedThread(
pw::thread::embos::Options()
.set_name("example_thread")
.set_priority(kFooPriority)
.set_time_slice_interval(kFooTimeSliceInterval)
.set_context(example_thread_context),
example_thread_function);
}
Module Configuration Options#
The following configurations can be adjusted via compile-time configuration of this module, see the module documentation for more details.
-
PW_THREAD_EMBOS_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 an OS_EVENT to every thread’s pw::thread::embos::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.
-
PW_THREAD_EMBOS_CONFIG_MINIMUM_STACK_SIZE_WORDS#
The minimum stack size in words. By default this uses Segger’s recommendation of 68 bytes.
-
PW_THREAD_EMBOS_CONFIG_DEFAULT_STACK_SIZE_WORDS#
The default stack size in words. By default this uses Segger’s recommendation of 256 bytes to start.
-
PW_THREAD_EMBOS_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.
-
PW_THREAD_EMBOS_CONFIG_MIN_PRIORITY#
The minimum priority level, this is normally 1, since 0 is not a valid priority level.
-
PW_THREAD_EMBOS_CONFIG_DEFAULT_PRIORITY#
The default priority level. By default this uses the minimal embOS priority.
-
PW_THREAD_EMBOS_CONFIG_DEFAULT_TIME_SLICE_INTERVAL#
The round robin time slice tick interval for threads at the same priority. By default this is set to 2 ticks based on the embOS default.
-
PW_THREAD_EMBOS_CONFIG_LOG_LEVEL#
The log level to use for this module. Logs below this level are omitted.
embOS Thread Options#
-
class pw::thread::embos::Options#
-
set_name(const char *name)#
Sets the name for the embOS task, this is optional. Note that this will be deep copied into the context and may be truncated based on
PW_THREAD_EMBOS_CONFIG_MAX_THREAD_NAME_LEN
.
-
set_priority(OS_PRIO priority)#
Sets the priority for the embOS task. Higher values are higher priority, see embOS OS_CreateTaskEx for more detail. Precondition: This must be >=
PW_THREAD_EMBOS_CONFIG_MIN_PRIORITY
.
-
set_time_slice_interval(OS_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.
A value of 0 disables time-slicing of this thread.
Precondition: This must be <= 255 ticks.
-
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 providedpw::span<OS_UINT>
stack or the templated form ofContextWithStack<kStackSizeWords>
can be used.
-
set_name(const char *name)#
Thread Identification Backend#
Pigweed AI summary: A backend for thread identification is provided using OS_GetTaskID(), with DASSERT used to ensure the scheduler has started via OS_IsRunning().
A backend for pw::thread::Id
and pw::thread::get_id()
is offerred using
OS_GetTaskID()
. It uses DASSERT
to ensure that the scheduler has started
via OS_IsRunning()
.
Thread Sleep Backend#
Pigweed AI summary: This paragraph describes a backend for thread sleep functions in a programming language. The backend uses OS_Delay() if the duration is at least one tick, otherwise it uses OS_Yield(). It also includes a check to ensure it is only invoked from a thread.
A backend for pw::thread::sleep_for()
and pw::thread::sleep_until()
is
offerred using OS_Delay()
if the duration is at least one tick, else
OS_Yield()
is used. It uses pw::this_thread::get_id() != thread::Id()
to
ensure it invoked only from a thread.
Thread Yield Backend#
Pigweed AI summary: This article discusses the implementation of a backend for the pw::thread::yield() function using OS_Yield(). The backend ensures that the function is only invoked from a thread using pw::this_thread::get_id() != thread::Id().
A backend for pw::thread::yield()
is offered using via OS_Yield()
.
It uses pw::this_thread::get_id() != thread::Id()
to ensure it invoked only
from a thread.
Utilities#
Pigweed AI summary: The <literal>ForEachThread()</literal> function can be used to perform an operation for every thread, but it should only be used while the scheduler is suspended and after <literal>OS_Start()</literal> has been called. If the provided callback returns <literal>false</literal>, an <literal>Aborted</literal> error status is returned. The function may also return a <literal>FailedPrecondition</literal> error code if it is run before the OS has been initialized. The
ForEachThread()
#
Pigweed AI summary: The <literal>ForEachThread()</literal> function can be used to perform an operation for every thread, but it should only be used while the scheduler is suspended and after <literal>OS_Start()</literal> has been called. If the provided callback returns <literal>false</literal>, an <literal>Aborted</literal> error status is returned. The function returns <literal>FailedPrecondition</literal> if it is run before the OS has been initialized, and <literal>OkStatus
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 suspended, and this
should only be used after OS_Start()
has been called. Calling this before
the scheduler has started is non-fatal, but will result in no action and a
FailedPrecondition
error code.
An Aborted
error status is returned if the provided callback returns
false
to request an early termination of thread iteration.
Return values
FailedPrecondition
: Returned whenForEachThread()
is run before the OS has been initialized.Aborted
: The callback requested an early-termination of thread iteration.OkStatus
: The callback has been successfully run with every thread.
Snapshot Integration#
Pigweed AI summary: The pw_thread backend provides helper functions that capture embOS thread information to a pw::thread::Thread proto. SnapshotThread() captures the thread name, state, and stack information for the provided embOS TCB to a pw::thread::Thread protobuf encoder. SnapshotThreads() wraps the singular thread capture to instead capture all created threads to a pw::thread::proto::SnapshotThreadInfo message. In order to capture thread names when snapshotting a thread, embOS must have OS_TRACKNAME enabled.
This pw_thread
backend provides helper functions that capture embOS thread
info to a pw::thread::Thread
proto.
SnapshotThreads()
#
Pigweed AI summary: The function SnapshotThread() captures thread information for the provided embOS TCB to a pw::thread::Thread protobuf encoder. To capture all created threads, SnapshotThreads() can be used instead. Thread name capture requires OS_TRACKNAME to be enabled, and thread stack capture requires either OS_SUPPORT_MPU or OS_CHECKSTACK to be enabled. Thread state capture is not part of embOS's public API, but Pigweed uses logic based on information from Segger's public forum to interpret thread state.
SnapshotThread()
captures the thread name, state, and stack information for
the provided embOS 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:
// 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::embos::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.
Thread Name Capture#
Pigweed AI summary: To capture thread names during snapshotting, embOS requires the OS_TRACKNAME feature to be enabled. If it is disabled, no thread name will be captured. It is highly recommended to enable this feature for better debugging.
In order to capture thread names when snapshotting a thread, embOS must have
OS_TRACKNAME
enabled. If OS_TRACKNAME
is disabled, no thread name
is captured. Enabling this is strongly recommended for debugability.
Thread State Capture#
Pigweed AI summary: The embOS thread state is not publicly available, but the snapshot integration captures it based on information from Segger's public forum. This has been tested on embOS 4.22 and was initially reported for embOS 5.06, but the interpretation of thread state may be incorrect for other versions of embOS.
embOS thread state is not part of embOS’s public API. Despite this, the snapshot integration captures thread state based on information on how the thread state is represented from Segger’s public forum. This has been tested on embOS 4.22, and was initially reported for embOS 5.06. The logic Pigweed uses to interpret thread state may be incorrect for other versions of embOS.
Thread Stack Capture#
Pigweed AI summary: This article explains that in order to capture full thread stack information, embOS must track the stack bounds for each task. This is done when either OS_SUPPORT_MPU or OS_CHECKSTACK are enabled, and the callback for thread stack dumping will be called. If these options are disabled, stack start and end pointers will not be captured and the ProcessThreadStackCallback will not be called.
Full thread stack information capture is dependent on embOS tracking the stack
bounds for each task. When either OS_SUPPORT_MPU
or OS_CHECKSTACK
are
enabled, stack bounds are tracked and the callback for thread stack dumping
will be called. If both of these options are disabled, stack_start_pointer
and stack_end_pointer
will not be captured, and the
ProcessThreadStackCallback
will not be called.