FreeRTOS uses its queues for communication between and within tasks. FreeRTOS also uses its queues to implement semaphores and mutexes.
What’s The Difference?
Semaphores and mutexes may sound like the same thing, but they’re not. FreeRTOS implements them similarly, but they’re intended to be used in different ways. How should they be used differently? Embedded systems guru Michael Barr says it best in his article, “Mutexes and Semaphores Demystified”:
The correct use of a semaphore is for signaling from one task to another. A mutex is meant to be taken and released, always in that order, by each task that uses the shared resource it protects. By contrast, tasks that use semaphores either signal [“send” in FreeRTOS terms] or wait [“receive” in FreeRTOS terms] - not both.
A mutex is used to protect a shared resource. A task acquires a mutex, uses the shared resource, then releases the mutex. No task can acquire a mutex while the mutex is being held by another task. This guarantees that only one task is allowed to use a shared resource at a time.
Semaphores are used by one task to signal another task. To quote Barr’s article:
For example, Task 1 may contain code to post (i.e., signal or increment) a particular semaphore when the “power” button is pressed and Task 2, which wakes the display, pends on that same semaphore. In this scenario, one task is the producer of the event signal; the other the consumer.
Implementation
FreeRTOS implements an N-element semaphore as a queue that can hold N items. It doesn’t store any actual data for the queue items; the semaphore just cares how many queue entries are currently occupied, which is tracked in the queue’s uxMessagesWaiting field. It’s doing “pure synchronization”, as the FreeRTOS header file semphr.h calls it.
Therefore the queue has a item size of zero bytes (uxItemSize == 0). Each semaphore access increments or decrements the uxMessagesWaiting field; no item or data copying is needed.
Like a semaphore, a mutex is also implemented as a queue, but several of the xQUEUE struct fields are overloaded using #defines:
/* Effectively make a union out of the xQUEUE structure. */
#define uxQueueType pcHead
#define pxMutexHolder pcTailSince a mutex doesn’t store any data in the queue, it doesn’t need any internal storage, and so the pcHead and pcTail fields aren’t needed. FreeRTOS sets the uxQueueType field (really the pcHead field) to 0 to note that this queue is being used for a mutex. FreeRTOS uses the overloaded pcTail fields to implement priority inheritance for mutexes.
From the article, “Introduction to Priority Inversion”:
[Priority inheritance] mandates that a lower-priority task inherit the priority of any higher-priority task pending on a resource they share. This priority change should take place as soon as the high-priority task begins to pend; it should end when the resource is released.
FreeRTOS implements priority inheritance using the pxMutexHolder field (which is really just the overloaded-by-#define pcTail field). FreeRTOS records the task that holds a mutex in the pxMutexHolder field. When a higher-priority task is found to be waiting on a mutex currently taken by a lower-priority task, FreeRTOS “upgrades” the lower-priority task to the priority of the higher-priority task until the mutex is available again.