At what point to use unique_lock with shared_mutex? - c++

Typically, when using a "normal" mutex, you would use it as in remove1().
However, now with shared_lock and unique_lock, should you use a shared lock first and the unique lock only when necessary? Note that remove() may not need to unique_lock when the model does not exist.
void remove1(int id) {
std::unique_lock<std::shared_mutex> lock(mutex_);
for (auto it = models_.begin(); it != models_.end(); ++it)
if ((*it)->getId() == id)
{
it = models_.erase(it);
return;
{
}
void remove2(int id) {
std::shared_lock<std::shared_mutex> sharedLock(mutex_);
for (auto it = models_.begin(); it != models_.end(); ++it)
if ((*it)->getId() == id)
{
sharedLock.unlock();
std::unique_lock<std::shared_mutex> uniqueLock(mutex_);
models_.erase(it);
return;
}
}

sharedLock.unlock();
std::unique_lock<std::shared_mutex> uniqueLock(mutex_);
Just because two operations are individually atomic does not mean that one followed by the other represents an atomic sequence. Once you give up a lock, you give it up. If that mutex guards access to the container, there's nothing preventing the iterator you have from being invalidated.
What you're trying to do is have the inner unique_lock atomically upgrade the outer shared_lock in exclusive mode. That can't be done in C++17 at all.
And of course, you never re-lock the shared_lock before you leave the if block, so after erasing one element, you're in trouble.
And that ignores the fact that whenever you erase an element from either of your loops, you'll skip the next one. The iterator returned by erase points to the next element, and your ++it in the loop header will skip it. This is true of both functions.
But in any case, the overall purpose of shared_mutex is to permit multiple readers but only one modifier. Your entire operation is really a modification operation. It may be conditionally modifying, but atomically it is a modification operation. What you want is for everyone to either see the list as it was before the operation or for everyone to see the list as it was after all matching elements are erased. Nobody should ever see the list while you're modifying it.
So it should use exclusive access.

Related

Pushing to a vector and iterating on it from different threads

Is such a piece of code safe?
vector<int> v;
void thread_1()
{
v.push_back(100);
}
void thread_2()
{
for (int i : v)
{
// some actions
}
}
Does for (int i : v) compile into something like:
for ( ; __begin != __end; ++__begin)
{
int i = *__begin;
}
Is it possible that push_back in first thread will make data reallocation (when size == capacity), remove old data and *__begin in another thread will dereference freed iterator? And a runtime crash will occur
If so, how should I synchronize the threads? Something like:
void thread_1()
{
mtx.lock();
v.push_back(100);
mtx.unlock();
}
void thread_2()
{
mtx.lock();
for (int i : v)
{
// some actions
}
mtx.unlock();
}
?
Simply put, On some architectures, Fundamental types are inherently atomic, while on others they are not.
On those architectures, writing and reading from and to vector<int> v is thread safe as long as no reallocation occurs and ints are properly aligned; but it depends on various factors.
BUT:
you may want to avoid writing architecture-specific code (unless you want your code to basically run only on your own computer)
You have no mechanism in your code to prevent reallocation (which may invalidate iterators held by other threads), and since you also have no mechanism to synchronize the threads in your code, such reallocations can easily occur.
Considering your design, if you have a std::vector of classes/structs instead of Fundamental Types, you will also risk race conditions and/or UB even in simple concurrent read/writes since one thread can see the vector's element in a broken state (i.e. thread2 can see element#x in vector while it is being changed{push_back'd} by thread1)
in order to ensure thread safety, you have many options:
Prevent modifications to the queue entirely while its being read/written to - basically what you are doing in you own solution- using a global mutual exclusion mechanism
Prevent modifications by other threads for the element currently being manipulated using fine-grained mutual exclusion (could get tricky for linked data structures)
Use thread-safe data structure which have built-in mechanisms to ensure that a single element cannot be accessed by multiple threads
...

Reading from std::map without atomic flag on write

