QVector preallocated access thread safety - c++

Qt containers are safe as read-only when used by multiple threads. But what about write access? Can I resize a QVector and use operator[] in different threads for writing? The size of the QVector will stay constant, each thread will write in different memory location (own index for each thread), so no same memory simultaneous access. The QVector will be a global variable.

The Qt documentation defines QVector's member functions as reentrant, which means that it's safe to call its methods from threads if you have different instances of QVector for each thread. This means that QVector isn't going to be thread safe the way you are intending to use it.
If you can guarantee that your writes to your QVector won't alter its length and won't overlap, you may find that you won't have problems. But if you know you're going to be writing to different areas of your vector, why not split the vector into subvectors and work on each subvector with a thread? This will allow you to make a guarantee that you won't have thread-related trouble. When your work is done, you can replace the vector as a single entity.

Related

What is the purpose of std::thread::swap?

From what I understand, std::thread::swap swaps the thread ids of two threads. It doesn't change thread local storage or affect execution of either of the threads in any way. What is the purpose behind it? How/where/why is it used?
Don't think of it as "swapping the thread IDs of two threads", think of it as swapping two thread variables:
std::thread a(...); // starts thread 1
std::thread b(...); // starts thread 2
a.swap(b); // or std::swap(a, b);
// now variable a points to thread 2
// and variable b points to thread 1
Most variables can be swapped with std::swap, even the ones that can't be copied. I'm not sure why they also created a swap member function for std::thread.
This is useful because you can, for example, sort a vector of threads. The sort algorithm will use swap to rearrange the threads in the vector. Remember that objects can't be moved in C++, only their contents. swap, and move semantics, allow you to move the contents of most objects into different objects. For thread, it moves the "pointer" to the thread.
std::thread is internally referential type - similar to the way for example std::vector is. Internally, std::vector too simply refers to a memory buffer stored in dynamic memory.
What you describe is how swapping works with such types. Swapping a vector merely swaps pointers to the buffers (as well as some book keeping information) - none of the stored objects are affected by the operation. These pointers are "handles" to a resource. Similarly, swapping threads swaps the internal thread id - which is a handle to the thread resource - without affecting the state of the referred threads.
As for purpose of swapping... It's simply a useful thing to do. It is most notably used in sorting and other re-ordering algorithms. I don't know of a particular reason to re-order thread objects, but neither do I know of a reason why that shouldn't be possible.

Is erasing and inserting in a single linked list thread safe?

Using std::forward_list are there any data races when erasing and inserting? For example I have one thread that does nothing but add new elements at the end of the list, and I have another thread that walks the (same) list and can erase elements from it.
From what I know of linked lists, each element holds a pointer to the next element, so if I erase the last element, at the same time that I am inserting a new element, would this cause a data race or do these containers work differently (or do they handle that possibility)?
If it is a data race, is there a (simple and fast) way to avoid this? (Note: The thread that inserts is the most speed critical of the two.)
There are thread-safety guarantees for the standard C++ library containers but they tend not to be of the kind people would consider thread-safety guarantees (that is, however, an error of people expecting the wrong thing). The thread-safety guarantees of standard library containers are roughly (the relevant section 17.6.5.9 [res.on.data.races]):
You can have as many readers of a container as you want. What exactly qualifies as reader is a bit subtly but roughly amounts to users of const member functions plus using a few non-const members to only read the data (the thread safety of the read data isn't any of the containers concern, i.e., 23.2.2 [container.requirements.dataraces] specifies that the elements can be changed without the containers introducing data races).
If there is one writer of a container, there shall be no other readers or writes of the container in another thread.
That is, reading one end of a container and writing the other end is not thread safe! In fact, even if the actual container changes don't affect the reader immediately, you always need synchronization of some form when communicating a piece of data from one thread to another thread. That is, even if you can guarantee that the consumer doesn't erase() the node the producer currently insert()s, there would be a data race.
No, neither forward_list nor any other STL containers are thread-safe for writes. You must provide synchronization so that no other threads read or write to the container while a write is occurring. Only simultaneous reads are safe.
The simplest way to do this is to use a mutex to lock access to the container while an insert is occurring. Doing this in a portable way requires C++ 11 (std::mutex) or platform-specific features (mutexes in Windows, perhaps pthreads in Linux/Unix).
Unless you're using a version of the STL that explicitly states it is thread-safe then no, the containers are not thread safe.
It's rare to make general purpose containers thread safe by default, as it imposses a performance hit on users who don't require thread safe access to the container, and this is by far the normal usage pattern.
If thread safety is an issue for you then you'll need to surround your code with locks, or use a data structure that is designed specifically designed for multi threaded access.
std containers are not meant to be thread safe.
You should carefully protect them for modify operations.

Can an std::map be moved while iterating through it?

When I'm iterating through an std::map, is there a possibility that by for example adding an element to the map in another thread, the objects in it will be removed causing the iteration to be corrupt? (As the iterator will be pointing to a non-existing variable as it's moved)
In theory when you add an element to an std::map, all the iterators in that map should stay valid. But the problem is that the operations are not atomic. If the OS suspends the inserting thread in the middle of the operation and gives control back to the iterating thread, the state of std::map might be invalid.
You need to synchronize access to the map via mutex or something similar. Alternatively you could use concurrency friendly collection from TBB or another similar library. TBB provides concurrent_unordered_map and concurrent_hash_map.
STL containers aren't thread safe. No guarantees at all. So you need to synchronize access to any standard container if they are used by different threads.
Yes--if another thread may be modifying the vector, you'll need to use something like a mutex to assure that only one thread has access to the vector at any given time.
With a map, the effects of a modification are much more limited -- rather than potentially moving the entire contents of a vector, a modification only affects an individual node in the map. Nonetheless, if one thread deletes a node just as another thread is trying to read that node, bad things will happen, so you still need a mutex to assure that only one thread is operating on the map at any given time.

