As of V2.92.08, μCµC/OS-II provides built-in support for run-time library thread safety through the use of Task Local Storage (TLS) for storage of task-specific run-time library static data and mutual exclusion semaphores to protect accesses to shared resources.
...
When OS_TLS_TBL_SIZE
is set to 1 or greater, each task’s OS_TCB
will contain a new array called .OSTCBTLSTbl[]
as shown below. Each array element is of type OS_TLS
which is actually a pointer to void. This allows an OS_TCB
to be extended so that it can have as many TLS areas as needed.
Anchor | ||
---|---|---|
|
Figure: Each OS_TCB
contains an array of OS_TLS
when
|
Panel | ||||
---|---|---|---|---|
|
...
|
...
| |
The number of entries (i.e., the value to set OS_TLS_TBL_SIZE
to) depends on the compiler being supported as well as whether TLS storage is needed for other purposes.
...
Finally, each .OSTCBTLSTbl[]
entry can have a ‘destructor’ associated with it. A destructor is a function that is called when the task is deleted. Destructors are common to all tasks. This means that if a destructor is assigned for a TLS ID, the same destructor will be called for all the tasks for that entry. Also, when a task is deleted, the destructor for all of the TLS IDs will be called – assuming, of course, that a destructor was assigned to the corresponding TLS ID. You set a destructor function by calling OS_TLS_SetDestruct()
and specify the TLS ID associated with the destructor as well as a pointer to the function that will be called. Note that a destructor function must be declared as follows:
Code Block | ||
---|---|---|
| ||
void MyDestructFunction (OS_TCB *p_tcb,
OS_TLS_ID id,
OS_TLS value); |
The drawing below shows the global destructor table. Note that not all implementations of os_tls.c
will have destructors for the TLS.
...
This function is called by OSInit()
and in fact, is called after creating the kernel objects but before creating any of the internal μCµC/OS-III tasks. This means that OS_TLS_Init()
is allowed to create event flags, semaphores, mutexes and message queues. OS_TLS_Init()
would typically create mutexes to protect access to shared resources such as the heap or streams.
...
OS_TLS_TaskCreate()
should check that TLS is a feature enabled for the task being created. This is done by examining the OS_TCB
’s option field (i.e., p_tcb->Opt
) as follows:
Code Block | ||
---|---|---|
| ||
void OS_TLS_TaskCreate (OS_TCB *p_tcb)
{
OS_TLS p_tls;
if ((p_tcb->Opt & OS_OPT_TASK_NO_TLS) == OS_OPT_NONE) {
p_tls = /* Allocate storage for TLS */
p_tcb->TLS_Tbl[MyTLS_ID] = p_tls;
}
} |
void OS_TLS_TaskDel (OS_TCB *p_tcb)
...
The code below shows how OS_TLS_TaskDel()
can be implemented.
Code Block | ||
---|---|---|
| ||
void OS_TLS_TaskDel (OS_TCB *p_tcb) { OS_TLS_ID id; OS_TLS_DESTRUCT_PTR *p_tbl; for (id = 0; id < OS_TLS_NextAvailID; id++) { p_tbl = &OS_TLS_DestructPtrTbl[id]; if (*p_tbl != (OS_TLS_DESTRUCT_PTR)0) { (*p_tbl)(p_tcb, id, p_tcb->TLS_Tbl[id]); } } } |
OS_TLS_TaskDel()
should actually check that TLS was used by the task being deleted. This is done by examining the OS_TCB
’s option field (i.e., p_tcb->Opt
) as follows:
Code Block | ||
---|---|---|
| ||
void OS_TLS_TaskDel (OS_TCB *p_tcb)
{
OS_TLS_ID id;
OS_TLS_DESTRUCT_PTR *p_tbl;
for (id = 0; id < OS_TLS_NextAvailID; id++) {
p_tbl = &OS_TLS_DestructPtrTbl[id];
if (*p_tbl != (OS_TLS_DESTRUCT_PTR)0) {
(*p_tbl)(p_tcb, id, p_tcb->TLS_Tbl[id]);
}
}
} |
An alternate implementation is shown below where OS_TLS_TaskDel()
needs to deallocate storage for the task is shown below.
...
OS_TLS_TaskSw()
should check that TLS is desired for the task being switched in. This is done by examining the OS_TCB’s option field (i.e. p_tcb->Opt) as follows:
Code Block | ||
---|---|---|
| ||
if ((p_tcb->Opt & OS_OPT_TASK_NO_TLS) == OS_OPT_NONE) {
/* TLS option enabled for this task */
} |
Compiler-Specific Lock APIs
As previously mentioned, some compilers may already have declared API functions that are called to ensure exclusive access to shared resources. For example, APIs such as _mutex_lock_file_system()
and _mutex_unlock_file_system()
could be required by the compiler to ensure exclusive access to the file system. os_tls.c might then implement these using μCµC/OS-III as shown below. Note that we also included the code to initialize the mutex in OS_TLS_Init()
.
Code Block | ||
---|---|---|
| ||
OS_EVENT *OS_TLS_FS_Sem; /* Needed to ensure exclusive access to the FS */
void OS_TLS_Init (INT8U *p_err)
{
OS_TLS_NextAvailID = 0u;
OS_TLS_NewLibID = OS_TLS_GetID(p_err);
if (*p_err != OS_ERR_NONE) {
return;
}
OS_TLS_FS_Sem = OSSemCreate(1);
}
void _mutex_lock_file_system (void)
{
INT8U os_err;
if (OSRunning == 0) {
return;
}
OSSemPend((OS_EVENT *)OS_TLS_FS_Sem,
(INT32U )0u,
(INT8U *)&os_err);
}
void _mutex_unlock_file_system (void)
{
INT8U err;
if (OSRunning == 0) {
return;
}
OSSemPost((OS_SEM *)OS_TLS_FS_Sem);
} |
The compiler may require the implementation of many such API functions to ensure exclusive access to the heap, environment variables, etc. These would all be found in os_tls.c
.