I'm currently trying to wrap my head around the problem of thread-safety using C++ STL containers. I recently tried to implement a thread safe std::vector by using a std::mutex as a member variable, just to then realize that although I could make member functions thread-safe by locking the lock, I couldn't make lib functions like std::sort thread-safe, since they only get the begin()/end() iterators, which is a result of the fundamental split between containers and algorithms in the STL in general.
So then I thought, if I can't use locks, how about software transactional memory (STM)?
So now I'm stuck with this:
#include <atomic>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <vector>
#define LIMIT 10
std::atomic<bool> start{false};
std::vector<char> vec;
void thread(char c)
{
while (!start)
std::this_thread::yield();
for (int i = 0; i < LIMIT; ++i) {
__transaction_atomic {
vec.push_back(c);
}
}
}
int main()
{
std::thread t1(thread, '*');
std::thread t2(thread, '@');
start.store(true);
t1.join();
t2.join();
for (auto i = vec.begin(); i != vec.end(); ++i)
std::cout << *i;
std::cout << std::endl;
return EXIT_SUCCESS;
}
Which I compile with:
g++ -std=c++11 -fgnu-tm -Wall
using g++ 4.8.2 and which gives me the following error:
error: unsafe function call to push_back within atomic transaction
Which I kinda get, since push_back or sort or whatever isn't declared transaction_safe but which leaves me with the following questions:
a) How can I fix that error?
b) If I can't fix that error, then what are these transactional blocks usually used for?
c) How would one implement a lock-free thread-safe vector?!
Thanks in advance!
Edit: Thanks for the answers so far but they don't really scratch my itch. Let me give you an example: Imagine I have a global vector and access to this vector shall be shared amongst multiple threads. All threads try to do sorted inserts, so they generate a random number and try to insert this number into the vector in a sorted manner, so the vector stays sorted all the time (including duplicates ofc). To do the sorted insert they use std::lower_bound to find the "index" where to insert and then do the insert using vector.insert().
If I write a wrapper for the std::vector that contains a std::mutex as a member, than I can write wrapper functions, e.g. insert which locks the mutex using std::lock_guard and then does the actual std::vector.insert() call. But std::lower_bound doesn't give a damn about the member mutex. Which is a feature, not a bug afaik.
This leaves my threads in quite a pickle because other threads can change the vector while someone's doing his lower_bound thing.
The only fix I can think of: forgett the wrapper and have a global mutex for the vector instead. Whenever anybody wants to do anything on/with/to this vector, he needs that lock.
THATS the problem. What alternatives are there for using this global mutex. and THATS where software transactional memory came to mind.
So now: how to use STMs on STL containers? (and a), b), c) from above).