#ifndef MTQUEUE_H #define MTQUEUE_H /////////////////////////////////////////////////////////////// // mtqueue.h -- thread-safe template-based queue // // ver 2.1 // // Language: Visual C++, ver 6.0 // // Platform: Micron Dual Pentium Pro 200, Win NT 4.0 // // Application: CSE691 - Project #3 prototype // // Author: Jim Fawcett, CST 2-187, Syracuse Univ // // (315) 443-3948, fawcett@syr.edu // /////////////////////////////////////////////////////////////// /* Class Operation =============== The mtqueue class accepts messages with an enQ(m) command where m is the message to be queued. It destructively returns the message with deQ(). deQ() returns messages in the order they were enqueued. enQ() and deQ() operations are protected with a mutex, unique to each class object. So they are efficiently synchronized under reads and writes to multiple queues. mtqueue is implemented with a doubly linked list of nodes. Each node holds pointers to its predecessor and successor, and a message value, so the queue is simply a class holding head and tail pointers, used by enQ(...) and deQ(). tail arrival +------+ +------+ | +------+ server end |node 1| |node 2| | |node 3| end +------+ +------+ +-->+------+ head ----->| next |----->| next |----->| next |----+ +----| prev |<-----| prev |<-----| prev | | | | t1 | | t2 | | t3 | \ / \ / +------+ +------+ +------+ enQ(...) adds a node to the head of the list, logically equivalent to adding an element to the end of the queue. deQ() removes an element from the tail of the list, logically equivalent to removing an element from the beginning of the queue. mtqueue sq; // make a queue to hold string messages sq.enQ(s); // enqueues a message m string s = sq.deQ(); // dequeues a message */ /////////////////////////////////////////////////////////////// // Design Notes // /////////////////////////////////////////////////////////////// /* If you want to use this queue class in an application that will run for a long time, doing lots of enQ(val)s and deQ()s, you should prevent the class from continuously newing and deleting nodes. this will cause the heap to get fragmented and performance will suffer, possibly failing due to memory allocation failures. // The approach I've used is to keep a list of free nodes. When deQuing, instead of deleting the node, I put it on a free list. When enQing, I check to see if the free list has any nodes before newing one. This way the queue will allocate nodes until the queue gets to its maximum length. Then as its length varies with time it will simply reuse nodes on the free list. The free list is managed by a separate class nodePool. By careful design of nodePool, the design of mtqueue was not affected at all. */ /////////////////////////////////////////////////////////////// // Build Instructions // /////////////////////////////////////////////////////////////// // Files Required: // // - mtqueue.h, mtqueue.cpp, nodePool.h, nodePool.cpp // // // // Compiler Command: // // cl -GX -DTEST_MTQUEUE mtqueue.cpp nodePool.cpp // /////////////////////////////////////////////////////////////// /* Maintenance History: ==================== ver 2.1 : 08 Oct 2000 - added synchronization of length ver 2.0 : 11 Feb 2000 - added nodPool class to manage free list - provide ver 1.1 : 31 Jan 2000 - moved using namespace std to implementation files - added private declarations for copy ctor and assignment operator with no implementations so they can't be called - removed reference qualifier for node constructor, e.g., changed node*& to node*. It wasn't needed. ver 1.0 : 14 Nov 1998 - developed this first release by modifying queue.h and queue.cpp */ // /////////////////////////////////////////////////////////////// // declaration of mtqueue class // /////////////////////////////////////////////////////////////// #include #include #include #include "nodePool.h" template class mtqueue { public: mtqueue(); ~mtqueue(); void enQ(const T &t); T deQ(); void flush(); int length(); int freeLength(); private: mtqueue(const mtqueue &mtq); mtqueue& operator=(const mtqueue &mtq); node *head; node *tail; int len; HANDLE mutex; nodePool np; }; //----< return length of free list >--------------------------- template inline int mtqueue::freeLength() { return np.size(); } //----< queue constructor >------------------------------------ // // construct queue by setting both head and tail to null // signifying an empty queue // template mtqueue::mtqueue() : head(NULL), tail(NULL), len(0) { mutex = CreateMutex(0,FALSE,0); } // //----< queue destructor >------------------------------------- // // walk down list and delete each node // template mtqueue::~mtqueue() { /* while(head != NULL) { node *temp = head; head = head->next(); delete temp; } head = tail = NULL; */ CloseHandle(mutex); } //----< return length of queue >------------------------------- template int mtqueue::length() { int temp; WaitForSingleObject(mutex,INFINITE); temp = len; ReleaseMutex(mutex); return temp; } //----< remove contents of queue >----------------------------- template void mtqueue::flush() { while(size() > 0) deQ(); } //----< add element to back of queue >------------------------- // // create node and link to the head of queue list // template void mtqueue::enQ(const T &t) { WaitForSingleObject(mutex,INFINITE); // head = new node(t,head); // create node pointing to old head head = np.retrieveNode(t,head); if(!head) throw "\n memory alloc failure in mtqueue::enQ\n\n"; if(tail != NULL) // if there was a node in queue then head->next()->prev() = head; // make new node its predecessor else // otherwise this is the first node tail = head; // so make tail point to it len++; ReleaseMutex(mutex); } // //----< remove element from front of queue >------------------- // // retrieve value of last node on list, e.g., first in queue // and eliminate node // template T mtqueue::deQ() { static T safe; WaitForSingleObject(mutex,INFINITE); if(tail == NULL) { // empty queue so return safe ReleaseMutex(mutex); return safe; } T temp = tail->value(); // retrieve last node's value if(tail != head) { // if this is not the only node tail = tail->prev(); // make new tail pointing to its // delete tail->next(); // predecessor and delete node, np.saveNode(tail->next()); tail->next() = NULL; // make new last node nextless } else { // otherwise this is only node // delete tail; // delete node and make list empty np.saveNode(tail); head = tail = NULL; // by nulling its head and tail } len--; ReleaseMutex(mutex); return temp; } #endif