2025-03-07 8:19 AM
I traditionally use a volatile type for my schedulers. Very simple like so
volatile uint8_t myflag;
uint8_t ticks;
void Func(void){
++ticks;
if(!(ticks % 5)){
myflag = true;
}
}
void User(void){
if(myflag) {
myflag = false;
//DO Something
}
}
I'm trying to update my knowledge. The online thought seems to be that this isn't really the safe way as this does not have any memory barrier. So the proper way is to use an atomic. So I tried an atomic.
atomic_bool myflag = ATOMIC_VAR_INIT(false);
uint8_t ticks;
void Func(void){
++ticks;
if(!(ticks % 5)){
atomic_store(&myflag ,true);
}
}
void User(void){
if(atomic_load(&myflag)) {
atomic_store(&myflag ,false);
//DO Something
}
}
However, this definitely does not work. The User misses flag settings regularly. Its not just a delay. But per the scope its like a complete flag setting is missed. What have I done wrong here? I even tried to make it volatile and also tried atomic_flag but they both miss settings but the old school way does not miss any settings.
2025-03-10 8:23 AM - edited 2025-03-10 8:25 AM
If you only set the flag (volatile bool) in one thread and only check/reset it in the other thread, which is what you do in your example, you do not have this issue. With incrementing, yes it can be an issue but it depends on the details here. If only one thread modifies a regular sized integer, you don't have an issue.
If you want a memory barrier, you can do a DSB instruction. However, unless you are also modifying other data that needs to be set before the flag is processed, this is not necessary.
For embedded platforms like the STM32, the "volatile" flag is sufficient to ensure the data is read/written every time it is encountered.
2025-03-10 8:31 AM
@TDK you mean if I was updating a separate variable then setting the flag to let another "thread" know the variable was updated then just making the bool volatile would in theory not be enough?
2025-03-10 9:12 AM
Not sure what you mean by issue #2. I believe I addressed it above -> modification based on current value, like increment/decrement/whatever but not assignment like x = 5 or flag = true. When you use constant value assignment, it's atomic at hardware level - the value is only written, not read, so there is no "sequence".
2025-03-10 9:24 AM
If you don't know what I mean by #2 you need to reread my post. Its the only relevant point. You are fixated on torn values. I keep explaining that is not the only issue with concurrency. If you find any concurrency example online it will probably include the counting example where numbers are incorrectly skipped. You are consistently ignoring this.
2025-03-10 9:26 AM
Correct. There are few issues.
2025-03-10 10:14 AM
... and none of the above (other than volatile) applies to a boolean flag being set by ISR and reset by some other code. :)
2025-03-10 10:23 AM
I can't disagree with that :thumbs_up:
2025-03-10 10:48 AM - edited 2025-03-10 10:50 AM
The stdatomic API has memory barrier semantics, even if access to one byte is atomic by itself. By default it uses the strongest memory barrier (CST). This can generate MB instructions (overhead in execution time and code size). Otherwise, for CM0 (which has no D cache) the behavior *should* not differ. But you see it differs.