Hello All,
I am fairly new to esp-idf and seeking information about a difference between semaphores and
critical sections in esp-idf. Digging through online posts, the only difference I found so far is that critical sections disable interrupts when protecting shared resources, and semaphores do not. What else should I be aware of?
A related Question #2 -- I need to implement some reference counting for my objects. I cannot use shared_ptr because it does not play well with freertos queues, and I also need write access to the reference counter. What would be the best way to implement atomic read-modify-write on an uint32 value? On esp32, reads and writes to uint32_t are atomic, but not RMW operation. Should I use freertos semaphore or a critical section? My objects are not accessed in ISRs.
Thank you in advance.
Difference between SemaphoreHandle_t and critical section in esp-idf
-
- Posts: 9883
- Joined: Thu Nov 26, 2015 4:08 am
Re: Difference between SemaphoreHandle_t and critical section in esp-idf
I think you specifically mean a mutex instead of a semaphore? That comes closest to the purpose of a critical section. Also, while in plain old FreeRTOS, a critical section is implemented by disabling interrupts, we can't do that in ESP-IDF as there is a second core that could still access the resource. For this reason, in ESP-IDF, critical sections use spinlocks instead.
In practice, the difference is what happens when a second thread tries to access a resource locked by either the mutex or the critical section. For a mutex, FreeRTOS will de-schedule the task in order to do something else. If there's nothing else to do, FreeRTOS will run the idle task, which will make the CPU save some power. As soon as the resource is released, FreeRTOS will schedule the thread again.
A spinlock-based critical section will just sit there instead, actively polling if the resource is already available.
The difference in practice is that 1. a mutex is more efficient for operations that take longer, as it allows the OS to do other things, and 2. a spinlock is more efficient for operations that take only a few cycles, as de-scheduling and re-scheduling the task (which is what happens with a mutex) costs quite a few cycles.
The upshot: if the operation you want to protect is simply a read-modify-write, use a critical section. If the operation you want to protect takes longer (say, shoving bytes into an UART), use a mutex.
There's actually a third option. While a RMW operation is not atomic, the ESP32 actually has an atomic-compare-set instruction that can be used to create 'atomic' writes without spinlocks. Easiest way to do that is to use e.g. the GCC builtin atomics functions.
In practice, the difference is what happens when a second thread tries to access a resource locked by either the mutex or the critical section. For a mutex, FreeRTOS will de-schedule the task in order to do something else. If there's nothing else to do, FreeRTOS will run the idle task, which will make the CPU save some power. As soon as the resource is released, FreeRTOS will schedule the thread again.
A spinlock-based critical section will just sit there instead, actively polling if the resource is already available.
The difference in practice is that 1. a mutex is more efficient for operations that take longer, as it allows the OS to do other things, and 2. a spinlock is more efficient for operations that take only a few cycles, as de-scheduling and re-scheduling the task (which is what happens with a mutex) costs quite a few cycles.
The upshot: if the operation you want to protect is simply a read-modify-write, use a critical section. If the operation you want to protect takes longer (say, shoving bytes into an UART), use a mutex.
There's actually a third option. While a RMW operation is not atomic, the ESP32 actually has an atomic-compare-set instruction that can be used to create 'atomic' writes without spinlocks. Easiest way to do that is to use e.g. the GCC builtin atomics functions.
Re: Difference between SemaphoreHandle_t and critical section in esp-idf
That's an awesome answer, and thank you very much for your help.
Re: Difference between SemaphoreHandle_t and critical section in esp-idf
Out of curiosity, what would happen on ESP32 if I were to use intrinsics listed here:
https://gcc.gnu.org/onlinedocs/gcc/_005 ... ltins.html
and specify a memory model?
Thanks!
https://gcc.gnu.org/onlinedocs/gcc/_005 ... ltins.html
and specify a memory model?
Thanks!
-
- Posts: 9883
- Joined: Thu Nov 26, 2015 4:08 am
Re: Difference between SemaphoreHandle_t and critical section in esp-idf
I'm not entirely up to speed on C++ memory models, but the way I read it, you don't get to specify a memory model. The C++ memory model effectively specifies the expected behaviour for various operations in a multithreaded environment wrt what they can and cannot return, but also specifies what the user (well, the C++ compiler and standard library in the case of C++) must do in order to make the memory model work. In the case of atomics, I think that all it means in practice is that you shouldn't mix atomic and non-atomic accesses to an atomic variable as the second could lead to a data race. (Note that this is all very theoretical. Memory models like this effectively are invented to 'plaster over' things like out-of-order processors and non-coherent memories. The ESP32 series all have very simple in-order processors and by nature already have a machine architecture that 'does what you expect it to do', so the chances of madness happening because you abuse things like this in ways that are not intended are low. I'll leave it open if that's a good or a bad thing.)
Who is online
Users browsing this forum: Google [Bot] and 67 guests