I am trying to have a bunch of producer threads that wait untill the buffer has room for an item, then it puts items in the buffer while it can, going back to sleep if there is no more room.
At the same time there should be a bunch of consumer threads that wait untill there is something in the buffer, then it takes things from buffer while it can, going back to sleep if it's empty.
In pseudo code, here's what Iam doing, but all Iam getting is deadlocks.
condition_variable cvAdd;
condition_variable cvTake;
mutex smtx;
ProducerThread(){
while(has something to produce){
unique_lock<mutex> lock(smtx);
while(buffer is full){
cvAdd.wait(lock);
}
AddStuffToBuffer();
cvTake.notify_one();
}
}
ConsumerThread(){
while(should be taking data){
unique_lock<mutex> lock(smtx);
while( buffer is empty ){
cvTake.wait(lock);
}
TakeStuffFromBuffer();
if(BufferIsEmpty)
cvAdd.notify_one();
}
}
One other error worth mentioning is that your consumers only notify the waiting producers when the buffer becomes empty.
The optimal ways to notify consumers is only when the queue was full.
E.g.:
template<class T, size_t MaxQueueSize>
class Queue
{
std::condition_variable consumer_, producer_;
std::mutex mutex_;
using unique_lock = std::unique_lock<std::mutex>;
std::queue<T> queue_;
public:
template<class U>
void push_back(U&& item) {
unique_lock lock(mutex_);
while(MaxQueueSize == queue_.size())
producer_.wait(lock);
queue_.push(std::forward<U>(item));
consumer_.notify_one();
}
T pop_front() {
unique_lock lock(mutex_);
while(queue_.empty())
consumer_.wait(lock);
auto full = MaxQueueSize == queue_.size();
auto item = queue_.front();
queue_.pop();
if(full)
producer_.notify_all();
return item;
}
};