FreeRTOS - Resource management
Resources that are shared between tasks or between tasks and interrupts needs to be managed using a ‘mutual exclusion’ technique to ensure data consistency.
Basic Critical Sections
- Basic critical sections protect a region of code from access by other tasks and by interrupts
- Regions of code that are surrounded by calls to the macros taskENTER_CRITICAL() and taskEXIT_CRITICAL() respectively,
/* Ensure access to the PORTA register cannot be interrupted by placing it with in a critical section. */ taskENTER_CRITICAL(); /* A switch to another task cannot occur between the call to taskENTER_CRITICAL() * and the call to taskEXIT_CRITICAL(). Interrupts may still execute on FreeRTOS Ports * that allow interrupt nesting, but only interrupts whose priority is above the value * assigned to the configMAX_SYSCALL_INTERRUPT_PRIORITY constant - and those interrupts * are not permitted to call FreeRTOS API functions. */ PORTA |= 0x01; /* We have finished accessing PORTA so can safely leave the critical section */ taskEXIT_CRITICAL();
- A very crude method of providing mutual exclusion as all or partial interrupts are disabled
Suspending (Locking) the Scheduler
- Critical sections can also be created by suspending the scheduler
- A critical section that is too long to be implemented by simply disabling interrupts can instead be implemented by suspending the scheduler
- The scheduler is suspended by calling vTaskSuspendAll()
void vTaskSuspendAll( void );
The scheduler is resumed by calling xTaskResumeAll()
portBASE_TYPE xTaskResumeAll( void );
- A Mutex is a special type of binary semaphore that is used to control access to a resource that is shared between two or more tasks
- The mutex can be conceptually thought of as a token associated with the resource being shared
- For a task to legitimately access the resource it must first successfully ‘take’ the token
- When the token holder has finished with the resource it must ‘give’ the token back.
Create a Mutex
Use xSemaphoreCreateMutex() to create a mutex
xSemaphoreHandle xSemaphoreCreateMutex( void );
Priority Inversion & Priority Inheritance
- With a mutex, it is possible that a task with a higher priority has to wait for a task with a lower priority which hold the mutex to give up the control of the mutex
- A higher priority task being delayed by a lower priority task in this manner is called ‘priority inversion’.
- Priority inheritance works by temporarily raising the priority of the mutex holder to that of the highest priority task that is attempting to obtain the same mutex
- The priority of the mutex holder is automatically reset back to its original value when it gives the mutex back
- Priority inheritance does not ‘fix’ priority inversion, it merely lessens its impact.
Deadlock occurs when two tasks are both waiting for a resource that is held by the other, e.g.,
- Task A executes and successfully takes mutex X.
- Task A is pre-empted by Task B.
- Task B successfully takes mutex Y before attempting to also take mutex X – but mutex X is held by Task A so is not available to Task B. Task B opts to enter the Blocked state to wait for mutex X to be released.
- Task A continues executing. It attempts to take mutex Y – but mutex Y is held by Task B so is not available to Task A. Task A opts to enter the Blocked state to wait for mutex Y to be released.
- Gatekeeper tasks provide a clean method of implementing mutual exclusion without the worry of priority inversion or deadlock
- A gatekeeper task is a task that has sole ownership of a resource
- A task needing to access the resource can only do so indirectly by using the services of the gatekeeper