c11rvaluestdatomiclvalue-to-rvalue

What is the difference in atomic_load() and assignment?


I am working on a project that deals with lots of atomic operations. Till now I didn’t knew about atomic_load() and was only relying on assignment operator to get value of an atomic type and I haven’t seen an error except of so much of testing. Those atomic types are changed by multiple processes and threads as well by atomic_compare_exchange_strong_explicit(), so they will need an old value every time, and that’s where I always did oldValue = <Atomic_ type_variable> and it always works fine. Is that just by chance? Should I prefer using atomic_load()?


Solution

  • foo = atomic_var is just a shortcut syntax for foo = atomic_load(&atomic_var);

    Which itself is a shortcut for foo = atomic_load_explicit(&atomic_var, memory_order_seq_cst); That has a use-case when you want to use an ordering weaker than the default seq_cst.

    The main reason for using atomic_load explicitly in your source code is probably to remind human readers that a variable or pointer is atomic. Or maybe as a part of a macro, using atomic_load(&(macro_input)) would create a compile-time error for a non-atomic pointer.

    As a "generic" function, you can't take a normal function-pointer to it.

    Its existence may be just to make it easier to write the language standard, and explain everything in terms of functions.


    It's not the actual assignment that's key here, it's evaluating the atomic variable in an rvalue context (reading it's value as part of an expression, like you typically find on the right-hand side of an =). printf("%d\n", my_atomic_var); is also equivalent to atomic_load.

    (In C++ you need an explicit load or cast like (int)my_atomic_int for variadic functions like printf, otherwise the compiler tries to pass the std::atomic object to printf but the copy-constructor is deleted. Only non-variadic functions where the prototype causes conversion to a type like plain int allow foo(my_atomic_var). But in C, an _Atomic long as an arg to printf becomes a load that produces a plain long temporary. Example on Godbolt. Or that's passing an _Atomic long arg, which doesn't cause a warning about a %ld format string matching it, I'm not sure which. It's probably not a bad idea to be more explicit about atomic loads in args to variadic functions anyway.)


    And BTW, the same thing holds for atomic_var = foo; being exactly the same as atomic_store_explicit with mo_seq_cst. Here it is assignment that's key.

    Other kinds of lvalue references to an atomic variable are different, like read-modify-write atomic_var++ is equivalent to atomic_fetch_add.