Follow our illustrated blog on Embedded Software Architecture

How to build a hybrid solar/wind energy harvester?

Mutexes and semaphores: the FreeRTOS implementation

Mutexes and semaphores: the FreeRTOS implementation, 8.7 out of 10 based on 7 ratings
VN:F [1.9.22_1171]
Rating: 8.7/10 (7 votes cast)

Let us start with an interesting quote from the FreeRTOS web site:

Binary semaphores are used for both mutual exclusion and synchronisation purposes.

The quote clearly makes a distinction between the two use cases we saw in part 1:

  • mutual exclusion: how to serialize access to shared data, and
  • synchronization: how does a producer notify a consumer.

In our experience, people tend to use the term ‘synchronization’ for both use cases. But, mutual exclusion is more a protection than a synchronization mechanism (see part 1).

The concept of a mutex can be used to solve the mutual exclusion problem while a semaphore is useful for the signaling.

FreeRTOS mutex and semaphore

FreeRTOS mutex and semaphore implementation

The figure shows how FreeRTOS implements support for those two use cases and concepts. Both concepts make use of an underlying semaphore implementation. Here’s the API (copied from the FreeRTOS website):

  • vSemaphoreCreateBinary
  • xSemaphoreCreateCounting
  • xSemaphoreCreateMutex
  • xSemaphoreCreateRecursiveMutex
  • vSemaphoreDelete
  • xSemaphoreGetMutexHolder
  • xSemaphoreTake
  • xSemaphoreTakeFromISR
  • xSemaphoreTakeRecursive
  • xSemaphoreGive
  • xSemaphoreGiveRecursive
  • xSemaphoreGiveFromISR

…but, in order to avoid confusion, it is better to split the api in two parts: mutex-concept functions and semaphore-concept functions.

These are the mutex functions:

  • xSemaphoreCreateMutex: creates a binary semaphore with priority inheritance (this means that the priority of the task which holds the mutex is temporarily increased to the highest priority of all blocked tasks. This mitigates priority inversion.).
  • xSemaphoreTake, xSemaphoreGive: acquires and releases a mutex.
  • xSemaphoreCreateRecursiveMutex, xSemaphoreTakeRecursive, xSemaphoreGiveRecursive: allows you to recursively take a mutex. In order to release the mutex, the number of calls to xSemaphoreGiveRecursive must match the number of calls to xSemaphoreTakeRecursive.
  • xSemaphoreGetMutexHolder: remember the notion of ownership? Only one task is allowed to hold the mutex. The holder (the one that took the mutex with xSemaphoreTake) is also responsible to release it.
  • vSemaphoreDelete: destroys the mutex.

The mutex functions are not supposed to be used in interrupt context and that is the reason the ‘FromISR’ functions are not part of the mutex function list (an Interrupt Service Routine (ISR) cannot block when it tries to take a mutex e.g. xSemaphoreTakeFromISR is a ‘try-to-lock’ function).

These are the semaphore functions:

  • vSemaphoreCreateBinary: creates a semaphore (for signaling) with count one.
  • xSemaphoreCreateCounting: creates a counting semaphore.
  • xSemaphoreGive, xSemaphoreTake: sends and receives a signal. Because the underlying implementation uses task queues, it is not possible to use these functions from interrupt context. In an ISR, one can use next two functions:
  • xSemaphoreGiveFromISR, xSemaphoreTakeFromISR: in irq context, the ‘give’ operation is more common: the ISR signals a task because an event happened (e.g. data arrived or data was sent).
  • vSemaphoreDelete: destroys the semaphore.

FreeRTOS explicitly has a separated set of functions for execution in ISRs. From a developer’s perspective, a unified api (one function which can be used in isr or thread) is preferable. Not sure why FreeRTOS does not provide this, maybe it is the slight performance impact (abstraction always has a cost which in this case is checking the cpu context), or maybe the FreeRTOS developers prefer an explicit over an implicit interface, or maybe it is just legacy…

FreeRTOS semaphore low-level implementation

So, both mutex and concept-level semaphores are implemented by semaphores. On their part, semaphores are built on FreeRTOS queues. This is not unexpected because when a task blocks on a mutex or is waiting for a signal, the OS must have a way to link the blocked tasks to the corresponding semaphore so that later on, when the mutex is released or the signal arrived, the OS can resume the blocked task. There is one exception though: an ISR cannot block and, as such, cannot be queued. The Give and Take operations just test and return (instead of waiting for a result).

FreeRTOS queue implementation (in the context of mutexes/semaphores)

When a task is pushed or popped from a queue, the queue is also a shared data resource which must be protected against simultaneous access. There, we need the concept of a mutex. But then, this seems a chicken-egg problem: mutexes are low-level semaphores which are queues that require… mutexes again?? Well, FreeRTOS also provides a low-level, basic mutex by defining the next two functions:

  • taskENTER_CRITICAL(): enter critical region,
  • taskEXIT_CRITICAL(): leave critical region,

These functions do not support task queuing. Their implementation is platform dependent and can be found in the ‘portable’ directory of FreeRTOS. The Cortex-M3 port just disables or enables interrupts when entering or leaving the critical region. Then, in the critical region itself, the processor is all yours (at least if you’re on a uni-processor; as an alternative, the test-and-set locking approach would also work on multi-processors. See for example this article and this article).

VN:F [1.9.22_1171]
Rating: 8.7/10 (7 votes cast)
Share
About rtos.be

rtos.be is a joined initiative from Gert Boddaert and Pieter Beyens.
More info: about us, our mission, contact us.

Speak Your Mind

*