nobody wrote on Tuesday, November 02, 2004:
This behavior could be correct depending on the priority of your tasks.
Below is a simple test program that demonstrates the behavior.
1) Main creates the semaphore, then creates the two tasks.
2) Task B starts executing first, and obtains the semaphore.
3) Task B delays - causing Task A to execute.
4) Task A attempts to obtain the semaphore, but Task B has it so Task A blocks.
5) Eventually Task B is able to continue and does its ‘job’.
6) Task B gives up the semaphore. As Task A is blocked on the semaphore it is unblocked when the semaphore is given up.
If Task A had a higher priority than Task B it would now preempt Task B, obtain the semaphore, and do its ‘job’.
However, as in this demo Task A has the same priority as Task B, Task A does not preempt Task B and Task B continues to execute.
7) Task B immediately obtains the semaphore again.
8) Task B then blocks waiting to be able to do it’s task. Now Task B is blocked Task A can execute.
9) Task A attempts to obtain the semaphore, but it is unavailable (Task B has it again), so the call to cSemaphoreTake() fails!
Solutions (in order of preference):
a) Rearrange lIOFunc so that there is no delay while the semaphore is locked. This could be done if the semaphore is only released when the IO ‘job’ becomes available.
b) Add a call to portYIELD immediately after the call to lIOFunc(). This would force a context switch meaning that Task A and Task B would then alternate in obtaining the semaphore.
c) Lower the priority of the task once it has the semaphore (could mean the semaphore is locked for longer depending on other task priorities), then raise it again once it is released.
/////
TEST PROGRAM
/////
xSemaphoreHandle xSemaphore;
portLONG lIOfunc( void )
{
portLONG l;
if( cSemaphoreTake( xSemaphore, 1000UL ) != pdFAIL )
{
// Simulate a delay;
for( l = 0; l < 0x2; l++ )
{
vTaskDelay( 2 );
}
// Do job
cSemaphoreGive( xSemaphore );
return pdPASS;
}
return pdFAIL;
}
void vTaskA( void *pvParameters )
{
for( ;; )
{
lIOfunc();
}
}
void vTaskB( void *pvParameters )
{
for( ;; )
{
lIOfunc();
}
}
int main( void )
{
prvSetupHardware();
vSemaphoreCreateBinary( xSemaphore );
sTaskCreate( vTaskA, "A", 128, NULL, 1, NULL );
sTaskCreate( vTaskB, "B", 128, NULL, 1, NULL );
vTaskStartScheduler( portUSE_PREEMPTION );
/* Should never reach here! */
return 0;
}