I've written a small lightweight push/pop queue based on a vector (figured it should be fast) like this:
template <typename T> class MyVectorQueue{
public:
MyVectorQueue (size_t sz):my_queue(sz),start(0),end(0){}
void push(const T& val){
size_t idx=atomic_fetch_add(&end,1UL);
if (idx==my_queue.size())
throw std::runtime_error("Limit reached, initialize to larger vector");
my_queue[idx]=val;
}
const T& pop(){
if (start==end)
return end__;
return my_queue[start.fetch_add(1UL)];
}
size_t empty()
{
return end==start;
}
const T& end() {
return end__;
}
private:
T end__;
std::atomic<size_t> start,end;
std::vector<T> my_queue;
};
The size of the vector should be known and I am wondering why is it not thread safe? In what circumstances does this mess my structure?
Your start
and end
are atomic variables, but using std::vector::operator[]
is not atomic operation, making it not thread-safe.
Suppose you have 10 threads and the size of the vector
is 5. Now, suppose all of them are executing, say push
.
Now suppose all 10 threads may have passed the check and the if (end==my_queue.size())
is evaluated to false
, as and end
has not reached the limit, meaning - the vector
is not full.
Then, it's possible that all of them increment end
and at the same time call std::vector::operator[]
. At least 5 of the threads will try to access elements, "outside" the vector.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With