В чем разница между использованием явных заборов и std :: atomic?

Предполагая, что выравниваемые нагрузки и хранилища указателя на целевой платформе естественно атомарны, в чем разница между этим:

// Case 1: Dumb pointer, manual fence
int* ptr;
// ...
std::atomic_thread_fence(std::memory_order_release);
ptr = new int(-4);

это:

// Case 2: atomic var, automatic fence
std::atomic<int*> ptr;
// ...
ptr.store(new int(-4), std::memory_order_release);

и это:

// Case 3: atomic var, manual fence
std::atomic<int*> ptr;
// ...
std::atomic_thread_fence(std::memory_order_release);
ptr.store(new int(-4), std::memory_order_relaxed);

У меня сложилось впечатление, что все они были эквивалентны, однакоRelacy обнаруживает гонку данных в первом случае (только):

struct test_relacy_behaviour : public rl::test_suite<test_relacy_behaviour, 2>
{
    rl::var<std::string*> ptr;
    rl::var<int> data;

    void before()
    {
        ptr($) = nullptr;
        rl::atomic_thread_fence(rl::memory_order_seq_cst);
    }

    void thread(unsigned int id)
    {
        if (id == 0) {
            std::string* p  = new std::string("Hello");
            data($) = 42;
            rl::atomic_thread_fence(rl::memory_order_release);
            ptr($) = p;
        }
        else {
            std::string* p2 = ptr($);        // <-- Test fails here after the first thread completely finishes executing (no contention)
            rl::atomic_thread_fence(rl::memory_order_acquire);

            RL_ASSERT(!p2 || *p2 == "Hello" && data($) == 42);
        }
    }

    void after()
    {
        delete ptr($);
    }
};

Я связался с автором Relacy, чтобы узнать, было ли это ожидаемым поведением; он говорит, что в моем тестовом случае действительно есть гонка данных. Тем не менее, у меня возникают проблемы с его определением; кто-то может указать мне, что это за гонка? Самое главное, каковы различия между этими тремя случаями?

Обновить: Мне пришло в голову, что Relacy может просто жаловаться навалентность (или, скорее, его отсутствие) переменной, доступ к которой осуществляется через потоки ... в конце концов, он не знает, что янамереваться использовать этот код только на платформах, где выравниваемый целочисленный / указательный доступ является естественным атомарным.

Еще одно обновление: Джефф Прешинг написал отличный пост в блогеобъясняя разницу между явными заборами и встроенными ("заборы" против "операций"). Случаи 2 и 3, очевидно, не эквивалентны! (Во всяком случае при определенных тонких обстоятельствах.)

Ответы на вопрос(5)

Ваш ответ на вопрос