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.