Qual é a diferença entre usar cercas explícitas e std :: atomic?

Supondo que as cargas de ponteiro e os armazenamentos alinhados sejam naturalmente atômicos na plataforma de destino, qual é a diferença entre isso:

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

isto:

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

e isto:

// 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);

Fiquei com a impressão de que eles eram todos equivalentes, no entantoRelatividade detecta uma corrida de dados no primeiro caso (apenas):

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($);
    }
};

Entrei em contato com o autor do Relacy para descobrir se isso era um comportamento esperado; ele diz que há de fato uma corrida de dados no meu caso de teste. No entanto, estou tendo problemas para localizá-lo; Alguém pode me indicar qual é a corrida? Mais importante, quais são as diferenças entre esses três casos?

Atualizar: Ocorreu-me que Relacy pode simplesmente estar reclamando sobre oatomicidade (ou falta dela) da variável que está sendo acessada através de threads ... afinal, ela não sabe que euintentar somente para usar este código em plataformas onde o acesso alinhado a inteiros / ponteiros é naturalmente atômico.

Outra atualização: Jeff Preshing escreveu um excelente post no blogexplicando a diferença entre as cercas explícitas e as internas ("cercas" vs "operações"). Os casos 2 e 3 aparentemente não são equivalentes! (Em certas circunstâncias sutis, de qualquer maneira.)

questionAnswers(5)

yourAnswerToTheQuestion