简体   繁体   中英

atomic writes and volatile reads

I am designing an multi threaded algorithm in which the requirement is to read the latest value of a shared variable. The writes to the variable are atomic (using compare-and-swap). However, the reads are not atomic.

Consider the following example:

//Global variable 
int a = 10;


// Thread T1
void func_1() {
     __sync_bool_compare_and_swap(&a, 10, 100);
}

// Thread T2
void func_2() {
     int c = a;
     /* Some Operations */
     int b = a;
     /* Some Operations */
} 

If the code int b = a is executed(by Thread T2) after __sync_bool_compare_and_swap in func_1(by thread T1), then as per my understanding, it is still not guaranteed to read the latest value of "variable a" as the compiler can cache "a" and use the old value of "a".

Now, to avoid this problem, I declared the variable "volatile" as below:

volatile int a = 10;

// Thread T1
void func_1() {
     __sync_bool_compare_and_swap(&a, 10, 100);
}

// Thread T2
void func_2() {
     volatile int c = a;
     /* Some Operations */
     volatile int b = a;
     /* Some Operations */
} 

For the same scenario of executing int b = a by thread T2 after __sync_bool_compare_and_swap by Thread T1 is finished, is it guaranteed to read the latest value of "a"?

How would cache coherency and memory consistency model affect the volatile read after an atomic write?

The volatile keyword only ensures that compiler will not store the variable in a register and instead will load the variable from memory every time it is used. It has nothing to do with the cache or memory consistency models of the system it is running under.

volatile doesn't make read operations atomic. Non-atomic reads concurrent with (atomic) write lead to an undefined behavior. Use atomic read in any form, either std::atomic or intrinsics. Don't use volatile for any form of concurrency.

Atomic read itself doesn't guarantee that the value will be latest . In your case thread T2 may never read 100, in theory. The standard says that implementation (hardware, OS, etc.) should do best effort to make writes visible to other threads in a finite time. Perhaps, it's impossible to put a formal requirements here.

With additional synchronization you can achieve more restricted behavior:

std::atomic<int> a = 10;
std::atomic<bool> done = false;

void func_1() {
    int old = 10;
    if (a.compare_exchange_strong(old, 100))
        done.store(true);
}

void func_2() {
    bool is_done = done.load();
    int b = a.load();
    assert(b == 100 || !is_done);

    while (!done.load()); // May spin indefinitely long, but should not do that
    assert(a.load() == 100);
}

Actually, to catch that simple atomic read reads not the latest value, one would have to put enough synchronization into the program (to define the latest ) so it will appear working correctly.

On all platforms that you are likely to use that support C++ and multiple threads, reading from a volatile-qualified, aligned int will be atomic and will read the latest value. However, it is absolutely not guaranteed by the C++ standard. There may be some platform where it doesn't work, and it might not work with the next CPU, compiler version, or OS version.

Ideally, use something that is guaranteed to provide atomicity and visibility. C++-11 atomic is probably the best choice. Compiler intrinsics would be the next best choice. If you have no choice but to just use volatile , I'd suggest you use preprocessor tests to confirm that you're on a platform where it is known to be sufficient and issue an error (with #error ) if not.

Note that on every platform you are likely to use, CPU memory caches are completely irrelevant because they're made invisible by hardware cache coherence. On all platforms that you are likely to use, the issues are only compiler optimizations, prefetched reads, and posted writes.

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