std::map thread-safety - c++

Is reference to object in std::map is thread safe?
std::map< std::string, Object > _objects;
map can be changed from many threads and this access is synchronized, but reference to value (Object &) accessable just from 1 instance and thread. is write operations with Object & is safe if another thread will add items to map? will it reallocate?

The C++11 standard guarantees that const method access to containers is safe from different threads (ie, both use const methods).
In addition, [container.requirements.dataraces] states
implementations are required to avoid data races when the contents of
the contained object in different elements in the same sequence,
excepting vector<bool>
In other words, except for vector<bool> modifying distinct contents is not a data race.
Now, if one thread invalidates an iterator used by another thread, clearly this is a data race (and results in undefined behavior). If one thread does non-const access to a container, and another does const access, that is a data race (and undefined behavior). (Note: a number of functions are "considered const" for the purpose of multithreading, including begin, end and other functions (and methods) that are non-const simply because they return non-const iterators. [] is included in this set of pseudo-const for thread safety reasons, except for map and unordered_set etc -- 23.2.2.1).
However, it appears that if you have a reference to an element within the container, and engage in operations that do not invalidate that reference in another thread, and never write to that element in another thread, you can safely read from that reference. Similarly, if other threads never even read from the element, then writing to that element shouldn't result in undefined behavior.
For standards references, 17.6.5.9.5 seems to guarantee that functions from the standard library won't run away and read/write elements needlessly.
So the short answer: you are safe, so long as the other thread doesn't directly mess with that particular entry in the map.

Elements in a map are stable, they do not get moved or invalidated unless the element is erased from the map. If only one thread is writing to a given object, and changes to the map itself are correctly synchronized, then I believe it will be safe. I'm sure it's safe in practice, and I think it's safe in theory too.
The standard guarantees that distinct elements can be modified by different threads, in [container.requirements.dataraces]
Notwithstanding (17.6.5.9), implementations are required to avoid data races when the contents of the contained object in different elements in the same sequence, excepting vector<bool>, are modified concurrently.
This only allows you to modify the elements, not to insert new elements into the map while modifying elements. For some containers, such as std::vector, modifying the vector itself might also modify elements by reallocating and moving them, but [associative.reqmts]/9 ensures std::map won't invalidate existing elements.
Since no member function of std::map is required to access the second member of its elements (i.e. the mapped_type) I think [res.on.data.races]/5 says no other thread will conflict with writes to that member when modifying the map. (Thanks to Yakk for that final piece of the puzzle)

Related

Is it safe to modify elements of std::valarray<T> concurrently?

If I've understood correctly, since C++11 it has been safe to call const member functions of a container concurrently and modify the elements of a container as long as the container itself is not modified as part of the operation (as seen from e.g. the table concerning thread safety in cppreference.com). Since std::valarray is not listed in the containers section of the (draft) standard, I'm unsure if thread safety also applies to it. In other words,
Is it safe to read from a std::valarray concurrently (in particular by using operator[] with slices)?
Is it safe to modify the elements of std::valarray<T> concurrently if the operation on T is safe?
I would like to use std::valarray for a multidimensional array of numbers that would be filled using multiple threads.
If I am reading your question correctly, [res.on.data.races] protects distinct slices from participating in data races, under
A C++ standard library function shall not directly or indirectly
access objects accessible by threads other than the current thread
unless the objects are accessed directly or indirectly via the
function's arguments, including this.
[container.requirements.dataraces] adds extra protection around modifications to distinct elements, which strictly valarray lacks.

c++ - vector of atomics fully thread safe?

