Do the advantages and disadvantages of somehow conditional std :: atomic_thread_fence get?

The code below shows two ways to get the general state through an atomic flag. The reader thread calls poll1()or poll2()to check if the author has flagged the flag.

Option survey number 1:

bool poll1() {
    return (flag.load(std::memory_order_acquire) == 1);
}

Option survey number 2:

bool poll2() {
    int snapshot = flag.load(std::memory_order_relaxed);
    if (snapshot == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        return true;
    }
    return false;
}

Please note that option # 1 was introduced in an earlier question , and option # 2 is similar to the sample code on cppreference.com .

Assuming that the reader agrees to consider only the general state, if the function pollreturns true, are the two functions pollcorrect and equivalent?

Does option # 2 have a standard name?

What are the advantages and disadvantages of each option?

, № 2 ? ?

:

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

int x; // regular variable, could be a complex data structure

std::atomic<int> flag { 0 };

void writer_thread() {
    x = 42;
    // release value x to reader thread
    flag.store(1, std::memory_order_release);
}

bool poll1() {
    return (flag.load(std::memory_order_acquire) == 1);
}

bool poll2() {
    int snapshot = flag.load(std::memory_order_relaxed);
    if (snapshot == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        return true;
    }
    return false;
}

int main() {
    x = 0;

    std::thread t(writer_thread);

    // "reader thread" ...  
    // sleep-wait is just for the test.
    // production code calls poll() at specific points

    while (!poll2()) // poll1() or poll2() here
      std::this_thread::sleep_for(std::chrono::milliseconds(50));

    std::cout << x << std::endl;

    t.join();
}
+4
1

, .

, , , - ( , , - , ). , /, Jeff Preshing.

check-then-fence №2 , . .

, g++ 4.8.1 x64 (Linux) , , . , , x86 (-64) , (x86 ).

ARM, , ( gcc.godbolt.com -O3 -DNDEBUG):

while (!poll1());:

.L25:
    ldr     r0, [r2]
    movw    r3, #:lower16:.LANCHOR0
    dmb     sy
    movt    r3, #:upper16:.LANCHOR0
    cmp     r0, #1
    bne     .L25

while (!poll2());:

.L29:
    ldr     r0, [r2]
    movw    r3, #:lower16:.LANCHOR0
    movt    r3, #:upper16:.LANCHOR0
    cmp     r0, #1
    bne     .L29
    dmb     sy

, , (dmb) - poll1, - poll2. , poll2 :-) ( , , , , .)

ARM64 , /, (ldar → load-).

while (!poll1());:

.L16:
    ldar    w0, [x1]
    cmp     w0, 1
    bne     .L16

while (!poll2());:

.L24:
    ldr     w0, [x1]
    cmp     w0, 1
    bne     .L24
    dmb     ishld

, poll2 , , , poll1 .

, , , , , . poll1 poll2, -, , , , (.. , , , () poll1 , poll2). , , - poll1 , poll2.

, , , poll2 , poll1, , , (, , ).

( ) :

#include <atomic>
#include <thread>
#include <cstdio>

int sharedState;
std::atomic<int> flag(0);

bool poll1() {
    return (flag.load(std::memory_order_acquire) == 1);
}

bool poll2() {
    int snapshot = flag.load(std::memory_order_relaxed);
    if (snapshot == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        return true;
    }
    return false;
}

void __attribute__((noinline)) threadFunc()
{
    while (!poll2());
    std::printf("%d\n", sharedState);
}

int main(int argc, char** argv)
{
    std::thread t(threadFunc);
    sharedState = argc;
    flag.store(1, std::memory_order_release);
    t.join();
    return 0;
}
+2

Source: https://habr.com/ru/post/1627796/


All Articles