c++memory-barriersmemory-model

Can't get c++'s seq_cst memory model to work


https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync I took an example from this article from the section acquire/release and rewrote it with bools.

 -Thread 1-
 y.store (20, memory_order_release);

 -Thread 2-
 x.store (10, memory_order_release);

 -Thread 3-
 assert (y.load (memory_order_acquire) == 20 && x.load (memory_order_acquire) == 0)

 -Thread 4-
 assert (y.load (memory_order_acquire) == 0 && x.load (memory_order_acquire) == 10)

This article says:

If this example were written using the sequentially consistent model, then one of the stores must happen-before the other (although the order isn't determined until run-time), the values are synchronized between threads, and if one assert passes, the other assert must therefore fail.

Why does my program sometimes print success!!! iteration number: if I'm using seq_cst memory order?

#include <atomic>
#include <thread>
#include <vector>
#include <iostream>

bool test() {
    std::atomic_bool is_started{false};

    std::atomic_bool x{false};
    std::atomic_bool y{false};

    std::atomic_bool x_before_y{false}; // indicates that store(x, true) happened-before store(y, true)
    std::atomic_bool y_before_x{false}; // indicates that store(y, true) happened-before store(x, true)

    std::thread t1([&]{ // -Thread 1-
        while (!is_started.load()) {}

        y.store(true, std::memory_order_seq_cst); // store(y, true)
    });

    std::thread t2([&]{ // -Thread 2-
        while (!is_started.load()) {}

        x.store(true, std::memory_order_seq_cst); // store(x, true)
    });

    std::thread t3([&] { // -Thread 3-
        while (!is_started.load()) {}

        if (!y.load(std::memory_order_seq_cst) && x.load(std::memory_order_seq_cst)) {
            x_before_y.store(true);
        }
    });

    std::thread t4([&]{ // -Thread 4-
        while (!is_started.load()) {}

        if (y.load(std::memory_order_seq_cst) && !x.load(std::memory_order_seq_cst)) {
            y_before_x.store(true);
        }
    });

    is_started.store(true);

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    return x_before_y.exchange(false) && y_before_x.exchange(false);
}

int main() {
    for (int i = 0; i < 100000000; i++) {
        if (test()){ 
            std::cout << "success!!! iteration number: " << i << std::endl;
            return 1;
        }
        if (i % 1000000 == 0) {
            std::cout << "still running" << std::endl;
        }
    }
    
    std::cout << "fail :(" << std::endl;
    return 0;
}

Solution

  • As @j6t mentioned in comment, example from the article doesn't show the difference between the memory orders