C. Sano C. Sano - 4 months ago 10
C Question

Correct way to turn on and check for a runtime flag that's only changed once

In C (especialy when working with microcontrollers), I've encountered situations where I start a thread that waits for some initial flag to be set. For the sake of this example, let's just say that I have a thread that only can truly start once some specific packet has been received. Once the flag is said, it will never be unset! The way I handle would be like this:

int receive_flag = 0;
void thread()
{
while (1)
{
usleep(SOME_CONSTANT_DELAY);
if (!receive_flag)
continue;
else
// this code should always run after some init happens
}
}


And then maybe in my generic packet receiver (different source file), I'd have something like

void process_packet(const byte p[])
{
// imagine that I have checks to see if
// packet is valid (size > 0, certain format, blabla)
if (p[0] == 0xFF) // maybe the first instance of this header
// will trigger the above thread to actually do stuff
settheflag(); // imagine I have a settheflag API exposed that changes
// receive_flag to 1
else
// do other stuff
}


Because my question is about the style/optimizations, I intentionally made the code incomplete (perhaps I should have done psuedo-code, but oh well), but I hope you guys get the idea. Assume that the rest of the code around it are correct, and I do not care if the thread starts a couple of iterations too late (so I don't care about any async issues).

Is this the right way to approach this situation? My concerns are:


  • In the looping thread, is the if(!receive_flag) going to cause extra performance concerns? Esp. because receive_flag should never be 0 AFTER it gets set once.

  • In the packet handler, is it better to check if the flag is set BEFORE setting the flag or just always set the flag (this is regarding performance, I know both would work... It's a matter of is it better to write once and always read after OR always write)



If there is an outright better way to handle this, that'll also be appreciated!

This also might be OS dependent... If so, I'm for now concerned with eLinux. If it needs to be more specific (with processor and stuff), let's just say this is for Raspberry pi 2A: 900MHz quad-core ARM Cortex-A7 CPU on the latest Raspian OS. If you want, you are free to use other architectures, but please try to keep it in the context of embedded systems.

I'll also be appreciative of C++ (up to C++11) solutions, but I'd prefer just C solutions.

EDIT:
For clarification, I cannot start the thread from the process_packet function for code organization purposes.

Answer Source
  • better: restructure your loop so you don't keep checking something that can't change back:

    void thread() {
      // wait until we're ready
      while (!receive_flag) {
         usleep(SOME_CONSTANT_DELAY);
      }
      // init steady state here
      // then enter steady state loop
      while (1) {
         // your code here
      }
    }
    

    (you should probably make your flag a std::atomic in this case, and definitely not rely on volatile)

  • better still: use proper synchronization rather than busy-waiting

    void thread() {
      // wait until we're ready
      {
         std::unique_lock<std::mutex> lock(flag_mutex);
         while (!receive_flag) {
             flag_condvar.wait(lock);
         }
      }
      // init steady state here ...
    
  • even better: if you have "... a settheflag API exposed ...", you can add a matching waitfortheflag() function you call once at the start of your thread function. Just move the initial wait loop into that.

  • best: don't start the thread before you're ready for it to run.

    You can move the thread start inside your existing settheflag API function.

    (there may be good reasons to avoid starting a thread from your packet handler, eg. if it's expensive, but this is probably cleanest)