Coding style for atomic execution - Page 1
Forum section: electronics / general

author: ricko_uk
author_rank: Super Contributor
timestamp: April 14, 2021, 10:28:52 pm
content: Hi,is there a preferred style when writing code like the one below? Out of the two options, which one is the preferred and why?I personally always use the first one which creates an additional variable but it has a 1:1 match of the Enter/ExitCritical.taskENTER_CRITICAL(); and taskEXIT_CRITICAL(); are freeRTOS functions to lock INTs for critical sections like ensuring atomic code execution.First Option...:taskENTER_CRITICAL(); //stops all INTstmpVar = xVarToTestAtomically; //xVarToTestAtomically is not the core's native data type so accessing it is not an atomic operationtaskEXIT_CRITICAL(); //restarts all INTsif (tmpVar){ExitThisFunctionRightHere();}DoSomethingElse();Second option...:taskENTER_CRITICAL(); //stops all INTsif (xVarToTestAtomically) //xVarToTestAtomically is not the core's native data type so accessing it is not an atomic operation{taskEXIT_CRITICAL(); //restarts all INTsExitThisFunctionRightHere();}taskEXIT_CRITICAL(); //restarts all INTsDoSomethingElse();Thank you

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 14, 2021, 10:37:29 pm
content: The first option is obviously cleaner and less error-prone, but beyond that, it's also more efficient if whatever you need to do with the variable takes some time, as the critical section only blocks for the time it takes to copy the variable.

author: langwadt
author_rank: Super Contributor
timestamp: April 14, 2021, 10:45:47 pm
content: Quote from: SiliconWizard on April 14, 2021, 10:37:29 pmand you can wrap it up in a define (or inline function)

author: lucazader
author_rank: Regular Contributor
timestamp: April 14, 2021, 10:52:30 pm
content: You could probably look in the C atomic types. (available since c11)lots of different data types in there as well as the ability to create your own atomic structs etc.https://en.cppreference.com/w/c/atomicThis should emit the relevant instructions on any platform that you run on to ensure the atomic nature of the variable you are interested in.

author: ricko_uk
author_rank: Super Contributor
timestamp: April 15, 2021, 12:22:12 am
content: Thank you all

author: mikerj
author_rank: Super Contributor
timestamp: April 15, 2021, 12:24:22 am
content: Quote from: ricko_uk on April 14, 2021, 10:28:52 pmBe careful, on an Arm Cortex micro these only disable interrupts with a priority at or below configMAX_SYSCALL_INTERRUPT_PRIORITY i.e. interrupts that can call RTOS API functions. Higher priority interrupts will still be running.

author: ricko_uk
author_rank: Super Contributor
timestamp: April 15, 2021, 02:59:14 am
content: Thank you Mikerj

author: jc101
author_rank: Frequent Contributor
timestamp: April 15, 2021, 05:08:37 pm
content: If you are looking to compare a variable in one task, but you may be altering it in another task, would it be better to wrap the access with a mutex (binary semaphore in FreeRTOS)?Created with xSemaphoreCreateMutex, then you take it with xSemaphoreTake and give it back with xSemaphoreGive when you're done.

author: ricko_uk
author_rank: Super Contributor
timestamp: April 15, 2021, 11:37:16 pm
content: Thank you jc101,newbie to RTOS. What is the difference between my approach and the one you suggest? I'm not familiar with mutex/semaphores.Thank you

author: newbrain
author_rank: Super Contributor
timestamp: April 16, 2021, 07:51:17 am
content: Quote from: ricko_uk on April 15, 2021, 11:37:16 pmThe major difference is that entering a critical section with taskENTER_CRITICAL() will stop any other activities or tasks going on at the same time (with the caveat explained by mikerj), in practice the RTOS is suspended until taskEXIT_CRITICAL().This mean that also tasks that do not depend on that specific variable are blocked, independent from their priority.In very general terms, the less and shorter critical sections, the better.Taking and releasing a mutex or semaphore, instead, will only affect the two involved tasks.If you are a using an RTOS, semaphores, mutexes, queues are something you should get familiar with, FreeRTOS has an oldish but still relevanttutorial book, with plenty of examples and explanations.

author: jc101
author_rank: Frequent Contributor
timestamp: April 16, 2021, 05:43:53 pm
content: Quote from: ricko_uk on April 15, 2021, 11:37:16 pmAs stated in another post, using a mutex doesn't stop the whole system whilst giving you the behaviour of exclusive access to something when you need it. Mutex is Mutually Exclusive access to something.The FreeRTOS website has lots of well commented examples for the basic features. The books here are worth readinghttps://www.freertos.org/Documentation/RTOS_book.html

author: ricko_uk
author_rank: Super Contributor
timestamp: April 17, 2021, 12:19:25 pm
content: Thank you newbrain and jc101

author: peter-h
author_rank: Super Contributor
timestamp: April 18, 2021, 12:56:59 pm
content: Why is there no simple DI / EI ?I know one problem with "EI" is that it would re-enable interrupts even if they were disabled, which would prevent a generic function to be called from both "EI" code and "DI" code, but you could easily detect the int enable status (on the Z80 it was an intentional side effect of one instruction) and re-enable ints only if they were previously enabled.

author: Siwastaja
author_rank: Super Contributor
timestamp: April 18, 2021, 02:40:04 pm
content: Quote from: peter-h on April 18, 2021, 12:56:59 pmThat is an option but the OP intentionally wants to use an RTOS and the whole point of OS is to abstract away hardware, providing higher-level and consistent interfaces.I personally think this line of thought is overrated because many of the "useful abstractions" are borrowed from non-RT OSes which again work against the long history and highly variable hardware of general purpose computing, where uniform standards are a necessity, and committees coming up with standards such as POSIX are the necessary evil. The end result is, code written for RTOS tends to take longer to write, is longer, adds overhead and prevents using of some features present in the microcontroller, and finally, doesn't necessarily offer the portability the designers wanted.The proof is in the examples; the OP used the RTOS abstractions, but somehow come to the conclusion that adding a comment that describes whatactuallyhappens on the low level, is necessary. I don't blame them.As you say, on microcontrollers, atomic execution can be as simple as disabling and enabling interrupts. Parallelism is also, in most cases, extremely simple, to the point that constructing a typical "parallel" MCU application using interrupts comes easier than learning just the syntax to use things like pthreads, or any interface an RTOS provides. Of course, in some more complex cases, doing everything from scratch becomes more work, but MCU projects usually aren't that complex.Personally, I'm not a big fan of abstracting away atomic accesses to "automate" the re-enabling of interrupts to the existing state; the motivation is such automation will allow nesting atomic operations, encouraging you to use such operationsanywherebecause it's "easier" and "safer". No, atomic operations and disabling/enabling interrupts temporarily, in my mind, is a special operation which always happens with care and consideration, and is documented using comments. So then I just disable and re-enable the interrupts, and don't allow myself to create a complex condition where I don't know if the interrupts were already disabled and should not be re-enabled.Low level programming on microcontrollers is a strength, not a hindrance, and I don't like abstracting it away. Desktop computing is different.

author: ricko_uk
author_rank: Super Contributor
timestamp: April 20, 2021, 06:11:57 pm
content: Thank you Siwastaja