#ifndef BLOCKINGQUEUE_H #define BLOCKINGQUEUE_H ///////////////////////////////////////////////////////////////////// // blockingQueue.cpp - queue that blocks on deQ of empty queue // // ver 1.0 // // Language: Visual C++, ver 7.1, SP 2 // // Platform: Dell Dimension 8300, Win XP Pro, SP2 // // Application: CSE687 - Object Oriented Design // // Author: Jim Fawcett, CST 2-187, Syracuse Univ // // (315) 443-3948, jfawcett@twcny.rr.com // // // ///////////////////////////////////////////////////////////////////// /* Module Operations ================= This module provides a simple thread-safe blocking queue, based on the STL queue container adapter. When a client thread attempts to deQ an empty queue, it will block until another thread enQs an item. This prevents very high CPU utilization while a reading thread spin locks on an empty queue. Public Interface ================ BQueue Q // create blocking queue holding std::strings Q.enQ("an item"); // push onto queue std::string str = Q.deQ(); // pop from queue size_t s = Q.size(); // number of elements in queue Q.clear() // remove all contents from queue */ // /////////////////////////////////////////////////////////////////////// // Build Process // /////////////////////////////////////////////////////////////////////// // Required Files: // // blockingQueue.h, blockingQueue.cpp // // // // Compiler Command: // // cl /GX /DTEST_BLOCKINGQUEUE blockingQueue.cpp // // // /////////////////////////////////////////////////////////////////////// /* Maintenance History =================== ver 1.0 : 09 Apr 2005 - extracted from threads ver 3.8 ver 3.7 : 08 May 2004 - fixed a subtle bug in the blocking queue. Added a second check to the deQ locking test. See deQ prologue for details. ver 3.6 : 01 May 2004 - fixed blocking queue, BQueue::clear() function which called a non-existing std::queue function, discovered by Arun Iyer. - added copy ctors and assignment operators to LLock and BQueue classes. */ // #include #include #include #include #include "locks.h" ///////////////////////////////////////////////////////////////////// // BQueue class declaration - blocking queue based on STL queue template class BQueue { public: BQueue(); BQueue(const BQueue& bq); ~BQueue(); BQueue& operator=(const BQueue& Q); void enQ(const T& t); T deQ(); void clear(); size_t size(); private: std::queue _Q; LLock _l; HANDLE _hEvent; bool _waitFlag; }; //----< constructor >------------------------------------------------ template BQueue::BQueue() : _waitFlag(false) { _hEvent = CreateEvent(0,FALSE,TRUE,0); if(_hEvent==NULL) throw exception("CreateEvent failed in BQueue::BQueue()"); } //----< copy constructor >------------------------------------------- template BQueue::BQueue(const BQueue& bq) : _Q(bq._Q), _waitFlag(false) { _hEvent = CreateEvent(0,FALSE,TRUE,0); if(_hEvent==NULL) throw exception("CreateEvent failed in BQueue::BQueue()"); } //----< destructor >------------------------------------------------- template BQueue::~BQueue() { CloseHandle(_hEvent); } //----< assignment >------------------------------------------------- template BQueue& BQueue::operator =(const BQueue& bq) { if(this==&bq) return *this; _Q = bq._Q; _waitFlag = false; SetEvent(_hEvent); return *this; } // //----< enqueue >---------------------------------------------------- template void BQueue::enQ(const T& t) { _l.lock(); _Q.push(t); SetEvent(_hEvent); _l.unlock(); } //----< dequeue >---------------------------------------------------- // // This looks more complicated than you might think necessary. // However, without the second _Q.size() check: // If a single item is in the queue and a thread moves toward // the deQ, but finishes its time slice before deQ'ing, then // another thread may get through the locks, deQ the single // item and leave. When the first thread wakes up, it has // a false _waitFlag and so attempts to deQ the empty queue. // The second check prevents this. Now, if the second check // fails the deQ attempt must be repeated so we don't return // an uninitialized t value. // template T BQueue::deQ() { bool _waitFlag = false; bool popped = false; T t; do { _l.lock(); if(_Q.size() == 0) { ResetEvent(_hEvent); _waitFlag = true; } _l.unlock(); if(_waitFlag) { // sout << "\n Blocking on empty Q"; // to see blocking action WaitForSingleObject(_hEvent,INFINITE); } _waitFlag = false; _l.lock(); if(_Q.size() > 0) { t = _Q.front(); _Q.pop(); popped = true; } _l.unlock(); } while(!popped); return t; } // //----< purge queue >------------------------------------------------ template void BQueue::clear() { while(_Q.size() > 0) _Q.pop(); } //----< return number of elements in queue >------------------------- template size_t BQueue::size() { return _Q.size(); } #endif