Skip to content

Instantly share code, notes, and snippets.

@CelliesProjects
Created February 19, 2025 19:27
Show Gist options
  • Save CelliesProjects/5adbf69c279c998c13027071f56dfbbd to your computer and use it in GitHub Desktop.
Save CelliesProjects/5adbf69c279c998c13027071f56dfbbd to your computer and use it in GitHub Desktop.
A scoped mutex for a FreeRTOS semaphore handle that prevents deadlocks by ensuring that the mutex is always released, even if exceptions are thrown or the code exits early
#ifndef SCOPEDMUTEX_H
#define SCOPEDMUTEX_H
#include <Arduino.h>
#include <freertos/semphr.h>
/**
* @brief Constructs a ScopedMutex object from `SemaphoreHandle_t` `m` and attempts to acquire the mutex.
*
* Guarantees mutex release when the `ScopedMutex` object goes out of scope.
*
* This class prevents deadlocks by ensuring that the mutex is always released,
* even if exceptions are thrown or the code exits early.
*
* @param m The FreeRTOS semaphore handle.
* @param timeout The timeout in FreeRTOS ticks. Defaults to portMAX_DELAY (indefinite blocking).
* @note - If no timeout is specified, the mutex acquisition will block indefinitely and is guaranteed to return an acquired mutex.
*
* - If a timeout is provided, the `acquired()` method should be used to check if the mutex was successfully acquired.
*
* Example use:
* ```cpp
* // No timeout (blocks indefinitely):
* {
* ScopedMutex lock(myMutex); // Mutex guaranteed to be acquired (blocks)
*
* // Access shared resource here...
*
* } // Mutex automatically released
*
* // With timeout:
* {
* ScopedMutex lock(myMutex, pdMS_TO_TICKS(10));
*
* if (lock.acquired()) { // Check if acquired!
*
* // Access shared resource here...
*
* } else {
*
* // Handle timeout (e.g., log error, retry later)
*
* }
* } // Mutex automatically released
*
* ```
*/
class ScopedMutex
{
private:
SemaphoreHandle_t &mutex;
bool locked;
public:
explicit ScopedMutex(SemaphoreHandle_t &m, TickType_t timeout = portMAX_DELAY)
: mutex(m), locked(xSemaphoreTake(mutex, timeout)) {}
ScopedMutex(const ScopedMutex &) = delete;
ScopedMutex &operator=(const ScopedMutex &) = delete;
/**
* @brief Destroys the ScopedMutex object and releases the mutex if it was acquired.
*/
~ScopedMutex()
{
if (locked)
{
xSemaphoreGive(mutex);
}
}
/**
* @brief Returns true if the mutex was successfully acquired.
* @return True if the mutex was acquired, false otherwise (e.g., due to timeout).
*/
bool acquired() const { return locked; }
};
#endif // SCOPEDMUTEX_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment