Home > Back-end >  How to atomically read multiple variable at once in C?
How to atomically read multiple variable at once in C?

Time:06-25

I am trying to read three variables a, b, c atomically at once. The pattern looks something like the code below.

_Atomic uint32_t a, b, c;

void thread_high_priority(void)
{
  atomic_fetch_sub_explicit(&a, 1, memory_order_relaxed);
  atomic_fetch_add_explicit(&b, 1, memory_order_relaxed);
  atomic_fetch_sub_explicit(&c, 1, memory_order_relaxed);
}

void thread_low_priority(void)
{
  uint32_t _a = a;
  uint32_t _b = b;
  uint32_t _c = c;
}

thread_high_priority is a thread running in high priority and thread_low_priority running in low priority. thread_high_priority can interrupt the execution of thread_low_priority, but not the other way. That is, thread_high_priority will always run uninterruptedly.

The constraint is that thread_high_priority is time-critical. Therefore, I don't want to use a mutex to block as it is time-consuming and even causes deadlock. Is there a way to make sure all three variables are read at once without interruption?

Edit: The platform is ARMv7M architecture running in baremetal environment.

CodePudding user response:

You can solve this problem with a level of indirection.

As long as there is only one writer, you could do it like this:

  • Put the set of data items in a struct
  • Allocate several such structs
  • Write non-atomically to the members of a struct which the readers are not using
  • Atomically change a pointer to which struct the reader should use

The reader should read the pointer then access the data in the respective struct.

If it is possible that another interrupt occurs while the main context is still reading then you need to keep a pointer to which struct the reader is using, and the writer can check this before filling out the struct. Accessing this second pointer atomically is easier if there is only one reader.

To smooth things out you can allocate three or more structs, and treat them as a ring buffer.

CodePudding user response:

I also come up with another solution to check whether an interrupt occurs during reading a, b, c variables. It uses another variable of type atomic_flag to mark the occurrence of interruption. If an interrupt occurs, it simply redo reading the variables a, b, c.

_Atomic uint32_t a, b, c;
atomic_flag aflag;

void thread_high_priority(void)
{
  atomic_flag_clear(&aflag);
  atomic_fetch_sub_explicit(&a, 1, memory_order_relaxed);
  atomic_fetch_add_explicit(&b, 1, memory_order_relaxed);
  atomic_fetch_sub_explicit(&c, 1, memory_order_relaxed);
}

void thread_low_priority(void)
{
  uint32_t _a;
  uint32_t _b;
  uint32_t _c;
  
  while (!atomic_flag_test_and_set(&aflag))
    ;
  do {
    _a = a;
    _b = b;
    _c = c;
  } while (!atomic_flag_test_and_set(&aflag));
}
  • Related