¿Cuál es la diferencia entre usar cercas explícitas y std :: atomic?

Suponiendo que las cargas y los almacenes de punteros alineados son naturalmente atómicos en la plataforma de destino, ¿cuál es la diferencia entre esto:

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

esta:

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

y esto:

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

Tenía la impresión de que todos eran equivalentes, sin embargoRelacy detecta una carrera de datos en el primer caso (solo):

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

Me puse en contacto con el autor de Relacy para averiguar si se trataba de un comportamiento esperado; Él dice que de hecho hay una carrera de datos en mi caso de prueba. Sin embargo, estoy teniendo problemas para detectarla; ¿Alguien me puede indicar qué es la carrera? Lo más importante, ¿cuáles son las diferencias entre estos tres casos?

Actualizar: Se me ha ocurrido que Relacy puede simplemente quejarse de laatomicidad (o falta de ella, más bien) de la variable a la que se accede a través de hilos ... después de todo, no sabe queintentar solo para usar este código en plataformas donde el acceso de entero / puntero alineado es naturalmente atómico.

Otra actualización: Jeff Preshing ha escrito una excelente publicación de blogExplicando la diferencia entre cercas explícitas y las incorporadas. ("cercas" vs "operaciones"). ¡Los casos 2 y 3 aparentemente no son equivalentes! (En ciertas circunstancias sutiles, de todos modos).

Respuestas a la pregunta(5)

Su respuesta a la pregunta