I have a std::map myMap and a std::atomic myLock.
The write is:
if(myLock == 0)
{
myLock++;
myMap.insert(key, value);
myLock--;
}
If I do something like this without locking from another thread, is this considered undefined behavior? The key thing is, I do not mind if the results are not accurate (ie a value in the map updated after I iterated past it). I just don't want to crash.
MyConstIterator endIt = mMap.cend();
for(MyConstIterator it = myMap.cbegin(); it != endIt; ++it)
{
}
I'm trying to achieve lock less reads without a mutex, but I know std::map is not thread safe. Do I have to add to the atomic lock to avoid a crash?
Your use of lock won't make your map thread safe. Two threads can read myLock == 0 and head into your brace.
You need a mutex. This answer on locking may be useful.

safe usage of iterators in multithreaded applications

Say we have a data structure protected by a lock.
Now, Thread1 carries out these statements(say in function block X):
mGeoCodeVectorLock.lock();
auto it = std::find(mGeoCodeVector.begin(), mGeoCodeVector.end(), tokenName);
if(it != mGeoCodeVector.end()) {
mGeoCodeVector.erase(it);
}
mGeoCodeVectorLock.unlock();
And then Thread2 executes these statements(say in another function block Y):
auto iter = std::find(mGeoCodeVector.begin(), mGeoCodeVector.end(), tokenName);
mGeoCodeVectorLock.lock();
if(it != mGeoCodeVector.end()) {
mGeoCodeVector.erase(it);
}
mGeoCodeVectorLock.unlock();
Now, The way I see it:
1) If thread1 gets the lock in function block X and manages to erase the vector, then , the thread already holding an iterator and waiting on lock in function block Y ends up with an invalidated iterator since the container size has been changed by thread1.
2) So, the easy way to fix it would be i guess, to take all your iterators only once you manage to get hold of lock.
Are there some general principles/idioms of safe iterator usage, especially with regards to multi threaded applications?
Yes.
Lock the resource
create the iterators
mutate or query the resource
throw away the iterators
unlock the resource.
of course, using RAII in the implementation of the lock automates 1 and 5.
What is your structure, is it a true vector or a list? If it is a vector, you have to aquire the lock before doing find. However, if it is a list (or can be made a list) removing from the list doesn't invalidate any iterators other than to the object being removed, so as long as token name is different, no locks are required.

Mutex when writing to queue held in map for thread safety

