Gcc atomic read and writes

atomiccgccmultithreading

I have a multithreaded application where I one producer thread(main) and multiple consumers.

Now from main I want to have some sort of percentage of how far into the work the consumers are. Implementing a counter is easy as the work that is done a loop. However since this loop repeats a couple of thousands of times, maybe even more than a million times. I don`t want to mutex this part. So I went looking into some atomic options of writing to an int.

As far as I understand I can use the builtin atomic functions from gcc:
https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

however, it doesn`t have a function for just reading the variable I want to work on.

So basically my question is.

  1. can I read from the variable safely from my producer, as long as I use the atomic builtins for writing to that same variable in the consumer

or

  1. do I need some sort of different function to read from the variable. and what function is that

Best Answer

Define "safely".

If you just use a regular read, on x86, for naturally aligned 32-bit or smaller data, the read is atomic, so you will always read a valid value rather than one containing some bytes written by one thread and some by another. If any of those things are not true (not x86, not naturally aligned, larger than 32 bits...) all bets are off.

That said, you have no guarantee whatsoever that the value read will be particularly fresh, or that the sequence of values seen over multiple reads will be in any particular order. I have seen naive code using volatile to defeat the compiler optimising away the read entirely but no other synchronisation mechanism, literally never see an updated value due to CPU caching.

If any of these things matter to you, and they really should, you should explicitly make the read atomic and use the appropriate memory barriers. The intrinsics you refer to take care of both of these things for you: you could call one of the atomic intrinsics in such a way that there is no side effect other than returning the value:

__sync_val_compare_and_swap(ptr, 0, 0)

or

__sync_add_and_fetch(ptr, 0)

or

__sync_sub_and_fetch(ptr, 0)

or whatever