/* * 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 #include #include using namespace std; template class BoundedBuffer: private std::list { 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 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 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 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. };