Search code examples
c++lockingatomicmemory-barriersstdatomic

std::atomic - behaviour of relaxed ordering


Can the following call to print result in outputting stale/unintended values?

std::mutex g;
std::atomic<int> seq;
int g_s = 0;
int i = 0, j = 0, k = 0; // ignore fact that these could easily made atomic

// Thread 1
void do_work() // seldom called
{
    // avoid over
    std::lock_guard<std::mutex> lock{g};
    i++; 
    j++;
    k++;
    seq.fetch_add(1, std::memory_order_relaxed);
}

// Thread 2
void consume_work() // spinning
{
    const auto s = g_s;
    // avoid overhead of constantly acquiring lock
    g_s = seq.load(std::memory_order_relaxed);
    if (s != g_s)
    { 
       // no lock guard
       print(i, j, k);
    }
}

Solution

  • Even ignoring the staleness, this is causes a data race and UB.

    Thread 2 can read i,j,k while thread 1 is modifying them, you don't synchronize the access to those variables. If thread 2 doesn't respect the g, there's no point in locking it in thread 1.