Difference between SemaphoreHandle_t and critical section in esp-idf

braincat
Posts: 11
Joined: Tue Jun 15, 2021 9:54 pm

Difference between SemaphoreHandle_t and critical section in esp-idf

Postby braincat » Sat Jul 24, 2021 11:17 pm

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.

ESP_Sprite
Posts: 9008
Joined: Thu Nov 26, 2015 4:08 am

Re: Difference between SemaphoreHandle_t and critical section in esp-idf

Postby ESP_Sprite » Sun Jul 25, 2021 1:56 am

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.

braincat
Posts: 11
Joined: Tue Jun 15, 2021 9:54 pm

Re: Difference between SemaphoreHandle_t and critical section in esp-idf

Postby braincat » Sun Jul 25, 2021 3:19 am

That's an awesome answer, and thank you very much for your help.

braincat
Posts: 11
Joined: Tue Jun 15, 2021 9:54 pm

Re: Difference between SemaphoreHandle_t and critical section in esp-idf

Postby braincat » Sun Jul 25, 2021 3:40 pm

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!

ESP_Sprite
Posts: 9008
Joined: Thu Nov 26, 2015 4:08 am

Re: Difference between SemaphoreHandle_t and critical section in esp-idf

Postby ESP_Sprite » Mon Jul 26, 2021 2:05 am

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: No registered users and 76 guests