To avoid a possible deadlock as in the previous example, we can add a timeout when acquiring the mutexes in both tasks. This way, the tasks will not wait indefinitely if they fail to obtain the resource, which could indicate a design problem (such as a deadlock).
Modifications with Timeout:
We will use a time limit (timeout) instead of portMAX_DELAY
when calling xSemaphoreTake
. If the mutex is not obtained within this time, the error can be handled appropriately.
Here’s the updated code with a 500-millisecond timeout:
Code Example:
#include "bsp.h"
SemaphoreHandle_t xMutex1; /* Mutex semaphore 1 */
SemaphoreHandle_t xMutex2; /* Mutex semaphore 2 */
static void Task1( void *pvParameters ); /* Task1 To turn on LEDs */
static void Task2( void *pvParameters ); /* Task2 to turn off LEDs */
int main( void )
{
HAL_Init();
SEGGER_SYSVIEW_Conf( );
SEGGER_SYSVIEW_Start( );
xMutex1 = xSemaphoreCreateMutex(); /* Create the first Mutex Semaphore */
xMutex2 = xSemaphoreCreateMutex(); /* Create the second Mutex Semaphore */
xTaskCreate( Task1, "Task1", 240, NULL, 2, NULL ); /* Register Task1 to turn on LEDs */
xTaskCreate( Task2, "Task2", 240, NULL, 1, NULL ); /* Register Task2 to turn off LEDs */
vTaskStartScheduler(); /* Init the Kernel */
}
void Task1( void *pvParameters )
{
const TickType_t xTimeout = pdMS_TO_TICKS(500); /* Timeout of 500ms */
for(;;)
{
if (xSemaphoreTake(xMutex1, xTimeout) == pdTRUE) /* Try to take mutex 1 with timeout */
{
SEGGER_SYSVIEW_PrintfTarget("Task 1 takes the Mutex 1");
vTaskDelay(250);
SEGGER_SYSVIEW_PrintfTarget("Task 1 trying to take Resource 2");
if (xSemaphoreTake(xMutex2, xTimeout) == pdTRUE) /* Try to take mutex 2 with timeout */
{
SEGGER_SYSVIEW_PrintfTarget("Task 1 successfully took Resource 2");
vTaskDelay(250);
SEGGER_SYSVIEW_PrintfTarget("Task 1 gives the Resource 2");
xSemaphoreGive(xMutex2); /* Give the mutex 2 */
}
else
{
SEGGER_SYSVIEW_PrintfTarget("Task 1 failed to take Resource 2");
}
SEGGER_SYSVIEW_PrintfTarget("Task 1 gives the Resource 1");
xSemaphoreGive(xMutex1); /* Give the mutex 1 */
}
else
{
SEGGER_SYSVIEW_PrintfTarget("Task 1 failed to take Mutex 1");
}
}
}
void Task2( void *pvParameters )
{
const TickType_t xTimeout = pdMS_TO_TICKS(500); /* Timeout of 500ms */
for(;;)
{
if (xSemaphoreTake(xMutex2, xTimeout) == pdTRUE) /* Try to take mutex 2 with timeout */
{
SEGGER_SYSVIEW_PrintfTarget("Task 2 takes the Mutex 2");
vTaskDelay(250);
SEGGER_SYSVIEW_PrintfTarget("Task 2 trying to take Resource 1");
if (xSemaphoreTake(xMutex1, xTimeout) == pdTRUE) /* Try to take mutex 1 with timeout */
{
SEGGER_SYSVIEW_PrintfTarget("Task 2 successfully took Resource 1");
vTaskDelay(250);
SEGGER_SYSVIEW_PrintfTarget("Task 2 gives the Resource 1");
xSemaphoreGive(xMutex1); /* Give the mutex 1 */
}
else
{
SEGGER_SYSVIEW_PrintfTarget("Task 2 failed to take Resource 1");
}
SEGGER_SYSVIEW_PrintfTarget("Task 2 gives the Resource 2");
xSemaphoreGive(xMutex2); /* Give the mutex 2 */
}
else
{
SEGGER_SYSVIEW_PrintfTarget("Task 2 failed to take Mutex 2");
}
}
}
Explanation of the Changes:
- Defined Timeout:
- A timeout (
xTimeout
) of 500 ms is defined usingpdMS_TO_TICKS(500)
. - This ensures portability across different system configurations and tick rate settings.
- Mutex State Verification:
- Each time a task attempts to acquire a mutex, it verifies whether the acquisition was successful (
pdTRUE
). - If the acquisition fails, the task logs a message and continues execution, avoiding a potential deadlock scenario.
- Error Handling:
- When a task fails to acquire the second mutex (e.g., due to resource unavailability or a potential deadlock), it performs an alternative action or simply logs the failure.
- Debug messages are used to notify the issue, helping in troubleshooting and improving system reliability.
SystemView Output:
TimeLine:
Task1 and Task2 initially acquire their respective mutexes: Task1 takes Mutex 1. Task2 takes Mutex 2. |
Both tasks attempt to acquire the second mutex: Task1 tries to take Mutex 2. Task2 tries to take Mutex 1. |
Timeouts occur due to contention: Task1 and Task2 fail to acquire the second mutex within the specified 500 ms timeout. Debug messages indicate these failures (e.g., "Task 1 failed to take Resource 2"). |
Tasks release the resources they already hold: After failing, both tasks release their respective mutexes to allow other tasks to proceed. |
Cycle repeats:
|
This output confirms that the timeout mechanism prevents indefinite blocking and potential deadlocks. However, the repetitive failure suggests that the resource allocation strategy might require optimization to reduce contention.