Search code examples
cmultithreadingconcurrencystdatomic

Memory order introduced by atomic_exchange_explicit/atomic_exchange


I'm trying to understand memory ordering intoduced by atomic_exchange[_explicit] and wrote the following code:

static _Atomic int i = 123;

void *update(void *ignored){
    if(atomic_exchange_explicit(&i, 200, memory_order_release) != 200){ //1
        printf("Updated\n", stdout);
    } 
}

int main(int args, const char *argv[]){
    pthread_t t1;
    pthread_create(&t1, NULL, &update, NULL);

    pthread_t t2;
    pthread_create(&t2, NULL, &update, NULL);

    sleep(1000);
}

Question: Is it possible the Updated\n will be printed twice (by both of the threads)?

I think the behavior is undefined. The UB is held even if we replace memory_order_release with memory_order_acquire at //1. The generated code is the same for acq/rel/acq_rel: https://godbolt.org/z/sFjcve .

This is because we have data race since for synchronize with relation to be held we need one operation is an acquire operation and the next operation is a release operation and the release operation reads a value written by a side effect from a release sequence of the acquire operation 5.1.2.4(p11):

In particular, an atomic operation A that performs a release operation on an object M synchronizes with an atomic operation B that performs an acquire operation on M and reads a value written by any side effect in the release sequence headed by A.

So the only way to make this code's behavior well-defined is to replace atomic_exchange_explicit(&i, 200, memory_order_release) != 200 with atomic_exchange(&i, 200) != 200


Solution

  • You are incorrect. Regardless of the memory ordering requested, the exchange is atomic. Memory ordering explains how this read, write, or read-modify-write operation interacts with other operations, but the operation is always atomic.