What does the C++ standard library guarantee to avoid data races? - c++

Reading C++11 FAQ -- Threads there's this paragraph which I don't understand:
Consequently, C++11 provides some rules/guarantees for the programmer to avoid data races:
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.
A C++ standard library function shall not directly or indirectly modify objects accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's nonconst arguments, including this.
C++ standard library implementations are required to avoid data races when different elements in the same sequence are modified concurrently.
I know what a data race is, and multithreading generally, but I don't understand what these sentences are saying.
Could you explain them more clearly? Perhaps with an example? What is or isn't it safe for me (i.e. an application programmer) to do in a multi-threaded context?
Had I not read these, I would have guessed that it's not safe to have multiple threads calling a non-const method of an object of any type, but I suppose this is saying something in addition to that?

OK I think I figured it out from the comments (please correct me if I'm wrong).
These first two are similar -- i.e. that a function will only read or write memory that's reachable via the function argument.
Perhaps this means, no more and no less than, that functions won't read or mutate global or static data in their implementation.
There were a few functions in the C library which broke this rule, for example ctime.
ctime returns a pointer to static data and is not thread-safe.
The third is saying that a thread can mutate an element in a container while another thread accesses a different element.
This does not imply that it's also inherently safe to mutate the container, e.g. to insert a new element.
And vector<bool> is an exception to this rule:
std::vector<bool> behaves similarly to std::vector, but in order to be space efficient, it ... does not guarantee that different elements in the same container can be modified concurrently by different threads.

Related

c++ is it safe to read a map that may be written at the same time? [duplicate]

I have trouble finding any up-to-date information on this.
Do C++11 versions of STL containers have some level of thread safety guaranteed?
I do expect that they don't, due to performance reasons. But then again, that's why we have both std::vector::operator[] and std::vector::at.
Since the existing answers don't cover it (only a comment does), I'll just mention 23.2.2 [container.requirements.dataraces] of the current C++ standard specification which says:
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.
i.e. it's safe to access distinct elements of the same container, so for example you can have a global std::vector<std::future<int>> of ten elements and have ten threads which each write to a different element of the vector.
Apart from that, the same rules apply to containers as for the rest of the standard library (see 17.6.5.9 [res.on.data.races]), as Mr.C64's answer says, and additionally [container.requirements.dataraces] lists some non-const member functions of containers that can be called safely because they only return non-const references to elements, they don't actually modify anything (in general any non-const member function must be considered a modification.)
I think STL containers offer the following basic thread-safety guarantee:
simultaneous reads of the same object are OK
simultaneous read/writes of different objects are OK
But you have to use some form of custom synchronization (e.g. critical section) if you want to do something different, like e.g. simultaneous writes on the same object.
No. Check out PPL or Intel TBB for thread safe STL-like containers.
Like others have noted they have usual "multiple reader thread safety" but that is even pre C++11. Ofc this doesnt mean single writer multiple readers. It means 0 writers. :)

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.

Are C++11 objects potentially slower in multi-threaded environments because of the new const?

