uC-OS2
uC-OS2 copied to clipboard
'OS_FLAG_CONSUME' implementation cause race condition
#define FLAG_UNLOCKED ((OS_FLAGS)0x0001)
OS_FLAG_GRP *flag_group;
void WorkWithSharedResource()
{
INT8U err;
// 1: Start of critical section
OSFlagPend(flag_group, FLAG_UNLOCKED, (OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME), 0, &err);
// 2: start long-running operation, wait semaphore (signaled in interrupt)
OSSemPend(...)
// 3: end of critical section
OSFlagPost(flag_group, FLAG_UNLOCKED, OS_FLAG_SET, &err)
}
// high priority task
void TaskA(void *p_arg)
{
while (true)
{
WorkWithSharedResource();
}
}
// low priority task
void TaskB(void *p_arg)
{
while (true)
{
WorkWithSharedResource();
}
}
Conditions: flag_grp->flags contains FLAG_UNLOCKED (i.e. bit is set)
Steps:
- 1A - high priority task acquire flag (because flag is set)
- 2A - high priority task start pend on semaphore
- context switch to TaskB
- 1B - low priority task start pend (flag is not set, TaskA consumed it in step 1)
- semaphore signaled by interrupt, TaskA become ready, context switch to TaskA
- 3A - high priority task set flag (in that step TaskB become ready through pgrp->OSFlagWaitList traversal)
- 1A - TaskA continues execution, it's priority is higher than TaskB. So 'FLAG_UNLOCKED' will be acquired again.
- 2A - TaskA pend on semaphore
- context switch to TaskB
- OSFlagPend continues execution in believing that flag group contains 'FLAG_UNLOCKED' flag (because of OSTCBCur->OSTCBFlagsRdy), but it's not. So, TaskA and TaskB in critical section, race condition...
#18 - Fix 'OS_FLAG_CONSUME' option behavior.
P.S. In this example code 'flag_group' should act as a binary semaphore with FIFO strategy.
Flags don't function in the same way as semaphores and should not be used for Critical Sections. If multiple tasks are pending on a flag with the consume option, the loop inside FlagPost will ready all of them. Flags were designed to operate primarily as a notification mechanism. For resource protection, you should use semaphores or mutexes.