1

If I have only two threads, and I want one of them to wait for the other to reach a certain point, is it safe to do the following:

bool wait = true;

//Thread 1:
while(wait) ;
wait = true; //re-arm the signal

//Thread 2:
/* Preform here the code that needs to complete before Thread 1 continues */
wait = false;

Basically, if one thread only writes to it and the other only reads, can there be a problem? I assume a read or a write of a single bool is atomic, and even if not, I don't see how it can make a difference here.

Baruch
  • 20,590
  • 28
  • 126
  • 201

2 Answers2

5

No, it can't work. If you use std::atomic<bool> instead it will work.

Atomics in C++ address three issues. First, the possibility that a thread switch will happen in the middle of storing or reading a value that requires more than one bus cycle; this is called "tearing". Second, the possibility that two threads will be running on two separate processors with two separate caches, and one thread won't see changes made by the other. This is called "cache coherency". Third, the possibility that the compiler will move code around because the order doesn't appear to matter.

Even though a bool value probably only requires one bus cycle to read or write, it doesn't address the other two issues.

There are no reliable shortcuts. Use proper synchronization.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • If I replace the `bool` above with `atomic` and leave the rest, it will work? (I am unfamiliar with `atomic`, other than that it is part of C++11) – Baruch Apr 09 '13 at 21:26
  • @baruch, probably you should try [std::atomic_flag](http://en.cppreference.com/w/cpp/atomic/atomic_flag) instead - because it is guaranteed to be lock free. – Evgeny Panasyuk Apr 09 '13 at 21:28
  • Yes, it will work. But sitting in a tight loop waiting for an atomic variable to change is usually a waste of processor cycles; better to use a condition variable, so that the processor is available for other tasks. – Pete Becker Apr 09 '13 at 21:28
  • @PeteBecker, 1. Maybe `wait = false;` is called very often, etc, why not just use `std::atomic_flag` if it is appropriate? 2. Anyway, **baruch** may use it in other places, so it is better to give broader view. – Evgeny Panasyuk Apr 09 '13 at 21:34
  • @EvgenyPanasyuk - I didn't say don't use it. But the semantics of an atomic flag are different enough from those of an atomic variable that changing would require rewriting the code example. – Pete Becker Apr 09 '13 at 21:39
  • Does `sig_atomic_t` also work? I am using VC++ 2010 as one of the compilers and it doesn't support `atomic` – Baruch Apr 10 '13 at 12:24
  • How is the second problem (separate caches) solved by protecting "critical sections" with any type of OS offered semaphore/mutex? – Baruch Apr 10 '13 at 12:40
  • @baruch - no `sig_atomic_t` won't work. Read about what it does. – Pete Becker Apr 10 '13 at 14:19
  • @baruch - in general, cache synchronization comes from synchronization primitives. In the documentation, look for statements like "values written prior to an unlock are visible to another thread after a subsequent lock". That's typical of the guarantees from a mutex. In C++11 the operative words are "synchronizes with". – Pete Becker Apr 10 '13 at 14:21
  • @PeteBecker Does marking a variable as `volatile` solve the last 2 problems? – Baruch Aug 08 '13 at 12:18
  • @baruch: in a word, no. Not portably; the only requirement for `volatile` is that reads and writes **of volatile objects** cannot be eliminated or reordered. This does not prohibit moving reads and writes of non-volatile objects, or eliminating them completely, nor does it address cache coherency. – Pete Becker Aug 08 '13 at 13:16
  • @PeteBecker I just saw here (http://stackoverflow.com/a/558931/331785) that x86 (and presumably x64 too) hardware takes care of cache-coherency. Did I understand this correctly? I am sorry I keep asking you directly, but you answered what seems a knowledgeable answer to my original question. – Baruch Aug 08 '13 at 19:12
  • @baruch - I was talking about what the language guarantees, which is what you need for portable code. If the hardware makes some of those things easier to do, then you can be sure that the implementation of `std::atomic` will take advantage of it. – Pete Becker Aug 08 '13 at 19:17
0

While you would get guarantees with an std::atomic<bool>, I think you could perhaps make it work with a simple volatile, because:

  • the compiler cannot reorder instructions on volatile values
  • one of the threads always write to the variable, so the instruction is effectively atomic
  • the other thread doesn't need to do the read and the write atomically

So there wouldn't be any form of tearing. However, the issue of cache coherency is still there, and that's hardware or OS dependent.

didierc
  • 14,572
  • 3
  • 32
  • 52