In the following multi producer and single consumer fixed size queue implementation i see the queue being overwritten by producers.
Why is the queue being overwritten?
At this point am not looking at performance of the code but would like to see that every item pushed is consumed and is not lost.
How do i extend this to multi prod multi consumer?
#include <iostream>
#include <thread>
#include <mutex>
#include <utility>
#include <ctime>
#include <condition_variable>
#include <stdlib.h>
#include <unistd.h>
#include <atomic>
using namespace std;
enum class returncode { QUEUE_EMPTY, QUEUE_FULL,SUCCESS } ;
atomic_long d;
class Queue {
private:
long* qdata;
atomic_size_t length;
atomic_size_t head;
atomic_size_t tail ;
atomic_size_t size;
std::mutex qmutex;
std::condition_variable qcond ;
public:
Queue(size_t len): length(len)
{
qdata=new long[length] ;
head=0;
tail=0;
size=0;
srand(time(NULL));
}
bool isfull()
{
if( size == length )
return true;
return false;
}
bool isempty()
{
if(size == 0)
return true;
return false;
}
returncode push(long data)
{
std::unique_lock<std::mutex> qguard(qmutex);
if(size == length) //if isfull
return returncode::QUEUE_FULL ;
qdata[head]=data;
head = (head+1) % length;
size += 1 ;
qcond.notify_one();
qguard.unlock(); //Should the unlock come before notify_one?
return returncode::SUCCESS;
}
//Its the users responsibility to check isempty before calling front.
//Read function
long front(void)
{
std::unique_lock<std::mutex> qguard(qmutex);
//if isempty
if( size == 0 )
return 0;
long d=qdata[tail];
return d;
}
void pop(void)
{
std::thread::id thisthreadid = std::this_thread::get_id();
std::unique_lock<std::mutex> qguard(qmutex);
while(size == 0 ) // check condition to be safe against spurious wakes
{
qcond.wait(qguard);// release lock and go join the waiting thread queue
return;
}
tail =(tail+1) % length;
size -= 1;
}
};
void produce(Queue* q)
{
while(true)
{
d++;
q->push(d) ;
//sleep(1);
}
}
void consume(Queue* q)
{
while(true){
//usleep(1000);
cout <<"Consume: Pop " << q->front() <<endl;
q->pop();
}
}
int main(int argc, char** argv )
{
Queue q(50);
d=0;
int nprods=4;
int nconsu=1;
std::thread producers[nprods];
std::thread consumers[nconsu];
for (int i=0; i < nprods ; ++i)
producers[i] = std::thread(produce, &q);
for (int i=0; i < nconsu ; ++i)
consumers[i] = std::thread(consume, &q);
for (int i=0; i<nprods; ++i)
producers[i].join();
for (int i=0; i<nconsu; ++i)
consumers[i].join();
return 0;
}
Output:
Consume: Pop 1 Consume: Pop 2 ... Sequential all upto the queue size and then q is over written. Consume: Pop 49 Consume: Pop 50 Consume: Pop 51 Consume: Pop 52 Consume: Pop 53 Consume: Pop 54 Consume: Pop 55 Consume: Pop 56 Consume: Pop 64 --> 57 to 63 lost Consume: Pop 72 --> 65 to 71 lost Consume: Pop 81 Consume: Pop 89 Consume: Pop 97 Consume: Pop 105