4

I am currently in debate with another developer who assures me that the following c++ statement is atomic:

x |= 0x1; // x is shared by multiple threads

Compiled with VC++11 in release mode this generates the following assemby:

01121270  or          dword ptr ds:[1124430h],1

The other developer says that the bit operations are atomic and therefore thread safe. My experience with an intel i7 processor says otherwise.

I thought that with a multicore processor, any shared memory write is unsafe because of the individual processor caches. But having done more research it seems as though the x86 processors provide some guarantees in relation to the order of memory operations between processors/cores which suggest that it should be safe... again, this doesn't seem to be the case in my experience.

Since I don't have authoritative knowledge of these kinds of things it's difficult for me to put my case or even be confident that I'm right.

  • 2
    It's just platform-dependent, isn't it? I think you should specify your platform. – ikh Mar 17 '15 at 12:30
  • 2
    Some compilers may generate thread-safe code on some architectures, but there's no guarantee. There's also no disadvantage to using `std::atomic`, which has an `operator|=` that is guranteed to be atomic, so you should do that. – Wintermute Mar 17 '15 at 12:31
  • I'm fairly sure that a `lock` instruction prefix is required on x86 architectures to ensure atomicity in the case of multiple processors/cores. I.e. it would have to be `lock or dword ptr ds:[11224430h],1`. But I don't have a reference handy. What does it compile to if you `use std::atomic`? – davmac Mar 17 '15 at 12:48
  • See for eg http://stackoverflow.com/questions/3339141/x86-lock-question-on-multi-core-cpus – davmac Mar 17 '15 at 12:52
  • Ah sorry, I'm referring to the x86 platform – Robert Smith Mar 17 '15 at 12:54
  • using std::atomic< uint32_t> does indeed add 'lock' to the or instruction. The downside to std::atomic is that it's c++11 only. – Robert Smith Mar 17 '15 at 12:58
  • @RobertSmith In pre-11, there's always [Boost.Atomic](http://www.boost.org/doc/libs/1_57_0/doc/html/atomic.html). – Angew is no longer proud of SO Mar 17 '15 at 13:40
  • Yes, but boost is unavailable in my company and the specific issue is in a third party library anyway and I presume that the developer would prefer not to introduce a dependency on boost or c++11. – Robert Smith Mar 18 '15 at 12:36
  • That instruction is an unlocked read-modify-write instruction. So it didn't even happen to produce an atomic operation on VC++11 in release mode. Nothing prevents another core from stealing the cache line during the modify. – David Schwartz Jan 17 '18 at 00:23

1 Answers1

8

No, it's definitely not guaranteed to be atomic. Whether it's implemented using an uniterruptible instruction (sequence) or not is up to the compiler and platform. But from the point of view of the standard, it's not atomic; so if one thread perfroms x |= 0x1; and another thread accesses x without a synchronisation point in between, it's Undefined Behaviour (a data race).

Supporting quotes from C++11:

1.10/5:

The library defines a number of atomic operations (Clause 29) and operations on mutexes (Clause 30) that are specially identified as synchronization operations. ...

Clause 29 introduces std::atomic and related functions. It does not specify fundamental types as atomic.

1.10/21:

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior. ...

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455