I have a map<int, queue<int>> with one thread writing into it i.e. pushing messages into the queues. They key refers to a client_id, and the queue holds messages for the client. I am looking to make this read-write thread safe.
Currently, the thread that writes into it does something like this
map<int, queue<int>> msg_map;
if (msg_map.find(client_id) != msg_map.end())
{
queue<int> dummy_queue;
dummy_queue.push(msg); //msg is an int
msg_map.insert(make_pair(client_id, dummy_queue);
}
else
{
msg_map[client_id].push(msg);
}
There are many clients reading - and removing - from this map.
if (msg_map.find(client_id) != msg_map.end())
{
if (!msg_map.find(client_id)->second.empty())
{
int msg_rxed = msg_map[client_id].front();
//processing message
msg_map[client_id].pop();
}
}
I am reading this on mutexes (haven't used them before) and I was wondering when and where I ought to lock the mutex. My confusion lies in the fact that they are accessing individual queues (held within the same map). Do I lock the queues, or the map?
Is there a standard/accepted way to do this - and is using a mutex the best way to do this? There are '0s of client threads, and just that 1 single writing thread.
Simplifying and optimizing your code
For now we'll not concern ourselves with mutexes, we'll handle that later when the code is cleaned up a bit (it will be easier then).
First, from the code you showed there seems to be no reason to use an ordered std::map (logarithmic complexity), you could use the much more efficient std::unordered_map (average constant-time complexity). The choice is entirely up to you, if you don't need the container to be ordered you just have to change its declaration:
std::map<int, std::queue<int>> msg_map;
// or
std::unordered_map<int, std::queue<int>> msg_map; // C++11 only though
Now, maps are quite efficient by design but if you insist on doing lookups for each and every operation then you lose all the advantage of maps.
Concerning the writer thread, all your block of code (for the writer) can be efficiently replaced by just this line:
msg_map[client_id].push(msg);
Note that operator[] for both std::map and std::unordered_map is defined as:
Inserts a new element to the container using key as the key and a default constructed mapped value and returns a reference to the newly constructed mapped value. If an element with key key already exists, no insertion is performed and a reference to its mapped value is returned.
Concerning your reader threads, you can't directly use operator[] because it would create a new entry if none currently exists for a specific client_id so instead, you need to cache the iterator returned by find in order to reuse it and thus avoid useless lookups:
auto iter = msg_map.find(client_id);
// iter will be either std::map<int, std::queue<int>>::iterator
// or std::unordered_map<int, std::queue<int>>::iterator
if (iter != msg_map.end()) {
std::queue<int>& q = iter->second;
if (!q.empty()) {
int msg = q.front();
q.pop();
// process msg
}
}
The reason why I pop the message immediately, before processing it, is because it will improve concurrency when we add mutexes (we can unlock the mutex sooner, which is always good).
Making the code thread-safe
#hmjd's idea about multiple locks (one for the map, and one per queue) is interesting, but based on the code you showed us I disagree: any benefit you'll get from the additional concurrency will quite probably be negated by the additional time it takes to lock the queue mutexes (indeed, locking mutexes is a very expensive operation), not to mention the additional code complexity you'll have to handle. I'll bet my money on a single mutex (protecting the map and all the queues at once) being more efficient.
Incidentally, a single mutex solves the iterator invalidation problem if you want to use the more efficient std::unordered_map (std::map doesn't suffer from that problem though).
Assuming C++11, just declare a std::mutex along with your map:
std::mutex msg_map_mutex;
std::map<int, std::queue<int>> msg_map; // or std::unordered_map
Protecting the writer thread is quite straightforward, just lock the mutex before accessing the map:
std::lock_guard<std::mutex> lock(msg_map_mutex);
// the lock is held while the lock_guard object stays in scope
msg_map[client_id].push(msg);
Protecting the reader threads is barely any harder, the only trick is that you'll probably want to unlock the mutex ASAP in order to improve concurrency so you'll have to use std::unique_lock (which can be unlocked early) instead of std::lock_guard (which can only unlock when it goes out of scope):
std::unique_lock<std::mutex> lock(msg_map_mutex);
auto iter = msg_map.find(client_id);
if (iter != msg_map.end()) {
std::queue<int>& q = iter->second;
if (!q.empty()) {
int msg = q.front();
q.pop();
// assuming you don't need to access the map from now on, let's unlock
lock.unlock();
// process msg, other threads can access the map concurrently
}
}
If you can't use C++11, you'll have to replace std::mutex et al. with whatever your platform provides (pthreads, Win32, ...) or with the boost equivalent (which has the advantage of being as portable and as easy to use as the new C++11 classes, unlike the platform-specific primitives).
Read and write access to both the map and the queue need synchronized as both structures are being modified, including the map:
map<int, queue<int>> msg_map;
if (msg_map.find(client_id) != msg_map.end())
{
queue<int> dummy_queue;
dummy_queue.push(msg); //msg is an int
msg_map.insert(make_pair(client_id, dummy_queue);
}
else
{
msg_map[client_id].push(msg); // Modified here.
}
Two options are a mutex that locks both the map and queue or have a mutex for the map and a mutex per queue. The second approach is preferable as it reduces the length of time a single lock is held and means multiple threads can be updating several queues concurrently.

How to minimize mutex locks when iterating a list while being able to add elements from another thread?

In a worker thread, I iterate over a list of items that need updating. In the main thread, the user often calls a function to add elements to that list. I'm using a mutex to prevent the list from being modified while it's being iterated. Generally, the pseudocode looks like this:
// called by the client to add an element.
void AddElementToList(element)
{
// lock mutex
// add element to the list
// unlock mutex
}
// this function is run by the worker thread
void WorkerThreadUpdate()
{
// lock mutex
// for each element in the list
// update the element
// mark the element for deletion if finished
// for every finished element
// remove element from list
// unlock mutex
}
The problem comes when updating the element. The element's update function can potentially take a long time, up to about one second in some cases. When this occurs, the user's next call to AddElementToList blocks, causing their application to freeze until all the element have been updated. The AddElementToList function is called often enough for this to be a very noticable problem.
So I'm looking for a better method. I want to update the elements while protecting the list but keep the user's main thread responsive. I'm sure this is pretty much multithreading 101, but I can't come up with the correct terms to find examples of what I'm looking for.
I was thinking of a solution that uses two lists, a "request" list and a "work" list.
The user calls AddElementToList which adds the element to the request list. In the worker thread, after the work list has been iterated, it goes through the request list and adds its elements to the work list for the next frame. It only locks when it actually modifies the lists.
// called by the client to add an element.
void AddElementToList(element)
{
// lock mutex
// add element to the request list
// unlock mutex
}
// this function is run by the worker thread
void WorkerThreadUpdate()
{
// for each element in the work list
// update the element
// mark the element for deletion if finished
// for every finished element
// lock mutex
// remove element from work list
// unlock mutex
// lock mutex
// for every element in the request list
// add element to the work list
// clear the request list
// unlock mutex
}
I think this should work, but I'm not entirely sure. Is this an acceptable way? Are there better methods for handling this?
Your plan to queue up the additions should work. Whether it's acceptable or not depends on whether it's acceptable to wait for queue additions until the thread does its next 'update' pass.
Is it even necessary to lock the list during the update? What else is accessing this list, and when? Can you lock the list, make a copy vector/list of the references, unlock the list and then run the update on each of the the copy refs one-by-one?
Let me ask these first:
what actually is a list in your implementation? A dynamic array? A linked list? Some hash table?
-- If it is a list, operation on one element should affect only the next, previous elements and possible the head and tail.
how do you add elements to the list? Always at the end? Or can insertions occur somewhere in the middle?
Let's assume that:
It is a single-or-bi-direction linked list, with additional variables pointed to its head and tail.
You add the elements only at the end.
If that is the case, I would suggest doing it somehow similar to this:
void AddElementToList(element) {
mutex_lock("push");
list.push_back(element);
mutex_release("push");
}
void WorkerThreadUpdate() {
AddElementToList(specialElement);
/*
at this point we are guaranteed that other threads,
which can only add to the list,
will not affect us, because we have a "guard" between us and them
*/
iterator del=NULL;
iterator i=list.begin();
for(; *i!=specialElement; ++i) {
if (del) {
del->remove();
del=NULL;
}
i->update();
if (some_condition)
del=i;
}
//*i == specialElement now
/*
We are now operating on the guard, so we have to be careful.
"specialElement" could still be the last element in the list.
*/
mutex_lock("push");
i->remove();
mutex_release("push");
}
I am not sure if standard STL is thread-safe and if you can just use their implementation of the list. Read their specs. If not - just implement your own list container.
Is there any chance that another thread will try to access the element
you're updating. If not, you can free the lock on the list before
starting the update, and reacquire it when you want to continue
iterating (or to delete the object). Something along the lines of:
void
WorkerThreadUpdate()
{
// lock mutex
ListType::iterator current = list.begin();
while ( current != list.end() ) {
ListType::value_type& obj = *current;
// unlock mutex
// do modifications...
// lock mutex
if ( needsDeleting ) {
current = list.erase( current );
} else {
++ current;
}
}
// unlock mutex
}
The important thing is that you must hold the lock when actually
accessing the iterator (at least with any of the standard containers).
Of course, you'll want to use some sort of scoped lock, just in case.
(Although that might not be necessary; I think all of the code in the
locked region should be no-throw.) I've not tried it, but I think you
could use std::unique_lock if you have C++11.
IMHO, while this works, it's not particularly elegant. I'd probably
keep the list entirely on the worker side, and use a message queue to
pass the objects to be inserted, with the worker thread reading from the
message queue from time to time in order to get the objects to insert.