I have a std::vector<std::atomic<size_t>> vec. Is it safe to run vec[index].fetch_add(1, std::memory_order_release) or store/load with multiple concurrent threads on it? I think it should be, because reading is thread safe and writing to one entry at the same time from multiple threads is impossible because of the atomics - is that right?
No it is not, in general, thread safe since the container itself is not atomic.
That said, so long as you don't change what is in the vector (i.e. doing anything that invalidates the return of data()) , you'll be fine.
Sadly you can't resort to std::atomic<std::vector<...>> as a std::vector is not trivially copyable.
vec[X].fetch_add is safe as long as you're modifying each individual atomic. If you call any non-const (modifying) method of the wrapping std::vector this is undefined behaviour, as std::vector itself is not thread safe.
De-facto, you can initialize the vector in some thread, pass its reference to some asynchronous task and from that point, only act on specific elements of the vector, not act on the vector itself. vec[X].action(...) is thread safe, vec.action(...) is not.
It depends what the other threads are doing.
From the standard:
17.6.5.9 Data race avoidance [res.on.data.races]
...
2 A C++ standard library function shall not directly or indirectly access objects (1.10) accessible by threads
other than the current thread unless the objects are accessed directly or indirectly via the function’s arguments,
including this.
3 A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads
other than the current thread unless the objects are accessed directly or indirectly via the function’s non-const
arguments, including this.
and
23.2.2 Container data races [container.requirements.dataraces]
1 For purposes of avoiding data races (17.6.5.9), implementations shall consider the following functions to be
const: begin, end, rbegin, rend, front, back, data, find, lower_bound, upper_bound, equal_range, at
and, except in associative or unordered associative containers, operator[].
2 Notwithstanding (17.6.5.9), 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.
By combining these two parts with the rules for atomics, we can deduce that calling vec[index].fetch_add(1, std::memory_order_release) cannot cause a race condition with other threads performing the same or other "const" operations (including those mentioned in paragraph 23.2.2.1). However, if another thread invokes a non-const operation on vec itself (e.g. insert, erase, resize etc.) then we experience undefined behaviour as specified by section 1.10:
1.10 Multi-threaded executions and data races [intro.multithread]
...
4 Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses
or modifies the same memory location.
...
21 The execution of a program contains a data race if it contains two conflicting actions in different threads,
at least one of which is not atomic, and neither happens before the other. Any such data race results in
undefined behavior.
Your expression vec[n].atomic_op(...) is not itself atomic, but decomposes into:
auto iter = vec.begin();
iter += n;
iter->atomic_op(...);
So, the first two statements are vulnerable to the usual iterator invalidation rules. Concurrently changing the size of the vector, or erasing any element before the nth, or the nth element itself, can break them.
Once you have an iterator to an element, if that iterator is not invalidated during the time you're using it, then whatever atomic operations you perform on the element itself will be safe.

C++11 / C++03 and std::vector thread safety

I am reading about thread safety of various stl containers from this link
Now I came across this point which states for C++11 only
Different elements in the same container can be modified concurrently
by different threads, except for the elements of std::vector<bool>
(for example, a vector of std::future objects can be receiving values
from multiple threads)
Does this mean if I have a method such as this which is being used by multiple
threads simultaneously (notice the method does not have any locks)
void ChangeValue(int index , int value)
{
someVector[index] = value;
}
Is the above method safe. My understanding is that it is safe for C++11 only.
However when I look at the other statement mentioned in the link
All const member functions can be called concurrently by different
threads on the same container. In addition, the member functions
begin(), end(), rbegin(), rend(), front(), back(), data(), find(),
lower_bound(), upper_bound(), equal_range(), at(), and, except in
associative containers, operator[], behave as const for the purposes
of thread safety (that is, they can also be called concurrently by
different threads on the same container). More generally, the C++
standard library functions do not modify objects unless those objects
are accessible, directly or indirectly, via the function's non-const
arguments, including the this pointer.
I come to the conclusion that in C++03 the above method can be safely used as well.
Kindly let me know if my understanding is correct.
It is meaningless to ask whether something is thread-safe under the C++03 standard - C++03 and earlier didn't have any concept of threads or thread safety.
ChangeValue is data race-free (as defined by C++11 and later) as long as no two threads pass the same argument for index, or else calls passing the same argument are synchronized with each other by some means external to the function.

How to get a non-const top element from priority_queue with user-defined objects?

