1

In C++, say I have a variable of basic data type, like int counter, that is used by many threads. In order to modify counter, a thread must obtain a simple lock first. But I want the value to be readable at any time, whether it is locked or not.

When a thread reads counter while some other thread is modifying it, do I have any guarantee to at least get either the pre-write or post-write value, rather than some corrupted value?

For example:

//counter == 10
counter += 5;
//counter == 15

Will all threads reading counter around this time be guaranteed to at least read 10 or 15? Instead of some strange value like -834289.

If the answer is implementation specific, I'm using Visual Studio 2015.

newprogrammer
  • 2,514
  • 2
  • 28
  • 46
  • 1
    Just a hint on terminology - what you are asking about is usually called 'torn reads'. You can google those for more information. – SergeyA Feb 22 '16 at 22:26
  • 2
    Is there a good reason for not using `std::atomic`? After all, it provides guarantees about reads and writes implemented appropriately for the particular hardware that you compile for. Deliberately writing code that requires you to ask whether it will work right on your hardware is premature (and probably inappropriate) optimization. – Pete Becker Feb 22 '16 at 22:42

3 Answers3

3

No it's not. Use a std::atomic_int

Sebastian Hoffmann
  • 2,815
  • 1
  • 12
  • 22
  • You will be hard pressed to find a modern problem where this will really be an issue. – SergeyA Feb 22 '16 at 22:33
  • @Sergey just like since any modern ISA use 2s complement you can harmlessly assume that as well? Writing code with undefined behaviour is a bad idea if the fix is trivial.Use relaxed semantics and there won't be any performance difference either. – Voo Feb 22 '16 at 23:13
  • @Voo, Can you please link to documentation suggesting undefined behavior for unlocked reads? – merlin2011 Feb 22 '16 at 23:45
  • 1
    The c++ standard makes no assertions about atomicity for an ordinary `int`. Relying on this assertion though, means your code theoretically causes UB. Your code will fail on any platform which does not uphold this assertion for an ordinary `int`. The standard however makes assertions about the atomicity of `std::atomic`, thus code using it has well defined behaviour. More important IMO though (as it has already stated by others that this is pratically irrelevant), is the fact that `std::atomic` is semantically more correct. – Sebastian Hoffmann Feb 22 '16 at 23:52
  • @Voo, I never said one should use this fact when writing programs. Just mentioned current state of the affairs. – SergeyA Feb 23 '16 at 01:44
  • What is more important important, the question is not practical. I can't imagine the case where the value of counter should be random. Even random number generator based on it would be poor. – SergeyA Feb 23 '16 at 02:05
  • @Sergey You also won't find any current ISA that doesn't use 2s complement. But relying on that has caused a fair share of bugs. Arguing about correctness based on what a CPU might do does not work even if your code only ever runs on that particular CPU. And this scenario (not caring about whether you see the latest value immediately only that you don't get torn reads) is not that uncommon. It's the whole reason we have a relaxed memory order. – Voo Feb 23 '16 at 07:15
  • For example you might want to log performance numbers without introducing a noticeable overhead, in that case an approximate number is good enough. – Voo Feb 23 '16 at 07:20
  • @Voo, find it hard to believe. Your approximate will be far off after 10 iterations. – SergeyA Feb 23 '16 at 14:43
  • 1
    @Voo relaxed atomics have **very** distinct feature - they are guranteed to make their way to main memory at least sometime (in a reasonable time spec says, I believe). Plain ints, while not be torn, do not have gurantee. For example, this addition can be optimized away by the compiler. It will never happen with atomics. – SergeyA Feb 23 '16 at 14:46
  • @SergeyA Yes I know (which is why I say "not care if you see the value *immediately*) what relaxed ordering does. On x64 you will actually have the most accurate number in memory due to its strict memory guarantees without any cost. On other ISAs you may miss the updates of the last few seconds at most - which is fine in many situations. This is not some weird thing nobody has ever done, this is a reasonable performance optimisation for things where 100% up-to-date information is unnecessary or not even a well defined concept. – Voo Feb 23 '16 at 16:14
  • @Voo, you missed my point. I am saying, that without atomicity, it will be optimized away **by the compiler**. Before it even reaches the CPU. This is why I find the question to have no practical usage. – SergeyA Feb 23 '16 at 16:26
2

On Intel architectures, assignment of word-sized values is generally atomic, so you will not read a corrupted value.

Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • I am yet to see an architecture where torn reads on properly aligned and sized values are possible. Haven't seen so far. – SergeyA Feb 22 '16 at 22:27
  • Yes, OP did. What do you imply? – SergeyA Feb 22 '16 at 22:29
  • @SergeyA, I'm not sure I understand what your original comment is getting at. Can you please clarify? Are you implying that the unlocked read would output a corrupt value? – merlin2011 Feb 22 '16 at 22:30
  • 1
    I am basically elaborating on your answer :) You are saying 'on Intel torn reads don't happen', I am saying, more than that. Certainly do not happen on Power, ARM and SPARC. – SergeyA Feb 22 '16 at 22:32
  • @SergeyA, Ah okay, sorry about my confusion. Thanks :) – merlin2011 Feb 22 '16 at 22:33
  • Downvoters care to link to documentation that suggests this is undefined behavior? – merlin2011 Feb 22 '16 at 23:39
1

Look at the interlocked family of functions here:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms686360(v=vs.85).aspx

The one you are looking for is most likely:

InterlockedAdd

Laserallan
  • 11,072
  • 10
  • 46
  • 67