C++ Thread safe vector.erase

I wrote a threaded Renderer for SFML which takes pointers to drawable objects and stores them in a vector to be draw each frame. Starting out adding objects to the vector and removing objects to the vector would frequently cause Segmentation faults (SIGSEGV). To try and combat this, I would add objects that needed to be removed/added to a queue to be removed later (before drawing the frame). This seemed to fix it, but lately I have noticed that if I add many objects at one time (or add/remove them fast enough) I will get the same SIGSEGV.
Should I be using locks when I add/remove from the vector?
You need to understand the thread-safety guarantees the C++ standard (and implementations of C++2003 for possibly concurrent systems) give. The standard containers are a thread-safe in the following sense:
It is OK to have multiple concurrent threads reading the same container.
If there is one thread modifying a container there shall be no concurrent threads reading or writing the same container.
Different containers are independent of each other.
Many people misunderstand thread-safety of container to mean that these rules are imposed by the container implementation: they are not! It is your responsibility to obey these rules.
The reason these aren't, and actually can't, be imposed by the containers is that they don't have an interface suitable for this. Consider for example the following trivial piece of code:
if (!c.empty() {
auto value = c.back();
// do something with the read value
}
The container can control the access to the calls to empty() and back(). However, between these calls it necessarily needs to release any sort of synchronization facilities, i.e. by the time the thread tries to read c.back() the container may be empty again! There are essentially two ways to deal with this problem:
You need to use external locking if there is possibility that a concurrent thread may be changing the container to span the entire range of accesses which are interdependent in some form.
You change the interface of the containers to become monitors. However, the container interface isn't at all suitable to be changed in this direction because monitors essentially only support "fire and forget" style of interfaces.
Both strategies have their advantages and the standard library containers are clearly supporting the first style, i.e. they require external locking when using concurrently with a potential of at least one thread modifying the container. They don't require any kind of locking (neither internal or external) if there is ever only one thread using them in the first place. This is actually the scenario they were designed for. The thread-safety guarantees given for them are in place to guarantee that there are no internal facilities used which are not thread-safe, say one per-object iterator object or a memory allocation facility shared by multiple threads without being thread-safe, etc.
To answer the original question: yes, you need to use external synchronization, e.g. in the form of mutex locks, if you modify the container in one thread and read it in another thread.
Should I be using locks when I add/remove from the vector?
Yes. If you're using the vector from two threads at the same time and you reallocate, then the backing allocation may be swapped out and freed behind the other thread's feet. The other thread would be reading/writing to freed memory, or memory in use for another unrelated allocation.

On MacOSX, using g++, is std::vector .size() thread safe?

I have a std::vector<...> that is shared in two threads.
Both of them make calls to vec->size();
Can this be a source of race conditions? I'm hoping not since vec->size() is const.
Thanks!
If you are calling ONLY vec->size() you are safe. But this is somehow difficult to believe. As soon you call any changing method, such as push_back a race can cause to get the wrong size.
Probably not. The problem isn't really in vec->size(), it's in all the other functions as well.
Consider this: vector::size() is typically calculated directly from members, e.g. .end - .begin. Now what happens with a push_back on one thread? It affects the size, obviously, via the members. It changes memory. But there is no memory barrier. Other threads on other cores will simply see the old memory. As a result, when they call size(), it will be calculated using the old values.
An obvious exception is when the vector doesn't change size after the creation of the threads. The threads will never have outdated information.