views:

965

answers:

10

Are the "modify" operators like +=, |=, &= etc atomic?

I know ++ is atomic (if you perform x++; in two different threads "simultaneously", you will always end up with x increased by 2, as opposed to x=x+1 with optimization switched off.)

What I wonder is whether variable |= constant, and the likes are thread-safe or do I have to protect them with a mutex?

(...or is it CPU-dependent? In this case, how is it on ARM?)

+59  A: 

You know wrong. There is no guarantee that ++ is atomic. and neither is there for the compound assignment operators, or indeed for any C++ operation.

anon
Which means this is CPU-specific. On single-core architectures that allow `inc [address]`, this is definitely atomic.
SF.
At least not until C++0x: "This Clause describes components for fine-grained atomic access. This access is provided via operations on atomic objects." [29.1/1 in the n3035 draft].
Roger Pate
@SF No it isn't. It is compiler specific. Just because a CPU architecture has an instruction does not mean that the compiler will use it in the way you think it should, if indeed it uses it at all.
anon
Even if the compiler can generate an atomic increment, whether x++ can be atomic may depend on the data type of x. For example on SF's target an increment of x if x is 'long long', will not be atomic, and if it were another integral type, it may conceivably depend on the specific ARM architecture and the data alignment.
Clifford
@SF: That's very specific. What if the compiler chooses not to use that instruction? What if/when you upgrade to a multicore system? What about architectures that do not have the `inc` instruction? *In general*, `++` is **not** atomic. You've just found one single narrow special case where it is not an issue
jalf
+5  A: 

For the change in value to be visible across cores, a += (for instance) would have to load the value, add the increment and then store it. This means that the operation will not be atomic.

To ensure atomicity you'd need to put appropriate locking around the operation.

Benno
+2  A: 

++ might be atomic on your compiler/platform, but in the c++ specs it is not defined to be atomic.

If you want to make sure to modify a value in an atomic way, you should use the appropiate methods, like Interlocked* on windows.

Same for all the other routines. If you want atomic operations, you should use the appropiate calls, not the standard ones.

Sam
+1  A: 

No operator in C or C++ is guaranteed to be atomic. They might be on your platform, but you won't know for sure. Typically, the only operation that is atomic is the an instruction Test and Set, which is usually available on most modern CPUs in some form as the basis for implementing semaphores.

plinth
+5  A: 

x++ is often implemented in 3 instructions: Read X into a register, Increment it, and Write it back to memory.

Your thread may be pre-empted in between any of those.

Alex
A: 

It's worth mentioning that these operators can be overloaded, so there can certainly be no general guarantee that they are atomic for all classes.

Oddthinking
They can't be overloaded for integers, which I think is what the question is about.
anon
Agreed, but it doesn't say that, which is why I thought it worth mentioning.
Oddthinking
+2  A: 

No, they're not atomic! If you need atomic operations on primitive types, and you're using linux, you can take a look here: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html and/or atomic.h...

DavideRizzi
A: 

Even if ++ is an atomic operation, that does not imply that two threads doing ++x will result in x being exactly two higher. You have to synchronize the threads somehow, or otherwise they won't necessarily see each other's changes.

FredOverflow
A: 

You have to protect your variable, with a mutex for instance

Nikko
+1  A: 

It's both compiler and CPU dependent. Some instruction sets provide atomic instructions for these (on machine-sized ints).

However, there's no guarantee that you compiler will use those instructions and won't optimize your code in a non-threadsafe way. You need to either write routine in assembly or use a compiler specific technique (such as instrinsics) that provides atomicity (or use a library that uses one of those techniques).


Specifically on ARM: The ORR/ADD/AND instructions take two operands and place the result in a register. Either operand can be the same register as the result register, so they can used as an atomic |=, +=, &=.

Of course, the result is put in a register and the first operand must also come from a register, so you'll have to make sure the register loads are done atomically.

Joe Gauterin