简体   繁体   中英

Do dependent reads require a load-acquire?

Does the following program expose a data race, or any other concurrency concern?

#include <cstdio>
#include <cstdlib>

#include <atomic>
#include <thread>

class C {
public:
    int i;
};

std::atomic<C *> c_ptr{};

int main(int, char **) {
    std::thread t([] {
        auto read_ptr = c_ptr.load(std::memory_order_relaxed);
        if (read_ptr) {
            // does the following dependent read race?
            printf("%d\n", read_ptr->i);
        }
    });

    c_ptr.store(new C{rand()}, std::memory_order_release);

    return 0;
}

Godbolt

I am interested in whether reads through pointers need load-acquire semantics when loading the pointer, or whether the dependent-read nature of reads through pointers makes that ordering unnecessary. If it matters, assume arm64, and please describe why it matters, if possible.

I have tried searching for discussions of dependent reads and haven't found any explicit recognition of their implicit load-reordering-barriers. It looks safe to me, but I don't trust my understanding enough to know it's safe.

Your code is not safe, and can break in practice with real compilers for DEC Alpha AXP (which can violate causality via tricky cache bank shenanigans IIRC).


As far as the ISO C++ standard guaranteeing anything in the C++ abstract machine, no, there's no guarantee because nothing creates a happens-before relationship between the init of the int and the read in the other thread.

But in practice C++ compilers implement release the same way regardless of context and without checking the whole program for the existence of a possible reader with at least consume ordering.

In some but not all concrete implementations into asm for real machines by real compilers, this will work. (Because they choose not to look for that UB and break the code on purpose with fancy inter-thread analysis of the only possible reads and writes of that atomic variable.)


DEC Alpha could famously break this code, not guaranteeing dependency ordering in asm, so needing barriers for memory_order_consume , unlike all(?) other ISAs.

Given the current deprecated state of consume , the only way to get efficient asm on ISAs with dependency ordering (not Alpha), but which don't do acquire for free (x86) is to write code like this. The Linux kernel does this in practice for things like RCU.

That requires keeping it simple enough that compilers can't break the dependency ordering by eg proving that any non-NULL read_ptr would have a specific value, like the address of a static variable.

See also

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM