3

C++ : I have a vector<bool> , no thread will ever write false to any of the indices . Should I still use a lock ? I don't think it will cause a race condition as all threads are writing the same value . The functions being accessed by multiple threads looks like :

void setVal(int index) 
{
  if (boolvec[index] == false)
    boolvec[index] = true; 
}
spooja__
  • 203
  • 2
  • 7
  • If no thread will ever set it to false, why do you need this function? Apparently someone sets it to false somewhere. – ttemple Aug 02 '20 at 12:24
  • If two or more threads access the same object and at least one of them modifies it you have a race condition and, consequently, undefined behavior if you don't properly synchronize the accesses. Note that there is no exception for code where your intuition says that unsynchronized access might be okay. It's not. – Pete Becker Aug 02 '20 at 16:05
  • 1
    @ttemple I'd assume that the values are initially all false; the threads compute some complex calculation in parallel and the result is the set of indices where the value remained false. – Daniel Jour Aug 02 '20 at 16:29

4 Answers4

5

To get by without a lock you should use a std::vector<atomic_flag_wrapper> where atomic_flag_wrapper wraps a std::atomic_flag similar to the code in this answer.

With std::vector<bool> you must use a lock, the standard explicitly tells you so:

Notwithstanding [res.on.data.races], implementations are required to avoid data races when the contents of the contained object in different elements in the same container, excepting vector<bool>, are modified concurrently.

http://eel.is/c++draft/container.requirements.dataraces#2 (C++ draft, 02.08.2020), emphasis mine

In plain English:

std::vector<bool> does not need to make sure that writes to two different elements are race-free; therefore there can be a data race; therefore you need a lock.

If it were e.g. a std::vector<char>, then the standard makes sure that charVector[0] and charVector[1] can be written to concurrently. But still then you cannot write to charVector[0] from more than one thread concurrently; you need to use atomics.

std:atomic<bool> is not guaranteed to be lock-free, so you should use std::atomic_flag which has this guarantee. You cannot put these into a std::vector, though, because they are not copy constructible. For that, you need a wrapper as described in this answer.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
3

You should use a lock. On some platforms it may be unnecessary, while on others it is essential to get correct functionality due to the nature of std::vector<bool>.

std::vector<bool> is designed for space efficiency, and elements are stored on individual bits. If one thread writes to boolvec[3], and another writes to boolvec[4], both will modify the same byte in memory. This requires a lock to ensure that the proper value is visible to all threads because of the read-modify-write cycle necessary to change the value. Without the lock, one some platforms the first write may not be immediately visible to all CPUs/cores/threads, resulting in the second write using a stale value, effectively undoing the previous thread's write to the same address.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
1

Yes, you should. Always a good idea, since it affects readability and maintainability. Even though in the current day your code may not be expected to write specific values, in the future, that may change. While it may work currently without a lock, suppose you want to change something. Then you get a bug and have to chase it down--a bug you could have reasonably prevented by writing your code defensively from the beginning.

C. Ventin
  • 135
  • 7
-2

In this specific case you can avoid using lock. In case multiple threads read false at the same index it does not matter, since the end result will be that value at that index is true.

magikarp
  • 129
  • 7
  • this lowers maintainability – Paul Baxter Aug 01 '20 at 22:43
  • 1
    No, none of this is correct. Without a lock, if one thread is writing the value there is no guarantee that some other thread will read either `false` or `true`. The behavior is undefined. Don't trust your intuition about how multi-threaded code works. – Pete Becker Aug 02 '20 at 16:08
  • I'm aware that this answer is bad, just waiting for 1 more downvote so I can get the badge. – magikarp Aug 02 '20 at 16:13