cancel
Showing results for 
Search instead for 
Did you mean: 

Atomic variable for scheduler question

Carl_G
Senior

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.

27 REPLIES 27
gbm
Principal

Ok, maybe I am too dumb but what exactly kind of synchronization is needed for bool variable access which is not modified (so no RMW access), just set, reset or tested?

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

What do you mean by "not modified"? The bool in my example is modified by both processes.

gbm
Principal

No, it is not modified. It is set to 0 or to 1. So, what would "locking" or "atomic access" change? It could maybe be needed if the numeric variable was incremented or decremented, but it is not.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

Per your definition, no boolean ever needs synchronization. Correct?

gbm
Principal

Not if it is always set by one party and cleared by another (one only).

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

Are you being pedantic about the term "synchronization"? Because clearly in multithreaded environments you need to ensure changes made by one thread are seen by another with some deterministic sequence and timing.

gbm
Principal

Right, but 1. this is not related to "atomicity" and 2. does not apply to physically single-core and single-threaded microcontroller.

With a single-core, single-threaded microcontroller, you would encounter the problem if a variable of a numeric type was incremented/decremented by two or more pieces of code in two or more threads or a thread and ISR. AFAIK, this is not the case we are discussing here.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

I think you are taking this term Atomicity to only refer to corrupting the value but there is more to it than that.

If you look at the standard concurrency example of two threads incrementing a number there are 2 issues.

1. A non-atomic sized number can get torn into a nonsense number. e.g. a 16-bit number incrementing on an 8-bit MCU.

2. Out of sequence increment causing increments to be lost/overwritten.

For this 1bit scenario the concern is only #2. Values being lost. As soon as you introduce IRQs you create this problem. Even on single core single threaded mcu.

gbm
Principal

Right, BUT NOT FOR BOOLEAN SET & RESET.

In ARM-M architecture the problem appears when you modify a 64-bit number.

It also appears when any-size the value is modified (arithmetic or logic operation) by low priority code and tested by high priority code.

It could appear during boolean operations on memory variables - and/or/xor/not, cause they are not atomic at hardware level.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

True for #1 but what about issue #2?