CWaitableTimer

A C++ class that wraps Win32 waitable timers.
by
Dr. Thomas Becker
tmbecker@compuserve.com


Contents

  1. Copyright and Permission Notice
  2. Overview of CWaitableTimer
  3. CWaitableTimer Public Members
  4. CWaitableTimer Exception Handling
  5. CWaitableTimer 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 CWaitableTimer

class CWaitableTimer

The CWaitableTimer class wraps the Win32 waitable timer object. Here, timers are restricted to non-periodic having no completion routine. The reason for providing this wrapper is to make it easier to switch to a substitute when working with NT version 3.51 or less. Waitable timers did not become available until version 4.0.

The constructor creates a waitable timer. The Set member function activates the timer, causing it to become signaled after the specified amount of time.

Example:

CWaitableTimer someTimer ;
someTimer.Set(3000) ;
Three seconds later, the handle that is returned by someTimer.GetHandle() will become signaled.

When the Set member function is called again while the timer is active and waiting for the time period to elapse, the handle is not signaled and the time interval starts over again.

When the Cancel member function is called while the timer is active and waiting for the time period to elapse, the timer enters the inactive state without signaling the handle.

Note: When you try to call the Set or Cancel member functions while the timer is active, it can never be guaranteed that the time interval does not complete and the handle does not get signaled before Set or Cancel get a chance to interrupt the timer. The Set member function always resets the handle to the non-signaled state. The Cancel member function, by contrast, does not affect the state of the handle at all.

The constructor does not perform any error checking. The Set and Cancel member functions throw a CWin32Exception with error code ERROR_INVALID_HANDLE if they find that the constructor failed to create the timer.

If the destructor encounters an error, it throws a CWin32Exception but does not propagate it.

CWaitableTimer Public Members

Enums Construction
CWaitableTimer Constructs a CWaitableTimer object.
Operations
Set Sets the timer.
Cancel Cancels the timer.
GetHandle Returns a handle to the timer object.

CWaitableTimer::CWaitableTimer

The CWaitableTimer default constructor creates the waitable timer object.
CWaitableTimer::CWaitableTimer( 
  BOOL bManualReset // = FALSE, manual reset timer
  );

Parameters

bManualReset
Indicates if the timer should be a manual reset timer.

Remarks

After construction, the timer is in an inactive state, and the handle is not signaled. No error checks are made to prevent exceptions propagating from the constructor. Validity checks on the handles will be performed in member functions.

CWaitableTimer::Set

The Set member function sets the timer to the specified number of milliseconds.
void CWaitableTimer::Set(
  DWORD dwMilliSeconds // timer interval
  );

Parameters

dwMilliseconds
Indicates the timer interval in milliseconds.

Return Value and Exceptions

If the function fails, it throws a CWin32Exception.

Remarks

The function can be called anytime. When the timer is in the active state and waiting for the time interval to elapse, it is started anew without the handle being signaled.

void CWaitableTimer::Cancel

The Cancel member function interrupts the timer and causes it to enter the inactive state.
CWaitableTimer::Cancel(
  );

Return Value and Exceptions

If the function fails, it throws a CWin32Exception.

Remarks

The cancel member function does not affect the state of the handle. All that is guaranteed is that when the cancel function returns, the timer is in the inactive state, waiting to be set off again.

When the function catches the timer in the active state while it is waiting for the time period to elapse, the wait is abandoned and the handle is not signaled.

When the function catches the timer in the inactive state, the cancel operation has no effect.


CWaitableTimer::GetHandle

Returns a handle to the timer object.
HANDLE CWaitableTimer::GetHandle(
  );

CWaitableTimer Exception Handling

The implementation of CWaitableTimer 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 CWaitableTimer 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.

CWaitableTimer Example

The following program can be used to test and demonstrate the functionality of the CWaitableTimer class.
#include<windows.h>
#include<iostream.h>
#include<conio.h>
#include"Win32Exceptions.h"
#include"WaitableTimer.h"

// Manual reset timer
CWaitableTimer cTimer(FALSE) ;

// Thread function that waits on timer
//
DWORD WINAPI Waiter(DWORD)
{
  while (1)
  {
    WaitForSingleObject(cTimer.GetHandle(), INFINITE) ;
    Beep(300, 100) ;
  }
  
  return 0 ;
}

///////////////////////////////////////////////////////////////
//
// main
//
void main()
{
  try
  {
    // Create waiter thread
    //
    DWORD dwThreadId ;
    CreateThread(
      NULL,	// pointer to thread security attributes  
      0,	// initial thread stack size, in bytes 
      (LPTHREAD_START_ROUTINE) Waiter,	// thread function 
      0,	// argument for new thread 
      0,	// creation flags 
      &dwThreadId 	// pointer to returned thread identifier 
      );	
    
    // Start and cancel timer according to user input
    //
    cout << "Thread ID: " << dwThreadId << "\n" 
         << "Enter s to set, c to cancel, ESC to quit:\n" 
         << flush ;
    char c = getche() ;
    cout << "\n"  << flush ;
    //
    while (27 != c)
    {
      switch (c)
      {
      case 's':
        cTimer.Set(2000) ;
        break ;
      case 'c':
        cTimer.Cancel() ;
        ResetEvent(cTimer.GetHandle()) ;
        break ;
      }
  
      c = getche() ;
      cout << "\n"  << flush ;
    }

  }
  catch(CWin32Exception& eWin32)
  {
    cout << "Exception:\n"
         << "Error code: " 
         << eWin32.GetErrorCode() 
         << "\nFile: " 
         << eWin32.GetFileName() 
         << "\nLine: " 
         << eWin32.GetLineNo() 
         << "\nPress any key to continue." 
         << flush ;
     getche() ;
  }
  
}