Home > Net >  why does memory_order_seq_cst didn't protect my atomic-operation running sequence?
why does memory_order_seq_cst didn't protect my atomic-operation running sequence?

Time:10-28

#include <assert.h>
#include <atomic>
#include <iostream>
#include <thread>
std::atomic_bool b(false);
std::atomic_bool lock{false};

void producer() {
  b.store(true, std::memory_order_seq_cst);
  lock.store(true, std::memory_order_seq_cst);
}

void consume() {
  while (!lock.load(std::memory_order_seq_cst))
    ;
  assert(b.load(std::memory_order_seq_cst));

  b.store(false, std::memory_order_seq_cst);
  lock.store(false, std::memory_order_seq_cst);
}

int main() {
  std::thread t1([&]() {
    while (true)
      consume();
  });
  std::thread t2([&]() {
    while (true)
      producer();
  });

  t1.join();
  t2.join();
}

Assert in consume should never fail, memory_order_seq_cst guarentee that atmoic operation run in the order that they are wrote;

But assert fail happened :(

CodePudding user response:

Your analysis would be correct for a single pair of produce() and consume() but not for an endless loop of them.

Thread 1 Thread 2
b.store(true, std::memory_order_seq_cst);
lock.store(true, std::memory_order_seq_cst);
while (!lock.load(std::memory_order_seq_cst));
assert(b.load(std::memory_order_seq_cst));
b.store(true, std::memory_order_seq_cst);
b.store(false, std::memory_order_seq_cst);
lock.store(false, std::memory_order_seq_cst);
lock.store(true, std::memory_order_seq_cst);
while (!lock.load(std::memory_order_seq_cst));
assert(b.load(std::memory_order_seq_cst)); Assert fires!

The table shows an ordering where b is false and lock is true.

If you want that guarantee, you need to have produce wait for lock to be false.

void producer() {
  while (lock.load(std::memory_order_seq_cst))
    ;
  b.store(true, std::memory_order_seq_cst);
  lock.store(true, std::memory_order_seq_cst);
}

CodePudding user response:

void producer() {
  b.store(true, std::memory_order_seq_cst);      // 1
  lock.store(true, std::memory_order_seq_cst);   // 2
}

void consume() {
  while (!lock.load(std::memory_order_seq_cst))
    ;                                            // 3
  assert(b.load(std::memory_order_seq_cst));     // 4

  b.store(false, std::memory_order_seq_cst);      // 5
  lock.store(false, std::memory_order_seq_cst);   // 6
}

The following steps will make the assert fail: 1 -> 5 -> 6 -> 2 -> 3 -> 4

  • Related