Home > OS >  std::map<int, std::bitset<256 > > thread safety w/o mutex?
std::map<int, std::bitset<256 > > thread safety w/o mutex?

Time:04-29

I have a

std::map<int, std::bitset<256 > > m;

After construction no new keys will be inserted and no keys will be removed. Can I safely assign the bitset in one thread while reading it in other threads without using a mutex?

// thread 1: 
std::bitset<256> a = getBitset();
m[1] = a;  

// thread 2:
std::bitset<256> b = m[1]; 
// or alternatively 
std::bitset<256> c = m.at(1);

I think that the program will not crash but a data race could occur in the bitset. A data race would be acceptable if the read would deliver a combination of the old and the new bitset.

CodePudding user response:

No, it is not safe.

operator= of the bitset is a modifying operation, and as such is not guaranteed to be free of a data race if the bitset is simultaneously accessed in another thread. And in practice, it will almost surely cause a data race, since it needs to write to the object. This is not specific to std::bitset, but generally true for pretty much all non-empty non-atomic standard library types (and most other non-empty types, as well).

Any data race will result in undefined behavior. There are no partial updates. In that sense, it should never be acceptable.

If you want to be able to do this, either wrap the bitset in a struct with a mutex to protect access to the bitset, or use something like an atomic shared_ptr that allows atomically exchanging the old bitset with a new one and delaying destruction of the old one until all references are gone.

Although I think it is not guaranteed, std::bitset may also be trivially copyable. You could check that with a static_assert on std::is_trivially_copyable_v and if true, you could also use a std::atomic or std::atomic_ref which will allow accessing the bitset atomically (i.e. free of data race) and will probably use a lock only if the underlying architecture doesn't support atomic access to an object of the corresponding size.

Also note that std::bitset b = m[1]; and m[1] also cause undefined behavior, because operator[] of std::map is also a modifying operation, and is not specified to be free of a data race, even if it doesn't insert a new element into the map. You would need to use e.g. the find() member function instead.

  • Related