/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
// ==== Thread Management ====
/**
\addtogroup CMSIS_RTOS_ThreadMgmt Thread Management
\ingroup CMSIS_RTOS CMSIS_RTOSv2
\brief Define, create, and control thread functions.
\details
The Thread Management function group allows defining, creating, and controlling thread functions in the system. The function
\b main is a special thread function that is started at system initialization and has the initial priority
\a osPriorityNormal.
\anchor ThreadStates
Threads can be in the following states:
- \b RUNNING: The thread that is currently running is in the \b RUNNING state. Only one thread at a time can be in this state.
- \b READY: Threads which are ready to run are in the \b READY state. Once the \b RUNNING thread has terminated, or is \b BLOCKED, the next \b READY thread with the highest priority becomes the \b RUNNING thread.
- \b BLOCKED: Threads that are blocked either delayed, waiting for an event to occur or suspended are in the \b BLOCKED state.
- \b TERMINATED: When \ref osThreadTerminate is called, and threads that joined are in \b TERMINATED state. When all joined threads have terminated, resources are released an the threads are in state \b INACTIVE.
- \b INACTIVE: Threads that are not created or have been terminated with all resources released are in the \b INACTIVE state.
\image html "ThreadStatus.png" "Thread State and State Transitions"
A CMSIS-RTOS assumes that threads are scheduled as shown in the figure Thread State and State Transitions. The thread
states change as follows:
- A thread is created using the function \ref osThreadNew. This puts the thread into the \b READY or \b RUNNING state
(depending on the thread priority).
- CMSIS-RTOS is pre-emptive. The active thread with the highest priority becomes the \b RUNNING thread provided it does not
wait for any event. The initial priority of a thread is defined with the \ref osThreadAttr_t but may be changed during
execution using the function \ref osThreadSetPriority.
- The \b RUNNING thread transfers into the \b BLOCKED state when it is delayed, waiting for an event or suspended.
- Active threads can be terminated any time using the function \ref osThreadTerminate. Threads can terminate also by just
returning from the thread function. Threads that are terminated are in the \b INACTIVE state and typically do not consume
any dynamic memory resources.
\note
Refer to \ref threadConfig for RTX5 configuration options.
Thread Examples
===============
The following examples show various scenarios to create threads:
Example 1 - Create a simple thread
Create a thread out of the function thread1 using all default values for thread attributes and memory from the \ref GlobalMemoryPool.
\code{.c}
__NO_RETURN void thread1 (void *argument) {
// ...
for (;;) {}
}
int main (void) {
osKernelInitialize();
;
osThreadNew(thread1, NULL, NULL); // Create thread with default settings
;
osKernelStart();
}
\endcode
Example 2 - Create thread with stack non-default stack size
Similar to the simple thread all attributes are default. The stack is dynamically allocated from the \ref GlobalMemoryPool
\ref osThreadAttr_t.stack_size is used to pass the stack size in Bytes to osThreadNew.
\code{.c}
__NO_RETURN void thread1 (void *argument) {
// ...
for (;;) {}
}
const osThreadAttr_t thread1_attr = {
.stack_size = 1024; // Create the thread stack with a size of 1024 bytes
}
int main (void) {
;
osThreadNew(thread1, NULL, &thread1_attr); // Create thread with custom sized stack memory
;
}
\endcode
Example 3 - Create thread with statically allocated stack
Similar to the simple thread all attributes are default. The stack is statically allocated using the uint64_t array thread1_stk_1. This allocates 64*8 Bytes (=512 Bytes) with an alignment of 8 Bytes (mandatory for Cortex-M stack memory).
\ref osThreadAttr_t.stack_mem holds a pointer to the stacks lowest address.
\ref osThreadAttr_t.stack_size is used to pass the stack size in Bytes to osThreadNew.
\code{.c}
__NO_RETURN void thread1 (void *argument) {
// ...
for (;;) {}
}
static uint64_t thread1_stk_1[64];
const osThreadAttr_t thread1_attr = {
.stack_mem = &thread1_stk_1[0];
.stack_size = sizeof(thread1_stk_1);
}
int main (void) {
;
osThreadNew(thread1, NULL, &thread1_attr); // Create thread with statically allocated stack memory
;
}
\endcode
Example 4 - Thread with statically allocated task control block
Typically this method is chosen together with a statically allocated stack as shown in Example 2.
os_objectSize macros supply the size of OS objects.
RTX5 has the following internal definitions to determine the size of OS objects:
\code{.c}
#define os_ThreadCbSize sizeof(os_thread_t)
#define os_TimerCbSize sizeof(os_timer_t)
#define os_EventFlagsCbSize sizeof(os_event_flags_t)
#define os_MutexCbSize sizeof(os_mutex_t)
#define os_SemaphoreCbSize sizeof(os_semaphore_t)
#define os_MemoryPoolCbSize sizeof(os_memory_pool_t)
#define os_MessageQueueCbSize sizeof(os_message_queue_t)
\endcode
Include "rtx_os.h" to access these macros.
\code{.c}
#include "rtx_os.h" //required for os_ThreadCbSize macro definition
__NO_RETURN void thread1 (void *argument) {
// ...
for (;;) {}
}
static os_thread_t thread1_tcb;
const osThreadAttr_t thread1_attr = {
.cb_mem = &thread1_tcb;
.cb_size = os_ThreadCbSize;
}
int main (void) {
;
osThreadNew(thread1, NULL, &thread1_attr); // Create thread with custom tcb memory
;
}
\endcode
Example 5 - Create thread with a different priority
The default priority of RTX is \ref osPriorityNormal. Often you want to run a task with a higher or lower priority. Using the \ref osThreadAttr_t control structure you can set any inital priority required.
\code
__NO_RETURN void thread1 (void *argument) {
// ...
for (;;) {}
}
const osThreadAttr_t thread1_attr = {
.priority = osPriorityHigh //Set initial thread priority to high
}
int main (void) {
;
osThreadNew(thread1, NULL, &thread1_attr);
;
}
\endcode
Example 6 - Joinable threads
In this example a master thread creates four threads with the \ref osThreadJoinable attribute. These will do some work and return using the osThreadExit call after finished. \ref osThreadJoin is used to synchronize the thread termination.
\code{.c}
__NO_RETURN void worker (void *argument) {
; // work a lot on data[]
osDelay(1000);
osThreadExit();
}
__NO_RETURN void thread1 (void *argument) {
osThreadAttr_t worker_attr = {0};
osThreadId_t worker_ids[4];
uint8_t data[4][10];
worker_attr.attr_bits = osThreadJoinable;
worker_ids[0] = osThreadNew(worker, &data[0][0], &worker_attr);
worker_ids[1] = osThreadNew(worker, &data[1][0], &worker_attr);
worker_ids[2] = osThreadNew(worker, &data[2][0], &worker_attr);
worker_ids[3] = osThreadNew(worker, &data[3][0], &worker_attr);
osThreadJoin(worker_ids[0]);
osThreadJoin(worker_ids[1]);
osThreadJoin(worker_ids[2]);
osThreadJoin(worker_ids[3]);
osThreadExit();
}
\endcode
@{
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\enum osThreadState_t
\details
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\enum osPriority_t
\details
The \b osPriority_t value specifies the priority for a thread. The default thread priority should be \a osPriorityNormal.
If a Thread is active that has a higher priority than the currently executing thread, then a thread switch occurs immediately
to execute the new task.
To prevent from a priority inversion, a CMSIS-RTOS compliant OS may optionally implement a priority inheritance method.
A priority inversion occurs when a high priority thread is waiting for a resource or event that is controlled by a thread
with a lower priority.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\typedef void (*os_thread_func_t) (void *argument)
\details
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\typedef osThreadId_t
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\struct osThreadAttr_t
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\def osThreadJoinable
\details
See \ref osThreadJoin.
*/
/**
\def osThreadDetached
\details
A thread in this state cannot be joined using \ref osThreadJoin.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osThreadId_t osThreadNew (os_thread_func_t func, void *argument, const osThreadAttr_t *attr)
\details
Start a thread function by adding it to the Active Threads list and set it to state \b READY. Arguments for the thread function are passed
using the parameter pointer \em *argument. When the priority of the created thread function is higher than the current \b RUNNING thread,
the created thread function starts instantly and becomes the new \b RUNNING thread. Thread attributes are defined with the parameter pointer \em attr.
Attributes include settings for thread priority, stack size, or memory allocation.
\note
Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn const char *osThreadGetName (osThreadId_t thread_id)
\details
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osThreadId_t osThreadGetId (void)
\details
Get the thread ID of the current running thread.
\note
Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Code Example:
\code{.c}
void ThreadGetId_example (void) {
osThreadId id; // id for the currently running thread
id = osThreadGetId ();
if (id == NULL) {
// Failed to get the id; not in a thread
}
}
\endcode
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osThreadState_t osThreadGetState (osThreadId_t thread_id)
\details
Return the state of the thread identified by parameter \em thread_id.
See \ref osThreadState_t for possible states.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadGetState from an ISR will return \ref osThreadError.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osStatus_t osThreadSetPriority (osThreadId_t thread_id, osPriority_t priority)
\details
Change the priority of an active thread.
\ref osStatus_t return values:
- \em osOK: the priority of the specified thread has been changed successfully.
- \em osErrorParameter: the value of the parameter \em thread_id or parameter \em priority is incorrect.
- \em osErrorResource: parameter \em thread_id refers to a thread that is not an active thread.
- \em osErrorISR: the function \b osThreadSetPriority cannot be called from interrupt service routines.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadSetPriority from an ISR will return \ref osErrorISR.
Code Example:
\code{.c}
#include "cmsis_os2.h"
void Thread_1 (void const *arg) { // Thread function
osThreadId_t id; // id for the currently running thread
osStatus status; // status of the executed function
:
id = osThreadGetId (); // Obtain ID of current running thread
if (id != NULL) {
status = osThreadSetPriority (id, osPriorityBelowNormal);
if (status == osOK) {
// Thread priority changed to BelowNormal
}
else {
// Failed to set the priority
}
}
else {
// Failed to get the id
}
:
}
\endcode
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osPriority_t osThreadGetPriority (osThreadId_t thread_id)
\details
Get the priority of an active thread. In case of failure, the value \b osPriorityError is returned.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadGetPriority from an ISR will return \ref osErrorISR.
Code Example:
\code{.c}
#include "cmsis_os2.h"
void Thread_1 (void const *arg) { // Thread function
osThreadId_t id; // id for the currently running thread
osPriority_t priority; // thread priority
id = osThreadGetId (); // Obtain ID of current running thread
if (id != NULL) {
priority = osThreadGetPriority (id);
}
else {
// Failed to get the id
}
}
\endcode
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osStatus_t osThreadYield (void)
\details
Pass control to the next thread that is in state \b READY. If there is no other thread in state \b READY,
then the current thread continues execution and no thread switching occurs.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadYield from an ISR will return \ref osErrorISR.
Code Example:
\code{.c}
#include "cmsis_os2.h"
void Thread_1 (void const *arg) { // Thread function
osStatus_t status; // status of the executed function
:
while (1) {
status = osThreadYield(); //
if (status != osOK) {
// thread switch not occurred, not in a thread function
}
}
}
\endcode
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osStatus_t osThreadSuspend (osThreadId_t thread_id)
\details
Suspends execution of the thread identified by parameter \em thread_id. Thread is put into the state \em Blocked (\ref osThreadBlocked).
The thread is not executed until restarted with the function \ref osThreadResume.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadSuspend from an ISR will return \ref osErrorISR.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osStatus_t osThreadResume (osThreadId_t thread_id)
\details
Forces a thread in BLOCKED state, specified with \em thread_id, to resume operation.
Functions that will put a thread into BLOCKED state are:
\ref osEventFlagsWait and \ref osThreadFlagsWait,
\ref osDelay and \ref osDelayUntil,
\ref osMutexAcquire and \ref osSemaphoreAcquire,
\ref osMessageQueueGet,
\ref osThreadJoin,
\ref osThreadSuspend.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadResume from an ISR will return \ref osErrorISR.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osStatus_t osThreadDetach (osThreadId_t thread_id)
\details
Changes the attribute of a thread specified in \em thread_id to \ref osThreadDetached. Detached threads are not joinable with \ref osThreadJoin.
When a detached thread is terminated all resources are returned to the system. The behaviour of \ref osThreadDetach on an already detached thread is undefined.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadDetach from an ISR will return \ref osErrorISR.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osStatus_t osThreadJoin (osThreadId_t thread_id)
\details
Waits for the thread specified by \em thread_id to terminate.
If that thread has already terminated, then \ref osThreadJoin returns immediately.
The thread referred to by thread_id must joinable. By default threads are created with the attribute \ref osThreadJoinable. The thread may not have been detached by \ref osThreadDetach.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadJoin from an ISR will return \ref osErrorISR.
\todo See Example 6
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn __NO_RETURN void osThreadExit (void)
\details
osThreadExit terminates the calling thread. This allows the thread to be synchronized with osThreadJoin.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osStatus_t osThreadTerminate (osThreadId_t thread_id)
\details
Remove the thread function from the active thread list. If the thread is currently /b RUNNING the execution stops and the thread terminates.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadTerminate from an ISR will return \ref osErrorISR.
\code
#include "cmsis_os2.h"
void Thread_1 (void c*arg); // function prototype for Thread_1
void ThreadTerminate_example (void) {
osStatus_t status;
osThreadId_t id;
id = osThreadNew (Thread_1, NULL, NULL); // create the thread
:
status = osThreadTerminate (id); // stop the thread
if (status == osOK) {
// Thread was terminated successfully
}
else {
// Failed to terminate a thread
}
}
\endcode
*/
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn uint32_t osThreadGetStackSize (osThreadId_t thread_id)
\details
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn uint32_t osThreadGetStackSpace (osThreadId_t thread_id);
\details
osThreadGetStackSpace returns the size of unused stack space for the thread passed in thread_id.
If this function is not implemented or stack checking is disabled it will return 0.
\note Cannot be called from \ref CMSIS_RTOS_ISR_Calls "Interrupt Service Routines".
Calling \ref osThreadGetStackSpace from an ISR will return 0.
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn uint32_t osThreadGetCount (void)
\details
*/
/*=======0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1====*/
/**
\fn osThreadEnumerate (osThreadId_t *thread_array, uint32_t array_items)
\details
*/
/// @}
// these struct members must stay outside the group to avoid double entries in documentation
/**
\var osThreadAttr_t::attr_bits
\details
The following predefined bit masks can be assigned to set options for a thread object.
Bit Mask | Description
:-----------------------|:-----------------------------------------
osThreadJoinable | Thread is created in a join-able state.
osThreadDettached | Thread is created in a detached state (default).
\var osThreadAttr_t::cb_mem
\details
Pointer to a memory location for the thread object. This can optionally be used for custom memory management systems.
Specify \token{NULL} to use the kernel memory management.
\var osThreadAttr_t::cb_size
\details
The size of the memory block passed with \ref cb_mem. Must be the size of a thread control block object or larger.
\var osThreadAttr_t::name
\details
String with a human readable name of the thread object.
\var osThreadAttr_t::priority
\details
Specifies the initial thread priority with a value from #osPriority_t.
\var osThreadAttr_t::reserved
\details
Reserved for future use. Must be \token{0}.
\var osThreadAttr_t::stack_mem
\details
Pointer to a memory location for the thread stack. This can optionally be used for custom memory management systems.
Specify \token{NULL} to use the kernel memory management.
\var osThreadAttr_t::tz_module
\details
TrustZone Thread Context Management Identifier to allocate context memory for threads. The RTOS kernel that runs in non-secure state calls the interface functions defined by the header file TZ_context.h. See TrustZone RTOS Context Management.
\var osThreadAttr_t::stack_size
\details
The size of the stack specified by \ref stack_mem in Bytes.
*/