According to Herb Sutter (http://isocpp.org/blog/2012/12/you-dont-know-const-and-mutable-herb-sutter), in C++11 const methods must not alter the object bit-wise, or must perform internal synchronization (e.g. using a mutex) if they have mutable data members.
Suppose I have a global object that I'm accessing from multiple threads and suppose it has mutable members. For the sake of argument, let's assume that we cannot modify the source of the class (it's provided by a third-party).
In C++98 these threads would use a global mutex to synchronize access to this object. So, an access would require a single mutex lock/unlock.
However, in C++11, any const member function call on this object will invoke internal synchronization as well, so potentially, a single const function call on this object will cost 2 lock/unlock operations (or more, depending on how many functions you call from a single thread). Note that the global mutex is still needed, because const doesn't seem to do anything for writers (except possibly slowing them down as well, if one of the non-const methods calls a const method).
So, my question is: If all of our classes have to be this way in C++ (at least to be usable by STL), doesn't this lead to excessive synchronization measures?
Thanks
Edit: Some clarifications:
It seems that in C++11, you cannot use a class with the standard library unless its const member functions are internally synchronized (or do not perform any writes).
While C++11 doesn't automatically add any synchronization code itself, a standard-library-compliant class doesn't need synchronization in C++98, but needs it in C++11. So, in C++98 you can get away with not doing any internal synchronization for mutable members, but in C++11 you can't.
in C++11, any const member function call on this object will invoke internal synchronization as well
Why? That synchronisation doesn't just magically appear in the class, it's only there if someone adds it explicitly.
so potentially, a single const function call on this object will cost 2 lock/unlock operations
Only if someone has added an internal mutex to it and you also use an external one ... but why on earth would you do that?
Note that the global mutex is still needed, because const doesn't seem to do anything for writers (except possibly slowing them down as well, if one of the non-const methods calls a const method).
If the class has an internal mutex that's used to make the const members thread-safe then it could also be used for non-const members. If the class doesn't have an internal mutex, then the situation is identical to the C++98 one.
I think you're seeing a problem that doesn't exist.
Herb's "new meaning for const" is not enforced by the language or compiler, it's just design guidance, i.e. an idiom for good code. To follow that guidance you don't add mutexes to every class so const members are allowed to modify mutable members, you avoid mutable members! In the rare cases where you absolutely must have mutable members, either require users to do their own locking (and clearly document the class as requiring external synchronisation) or add internal synchronisation and pay the extra cost ... but those situations should be rare, so it's not true that "C++11 objects are slower because of the new const" because most well-designed objects don't have mutable members anyway.
Yes, you are absolutely correct. You should make your objects follow these guidelines, and therefore access to them will potentially be slower in C++11. If and only if:
The class has mutable members which const member functions modify.
The object is being accessed from multiple threads.
If you ensure that at least one of these is untrue, then nothing changes. The number of objects that are being accessed from multiple threads should always be as minimal as possible. And the number of classes that have mutable members should be minimal. So you're talking about a minimal set of a minimal set of objects.
And even then... all that is required is that data races will not be broken. Depending on what the mutable data even is, this could simply be an atomic access.
I fail to see the problem here. Few of the standard library objects will have mutable members. I defy you to find a reasonable implementation of basic_string, vector, map, etc that need mutable members.
It seems that in C++11, you cannot use a class with the standard library unless its const member functions are internally synchronized (or do not perform any writes).
This is incorrect. You most certainly can. What you cannot do is attempt to access that class across multiple threads in a way that would "perform any writes" on those mutable members. If you never access that object through that C++11 class across threads in that particular way, you're fine.
So yes, you can use them. But you only get the guarantees that your own class provides. If you use your class through a standard library class in an unreasonable way (like your const member functions not being const or properly synchronized), then that's your fault, not the library's.
So, in C++98 you can get away with not doing any internal synchronization for mutable members, but in C++11 you can't.
That's like saying you can get away with computer crime back in the Roman Empire. Of course you can. They didn't have computers back then; so they didn't know what computer crime was.
C++98/03 did not have the concept of "threading". Thus, the standard has no concept of "internal synchronization", so what you could or could not "get away with" was neither defined nor undefined. It made no more sense to ask that question of the standard than to ask what the hacking laws were during Ceaser's day.
Now that C++11 actually defines this concept and the idea of a race condition, C++11 is able to say when you can "get away with not doing any internal synchronization".
Or, to put it another way, here is how the two standards answer your question: What is the result of a potential data race on a mutable member when accessed via a member function declared const in the standard library?
C++11: There will be no data races on any internal members when accessed by a const function. All standard library implementations of such functions must be implemented in such a way that a data race cannot occur.
C++98/03: What's a data race?

C++11 STL containers and thread safety

I have trouble finding any up-to-date information on this.
Do C++11 versions of STL containers have some level of thread safety guaranteed?
I do expect that they don't, due to performance reasons. But then again, that's why we have both std::vector::operator[] and std::vector::at.
Since the existing answers don't cover it (only a comment does), I'll just mention 23.2.2 [container.requirements.dataraces] of the current C++ standard specification which says:
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.
i.e. it's safe to access distinct elements of the same container, so for example you can have a global std::vector<std::future<int>> of ten elements and have ten threads which each write to a different element of the vector.
Apart from that, the same rules apply to containers as for the rest of the standard library (see 17.6.5.9 [res.on.data.races]), as Mr.C64's answer says, and additionally [container.requirements.dataraces] lists some non-const member functions of containers that can be called safely because they only return non-const references to elements, they don't actually modify anything (in general any non-const member function must be considered a modification.)
I think STL containers offer the following basic thread-safety guarantee:
simultaneous reads of the same object are OK
simultaneous read/writes of different objects are OK
But you have to use some form of custom synchronization (e.g. critical section) if you want to do something different, like e.g. simultaneous writes on the same object.
No. Check out PPL or Intel TBB for thread safe STL-like containers.
Like others have noted they have usual "multiple reader thread safety" but that is even pre C++11. Ofc this doesnt mean single writer multiple readers. It means 0 writers. :)

standard containers as local variables in multi-threaded application

I'm aware of the fact that the containers from standard library are not thread-safe. By that I used to think that a container, say of type std::list, cannot be accessed by more than one thread concurrently (some of which may modify the container). But now it seems that there is more to it than meets the eye; something more subtle, something not so obvious, well at least to me.
For example, consider this function which accepts the first argument by value:
void log(std::string msg, severity s, /*...*/)
{
return; //no code!
}
Is this thread-safe?
At first, it seems that it is thread-safe, as the function body accesses no shared modifiable resources, hence thread-safe. On second thought, it comes to me that when invoking such a function, an object of type std::string will be created, which is the first argument, and I think that construction of this object isn't thread-safe, as it internally uses std::allocator, which I believe isn't thread-safe. Hence invoking such a function isn't thread-safe either. But if it is correct, then what about this:
void f()
{
std::string msg = "message"; //is it thread-safe? it doesn't seem so!
}
Am I going right? Can we use std::string (or any container which uses std::allocator internally) in multi-threaded program?
I'm specifically talking about containers as local variables, as opposed to shared objects.
I searched google and found many similar doubts, with no concrete answer. I face similar problem as his:
c++ allocators thread-safe?
Please consider C++03 and C++11, both.
In C++11, std::allocator is thread safe. From its definition:
20.6.9.1/6: Remark: the storage is obtained by calling ::operator new(std::size_t)
and from the definition of ::operator new:
18.6.1.4: The library versions of operator new and operator delete, user replacement versions of global operator new and operator delete, and the C standard library functions calloc, malloc, realloc, and free shall
not introduce data races (1.10) as a result of concurrent calls from different threads.
C++03 had no concept of threads, so any thread safety was implementation-specific; you'd have to refer to your implementation's documentation to see what guarantees it offered, if any. Since you're using Microsoft's implementation, this page says that it is safe to write to multiple container objects of the same class from many threads, which implies that std::allocator is thread-safe.
In C++11 this would be addressed for the default allocator in:
20.6.9.1 allocator members [allocator.members]
Except for the destructor, member functions of the default allocator
shall not introduce data races (1.10) as a result of concurrent calls
to those member functions from different threads. Calls to these
functions that allocate or deallocate a particular unit of storage
shall occur in a single total order, and each such deallocation call
shall happen before the next allocation (if any) in this order.
Any user-provided allocator would have to hold to the same constraints if it were going to be used across different threads.
Of course, for earlier versions of the standard, nothing is said about this since they didn't talk about multithreading. If an implementation were to support multithreading (as many or most do), it would be responsible for taking care of those issues. Similar to the way implementations provide a thread-safe malloc() (and other library functions) for C and C++ even though the standards prior to very recently said nothing about that.
As you may have already figured, there is not going to be an easy yes or no answer. However, I think this may help:
http://www.cs.huji.ac.il/~etsman/Docs/gcc-3.4-base/libstdc++/html/faq/index.html#5_6
I quote verbatim:
5.6 Is libstdc++-v3 thread-safe?
libstdc++-v3 strives to be thread-safe when all of the following
conditions are met:
The system's libc is itself thread-safe,
gcc -v reports a thread model other than 'single',
[pre-3.3 only] a non-generic implementation of atomicity.h exists for the architecture in question.
When an std::string is copied during call to log, the allocator may be thread-safe (mandatory in C++11), but the copy itself isn't. So if there is another thread mutating the source string while copy is taking place, this is not thread safe.
You may end-up with half the string as it was before mutation and another half after, or may even end-up accessing deallocated memory if the mutating thread reallocated (e.g. by appending new characters) or deleted the string, while the copy was still taking place.
OTOH, the...
std::string msg = "message";
...is thread safe provided your allocator is thread safe.