CPassiveQueue

A C++ class that provides a simple thread-safe FIFO queue.
by
Dr. Thomas Becker
tmbecker@compuserve.com


Contents

  1. Copyright and Permission Notice
  2. Overview of CPassiveQueue
  3. CPassiveQueue Public Members
  4. CPassiveQueue Exception Handling
  5. CPassiveQueue Example

Copyright and Permission Notice

COPYRIGHT (C) 1997 Thomas Becker

PERMISSION 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.

Overview of CPassiveQueue

template<class T, DWORD dwLEN>
class CPassiveQueue

The CPassiveQueue class template realizes a simple thread-safe FIFO queue.

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.

CPassiveQueue Public Members

Enums
waitResult Return values from functions that perform wait operations.
Construction
CPassiveQueue Constructs a CPassiveQueue object.
Operations
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.

CPassiveQueue::waitResult

Return values from functions that perform wait operations.
enum waitResult
{
  waitTimeout, // timeout occurred
  waitExitEvent, // exit event was set
  waitSuccess  // operation completed successfully
} ;

CPassiveQueue::CPassiveQueue

The constructor creates the synchronization objects and initializes the member variables. To avoid exceptions from constructors, no error check is made on the Create... APIs. The Insert and Retrieve member functions will throw exceptions if the handles are NULL.
template<class T, DWORD dwLEN>
CPassiveQueue<T, dwLEN>::CPassiveQueue( 
  HANDLE hExitEvent // = NULL exit event
  );

Parameters

hExitEvent
Indicates the exit event that causes all member functions to return immediately. This handle can be set later by means of the SetHandleToExitEvent member function. However, the handle must be set to a valid event handle before the Insert or Retrieve member functions are called for the first time.

CPassiveQueue::Insert

The Insert member function places a new element into the queue.
template<class T, DWORD dwLEN>
CPassiveQueue<T, dwLEN>::waitResult 
CPassiveQueue<T, dwLEN>::Insert(
  const T& refNewElement, // new element
  DWORD dwMilliseconds // timeout
  );

Parameters

refNewElement
Refers to the new element that is to be inserted.
dwMilliseconds
Indicates the timeout value in milliseconds or INFINITE.

Return Value and Exceptions

If the function succeeds, the return value is waitSuccess.
If the function notices that the exit event was set, the return value is waitExitEvent.
If the function fails, a CWin32Exception is thrown.

CPassiveQueue::Retrieve

The Retrieve member function retrieves a pipe index from the queue.
template<class T, DWORD dwLEN>
CPassiveQueue<T, dwLEN>::waitResult 
CPassiveQueue<T, dwLEN>::Retrieve(
  T& refLocation, // location to retrieve to
  DWORD dwMilliseconds // timeout
  );

Parameters

refLocation
Refers to the T location that will receive the result.
dwMilliseconds
Indicates the timeout value in milliseconds or INFINITE.

Return Value and Exceptions

If the function succeeds, the return value is waitSuccess.
If the function notices that the exit event was set, the return value is waitExitEvent.
If the function fails, a CWin32Exception is thrown.

CPassiveQueue::SetHandleToExitEvent

Places the handle to the exit event in a private member variable.
template<T, dwLEN>
void CPassiveQueue<T, dwLEN>::SetHandleToExitEvent(
  HANDLE hExitEvent 
  );

CPassiveQueue Exception Handling

The implementation of CPassiveQueue uses Win32 APIs. All errors are thus identified by the error code that is returned by the GetLastError() API. When such an error occurs, the CPassiveQueue functions throw an exception of type CWin32Exception. CWin32Exception is a simple class that stores the error code as well as the name of the source file and the number of the line where the error occurred. The declaration of CWin32Exception can be found in the file Win32Exception.h.

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.

CPassiveQueue Example

A complete example of an application of the CPassiveQueue class can be found in the file MTServerV1.cpp. The following code fragments are excerpts from that file.

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 (CPassiveQueue::waitExitEvent 
        == enInsertRetVal) break ;

}
The 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:
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.
  //

}