/*
* C++ implementation of bounded buffer. Implementation is essentially
* the monitor one presented in the text, except mutual exclusion must be
* provided explicitly with a mutex.
*/
#include <list>
#include <mutex>
#include <condition_variable>
using namespace std;
template <typename T>
class BoundedBuffer: private std::list<T> {
public:
// Construct the buffer object with the indicates size limit.
BoundedBuffer(int bound = 10)
{
m_bound = bound;
}
// Enqueue the item.
void enqueue(const T &item)
{
// This object locks the mutex for us, and unlocks it when
// the method ends.
unique_lock<mutex> ulo(m_mutex);
// If the queue is full, wait for an empty signal. C++
// condition variables are not guaranteed to resume immediately
// after they are signalled (the operation is actually called
// notify); there may be some delay. So we put the wait in a
// loop so it repeats if the condition becomes false in the
// interim.
while(m_queue.size() >= m_bound)
m_nonfull.wait(ulo);
// Actually update the queue.
m_queue.push_back(item);
// Similar to the monitor signal. Start one waiting thread,
// if any.
m_nonempty.notify_one();
// Mutex is unlocked automatically by uol. This would
// be especially convenient if I had returns in this method,
// as the mutex is always automatically unlocked when the
// method returns.
}
// Dequeue the next item.
void dequeue(T &item)
{
// The inverse of the above. Lock the mutex to protect
// the structure, wait until there's a message, Fetch it,
// notify of extra space, then release.
unique_lock<mutex> ulo(m_mutex);
while(m_queue.size() <= 0)
m_nonempty.wait(ulo);
item = m_queue.front();
m_queue.pop_front();
m_nonfull.notify_one();
}
// Discard the contents of the queue.
void purge()
{
m_mutex.lock();
m_queue.clear();
m_nonfull.notify_all();
m_mutex.unlock();
}
// Return the size. Note that, with threading, this
// could have changed by the time the caller receives the answer.
int size()
{
m_mutex.lock();
int size = m_queue.size();
m_mutex.unlock();
return size;
}
// Note that if the bound is reduced below the current
// size, this is not an error. Attempts to add to the
// queue will block until it shrinks below the limt.
void set_bound(int bound)
{
m_mutex.lock();
m_bound = bound;
m_mutex.unlock();
}
int get_bound()
{
m_mutex.lock();
int ret = m_bound;
m_mutex.unlock();
return ret;
}
private:
std::list<T> m_queue; // Queue contents in an STL list.
int m_bound; // Maximum size.
mutex m_mutex; // Mutual exclusion of the local data.
condition_variable m_nonempty; // Wait for content.
condition_variable m_nonfull; // Wait for room.
};