A C++ class that provides a simple thread-safe FIFO queue.
by
Dr. Thomas Becker
tmbecker@compuserve.com
COPYRIGHT (C) 1997 Thomas BeckerPERMISSION NOTICE:
PERMISSION TO USE, COPY, MODIFY, AND DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE AND WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT ALL COPIES ARE ACCOMPANIED BY THE COMPLETE MACHINE-READABLE SOURCE CODE, ALL MODIFIED FILES CARRY PROMINENT NOTICES AS TO WHEN AND BY WHOM THEY WERE MODIFIED, THE ABOVE COPYRIGHT NOTICE, THIS PERMISSION NOTICE AND THE NO-WARRANTY NOTICE BELOW APPEAR IN ALL SOURCE FILES AND IN ALL SUPPORTING DOCUMENTATION.
NO-WARRANTY NOTICE:
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF MERCHANTABILITY ARE HEREBY DISCLAIMED.
The first template argument indicates the type of the queue elements, the second one indicates the length of the queue. It is easy to change the source code so that the length of the queue can be set at runtime: make the template argument a class member that is set by the constructor, find the places where it occurs as the length parameter in array declarations, and replace these array declarations with heap allocations using the new operator.
The constructor takes as an optional argument the handle to an exit event. Setting this event will cause the Insert and Retrieve member functions to return immediately. This event handle can also be specified later by means of the SetHandleToExitEvent member function. Note that an exit event must be specified before any of the member function can be called.
The Insert member function inserts an element into the queue. This is the function that is typically called by a listening thread. Such a thread accepts clients from a communications device and then places in the queue a pointer or an array index that identifies e.g. a socket descriptor or a pipe handle. Elements are always copied into the queue; therefore, the element type must possess a public copy constructor if it is a user-defined class.
The Retrieve member function retrieves an element from the queue. This is the function that is typically called by worker threads to retrieve a pointer or an array index leading to a communications device.
All member functions throw CWin32Exceptions if Win32 errors are encountered.
waitResult | Return values from functions that perform wait operations. |
CPassiveQueue | Constructs a CPassiveQueue object. |
Insert | Inserts an element into the queue. |
Retrieve | Retrieves an element from the queue. |
SetHandleToExitEvent | Places the handle to the exit event in a private member variable. |
enum waitResult { waitTimeout, // timeout occurred waitExitEvent, // exit event was set waitSuccess // operation completed successfully } ;
template<class T, DWORD dwLEN> CPassiveQueue<T, dwLEN>::CPassiveQueue( HANDLE hExitEvent // = NULL exit event );
template<class T, DWORD dwLEN> CPassiveQueue<T, dwLEN>::waitResult CPassiveQueue<T, dwLEN>::Insert( const T& refNewElement, // new element DWORD dwMilliseconds // timeout );
template<class T, DWORD dwLEN> CPassiveQueue<T, dwLEN>::waitResult CPassiveQueue<T, dwLEN>::Retrieve( T& refLocation, // location to retrieve to DWORD dwMilliseconds // timeout );
template<T, dwLEN> void CPassiveQueue<T, dwLEN>::SetHandleToExitEvent( HANDLE hExitEvent );
Win32Exception.h also contains the declaration of a class named CSehException, which can be used to catch operating system exceptions (SEH-exceptions) such as access violations via the C++ exception handling mechanism. See the documentation of the _set_se_translator() function and the article "Structured exception handling" in the C++ Language Reference for a detailed explanation.
The programm creates an instance of the CPassiveQueue class as a global variable:
#define LEN_QUEUE 6 CPassiveQueue<DWORD, LEN_QUEUE> cThePassiveQueue ;A separate thread waits in an infinite loop for clients to connect to an instance of a named pipe. Everytime a client has connected, the index of the pipe instance that the client is using is placed into the queue:
DWORD dwPipeIndex ; CPassiveQueue<DWORD, LEN_QUEUE>::waitResult enInsertRetVal ; while ( 1 ) { // // Wait for a client to connect to an instance of a named pipe. // When a client has connected, place the index of the pipe // pipe index in the dwPipeIndex variable. // // Place index dwPipeIndex of connected pipe in the queue. // enInsertRetVal = cThePassiveQueue.Insert( dwPipeIndex, INFINITE ) ; // Break if exit event was set // if (CPassiveQueueThe indices of connected pipe instances are retrieved from the queue by worker threads. These threads perform read and write operations on the pipe instance and then reconnect it:::waitExitEvent == enInsertRetVal) break ; }
DWORD dwPipeIndex ; CPassiveQueue<DWORD, LEN_QUEUE>::waitResult enRetrieveRetVal ; while(1) { // Retrieve a pipe index from queue. // enRetrieveRetVal = cThePassiveQueue.Retrieve( dwPipeIndex, INFINITE ) ; // Break if exit event was set // if (CPassiveQueue::waitExitEvent == enRetrieveRetVal) break ; // // Serve a client on the named pipe instance with index dwPipeIndex, // then reconnect the pipe. // }