5

Imagine having two threads, one assigning a value to a (already initialised) bool and another thread reading/checking this bool. Thread sanitizers might detect a possible data race here if the accesses to the bool are not guarded or the bool is non-atomic.

How is this possible? Is it possible that assigning to a bool is not always atomic, e.g., because of hardware characteristics like cache hierarchies or out-of-order execution?

Benski
  • 63
  • 5
  • 2
    `bool` is not atomic, use `std::atomic`. – Jarod42 Sep 20 '18 at 11:37
  • The question was not about the general difference between `bool` and `std::atomic`. It was more about this "tearing effect in the middle" of a `bool` as @SkepticalEmpiricist explained it. – Benski Sep 21 '18 at 12:38

1 Answers1

5

Even though the C++ standard does not mandate so, it is not possible in practice to get a tearing effect "in the middle" of a bool on x86, i.e. change the value only partially while another thread is accessing it. It is, however, possible that a CPU is maintaining more than one copy of it, as a cache for each core for example. Hence, one thread could be "seeing" an older value after another thread finished changing it to a new one. std::atomic (and specifically in your case std::atomic<bool>) provides you with memory barriers to remedy this issue.

Geezer
  • 5,600
  • 18
  • 31
  • Without std::atomic the optimiser may cache the read and/or remove the write (to memory) entirely. – Richard Critten Sep 20 '18 at 12:00
  • @RichardCritten I'd love to edit if you could provide me with a linked source or such. I couldn't find any while writing the answer... – Geezer Sep 20 '18 at 12:03
  • 1
    I thought typical cache coherency protocols as, e.g., MESI on intel processors would make sure that threads cannot read differing values from cache. – Benski Sep 21 '18 at 12:40
  • @Benski I believe you are correct, only that it is achieved by using [*memory barriers*](https://en.wikipedia.org/wiki/MESI_protocol#Memory_Barriers) -- for which in C++ you have these `std::atomic`s. – Geezer Sep 21 '18 at 12:53
  • @SkepticalEmpiricist was getting around to it when this got posted: https://blogs.msdn.microsoft.com/oldnewthing/20180924-00/?p=99805 _"...The assembly code loads ptr into a register once (at the start of the loop), and then it compares the value pointed-to by that register against zero. It never reloads the ptr variable, so it never notices that the thread changed the value of ptr to point to a different value...."_ – Richard Critten Sep 24 '18 at 17:28
  • @RichardCritten This is great stuff, I'm trying to reproduce on multiple compilers, playing around with optimization flags. So far to no avail... I'd love to add an edit with a link as a proof. Did you happen to succeed getting a repro, visible in the assembly like the costumer of Raymond Chen had described? – Geezer Sep 24 '18 at 18:10