std::priority_queue::top returns a constant value. However, I would like to remove the top element from the priority queue and be able to modify it somewhere else.
priority_queue<SomeClass, vector<SomeClass>, SomeClassCompare > pQueue;
...
SomeClass *toBeModified = &(pQueue.top());
pQueue.pop();
toBeModified->setMember(3); // I would like to do this
Is there a way I can grab the top element from a priority queue (and remove from the queue) and modify it as I wish?
Standard containers and container adapters have value semantics. When you push an element into the queue, a copy is created. When you remove an object from the queue, that object is destroyed.
Even if top() would return you a reference to non-const, that reference would become dangling as soon as you remove the element from the queue, and dereferencing it would result in undefined behavior.
This said, std::priority_queue returns you a reference to const in order to prevent you from messing (intentionally or unintentionally) with its internal ordering - that's pretty much the same reason why the key of associative containers such as std::map and std::set is const.
What you can do, instead, is to construct a copy of the value returned by top(), modify that copy, remove the original, and push the copy into the queue:
SomeClass obj = pQueue.top();
pQueue.pop();
obj.setMember(42);
pQueue.push(std::move(obj)); // You can move obj into the queue if you no more need it
If you need reference semantics, on the other hand, then you will have to push pointers into the queue (possibly smart pointers, depending on your use case) and provide an appropriate custom ordering criterion that would order those pointers based on properties of the objects they point to.
In this case, be careful not to modify those properties at run-time in a way that would make their ordering different. That would count as "messing with the internal ordering of the container", and will result in undefined behavior.
I don't think value semantics play any least role here. All other containers have the same value semantics and almost all of them provide front() with mutable reference.
There is one exact reason why priority_queue disallows modification of the top() element: this is because particular element is at top because it has been accordingly qualified so, according to its present value. Elements in the priority queue are always sorted according to the comparison criteria configured for the queue (operator < by default). By altering the element you can potentially destroy this condition and cause that all operations that use the sorted precondition would cause undefined behavior.
In short, the reason why priority_queue does not allow to modify contained elements is exactly the same as in case of set and keys for map.
I understand that you may have some dedicated comparison method, you use a field that contains the value to comparison, and you're going to modify any content of the object, except this very field. This way you don't violate the requirement of sorted order. There are two things you can do:
Make your class's parts that should be modified as mutable. This way you can get the element by top() and modify the mutable contents, even though this is const.
Create your own priority queue class by deriving std::priority_queue. It has a field named c (in protected section though), which contains a reference to the underlying container - and an underlying container rather always has a front() method that accesses the same element as top() in priority_queue. For your own safety, though, you should make your key field const, so that it's set during construction and never altered - just to minimize the risk.
Ah, and this problem cannot be also solved by using pointers - pointed objects will be exactly the same constant. These "reference semantics" also won't help you in any way because if you want to have a dedicated comparison method, it will have to look into the objects' contents, and this way you'll have exactly the same problem. This would help you, if you relied on simply comparing pointer values, but I'd rather doubt this could be a solution for 99% of cases.

Iterators validity and threads

Let's say that in the main thread of a C++11 program I build an std::set, fill it with items and extract an iterator it from it. After that, from another thread, I start modifying the set in such a way that elements can only be added to it but not erased.
Is the validity of it assured also while the set is being modified, or should I consider it invalid while the set is being modified by insertion operations from the other thread?
From section 23.2.1 [container.requirements.general]:
Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container.
For associative containers such as std::set, section 23.2.4 ([associative.reqmts]) says:
The insert and emplace members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements.
So your iterators will remain valid after inserting additional items.
However, thread safety is a different topic completely.
Section 17.6.5.9 ([res.on.data.races]) provides that
Operations on iterators obtained by calling a standard library container or string member function may access the underlying container, but shall not modify it.
Since that results in reading the container while it's being updated, it is not necessarily safe to use a std::set iterator while inserting into the collection from another thread. Your implementation may provide a stronger guarantee.