Atomic behavior of unary increment operators
Asked Answered
P

9

9

Somewhere I read that unary operators are atomic by nature and so they can be used as it is in multi threaded environment. To confirm the same, I wrote two separate programs where in

  1. I used a variable x and incremented using unary operator ++x
  2. I used a variable x and incremented using x=x+1

I compared the disassembly of both programs and found no difference. Please provide your inputs on this.

Policewoman answered 18/12, 2018 at 11:30 Comment(8)
Just because ++x is atomic doesn't mean, that x=x+1 isn't? (From a logical point of view, I doubt, that ++x is always atomic anyway)Bradley
Given a variable can reside in memory and thus require multiple instructions (load, add, store) they aren't automatically atomicCerussite
Doesn't matter whether or not some instruction is atomic, you either have correct synchronization or a race.Cartelize
I suspect that wherever you read that used the word "atomic" in a different sense than the one used in the context of concurrency. Either that, or they were completely wrong and you shouldn't trust anything you saw there.Forestay
std::atomic would be implemented by someone who knows the hardware that you're targeting, perhaps far better than you do. Let the expert do the work. Use std::atomic when you need atomicity.Eustace
In C, pre/post fix ++ are not unary-operators such as & * + - ~ !. but part of a unary-expression. So the question title is inconsistent with the body.Arbuckle
@chux The question says "unary operator", not "unary-operator" with a hyphen and italics implying the C/C++ grammar-specific meaning. Postfix operator++ is also not part of unary-expression but postfix-expression. Unary operator is a general, widely understood term that is used correctly here.Barbet
@Barbet A postfix-expression is one form of a unary-expression. True I am using the C specified meaning as that is objective. versus a wider understood term that can have various interpretations. After all, OP is looking for a specified answer per C/C++, not general programming.Arbuckle
R
13

Somewhere I read that unary operators are atomic by nature and so they can be used as it is in multi threaded environment.

That source is completely wrong. You need to use std::atomic (or the C equivalent) to achieve atomicity – unary operations are not special.


I compared the disassembly of both programs and found no difference

That doesn't mean that the generated operations are atomic. There is no difference as any decent compiler will optimize x=x+1 and ++x into the same assembly (assuming built-in types).

Radiance answered 18/12, 2018 at 11:32 Comment(1)
Comparing assembly might actually confirm the opposite. If you know that i = i + 1's assembly is nonatomic, then the same assembly is nonatomic for i++ therefore the latter is not atomic.Barbet
S
6

When writing cross platform C++, you only have atomic behavior when using std::atomic<>.

It is true that on certain platforms, like Intel 64bit, the processor guarantees that inc is atomic. However, please don't write code that depends on this! As your future debugger, I would like to know which data is intended to be shared over threads and which isn't.

Using std::atomic<int> might be a bit more work to write, however, it does guarantee that everything behaves atomically (on every platform) by either falling back to the platform requirements (std::atomic::is_lock_free), or by explicitly putting a lock around the access. It as well insert guards in order to make sure that the caches of the other processor cores are invalidated (if the platform requires this).

In practice for Intel 64bit, this should give you the same assembly, if not, log a bug on your compiler.

At the same time, some operations with ints might not be atomic (operator*=), std::atomic simply doesn't hold those operations, requiring you to correctly work with those.

On a side note: ++x and x = x+1 are different operations, they might be optimized to the same assembly. Given non-atomic platform requirements, the second suddenly is a bug which takes days to solve.

Siding answered 18/12, 2018 at 11:43 Comment(0)
S
5

The assertion that a unary operator is necessarily atomic is a myth.

For example, ++x requires a read and write to x so that opens up the chance of a data race.

The fact that ++x compiles to the same code as x = x + 1 is not relevant.

If you want to avoid data races then use atomic types, or mutual exclusion units if an appropriate atomic type is not available. For the avoidance of doubt, int is not necessarily an atomic type.

Sonneteer answered 18/12, 2018 at 11:33 Comment(0)
M
4

Somewhere I read that unary operators are atomic by nature and so they can be used as it is in multi threaded environment.

That's false. x++ for example requires a load of x, an add and a store of x. Those instructions are not atomic by nature.

Matrona answered 18/12, 2018 at 11:33 Comment(0)
N
2

Not true. Even if it would be, what reason would https://en.cppreference.com/w/cpp/atomic/atomic#Type_aliases then have?

I think what they probably meant is that a computation on such operation is usually very minute and thus high likely to never have a race condition, which is mostly true in live code where you dont calculate x++ in 4 for loops simultaneously.

Neurath answered 18/12, 2018 at 11:37 Comment(0)
M
2

You didn't specify the type of x.

  1. If x is 32 bit integer when platform is 16 or 8 bit, 'x++' operation will definitely will make multiple operations
  2. x could be even not basic type, x could be a instance of Class where operator++ does much more complicated stuff then just increment integer
Margeret answered 18/12, 2018 at 13:5 Comment(2)
"operation will definitely will make multiple operations" - I'd be careful about making that assertion. You can have 32 bit registers on 16 bits architectures, and you only need a bus lock followed by an increment for x++ to be atomic.Sonneteer
I agree with you that this will depend on the object being processed. I used integer of 32 bit for making it simple. But I should have used 64 bit integer since my machine is 64 bit and so the OS (Windows 10)Policewoman
B
1

The atomicity of the of the operation stronglr depends on the target system. Unary operation might not be atomic on RMW systems like RISC micro controllers.

There is no single generic answer for this question.

Brazier answered 18/12, 2018 at 11:40 Comment(4)
Good answer (albeit a bit terse), since it doesn't claim, that it can never be atomic (like others do). But: there is a "single generic answer" to the question: "No, unary operators are not atomic by nature".Bradley
@Bradley it depends on the abstraction level. If we abstract from the implementation and hardware - yes. If we do not - no.Brazier
@P__J__ The C language is specified in terms of an abstract machine: "The semantic descriptions in this International Standard describe the behavior of an abstract machine in which issues of optimization are irrelevant." The single generic answer is "unary operators are not atomic".Insignificancy
@AndrewHenle you are right - but in programming practice many programmers (especially those who are very close to the hardware) have to consider implementation and hardware. Simple example - what to do if you have a time critical function and you can't disable interrupts? stdatomic will not help in this case.Brazier
D
0

You make an assumption on the generated code, if only one instruction is generated yes it will be atomic, else no.

In your case this supposes the target processor has the instruction inc <address> and the compiler will produce it.

Daubigny answered 18/12, 2018 at 11:55 Comment(0)
A
0

Atomic behavior of unary operators

In C, pre/post fix ++ are not unary-operators such as & * + - ~ !. but part of a unary-expression. So the question title is inconsistent with the body.

Even a unary operator like + is not atomic as access to the object (think long long) make take multiple op code reads.

Arbuckle answered 18/12, 2018 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.