Windows Thread Synchronization 26

 

 

 

 

 

Using Waitable Timers with an Asynchronous Procedure Call Program Example

 

The following example associates an asynchronous procedure call (APC) function, also known as a completion routine, with a waitable timer when the timer is set. The address of the completion routine is the fourth parameter to the SetWaitableTimer() function. The fifth parameter is a void pointer that you can use to pass arguments to the completion routine. The completion routine will be executed by the same thread that called SetWaitableTimer(). This thread must be in an alertable state to execute the completion routine. It accomplishes this by calling the SleepEx() function, which is an alertable function. Each thread has an APC queue. If there is an entry in the thread's APC queue at the time that one of the alertable functions is called, the thread is not put to sleep. Instead, the entry is removed from the APC queue and the completion routine is called. If no entry exists in the APC queue, the thread is suspended until the wait is satisfied. The wait can be satisfied by adding an entry to the APC queue, by a timeout, or by a handle becoming signaled. If the wait is satisfied by an entry in the APC queue, the thread is awakened and the completion routine is called. In this case, the return value of the function is WAIT_IO_COMPLETION.

After the completion routine is executed, the system checks for another entry in the APC queue to process. An alertable function will return only after all APC entries have been processed. Therefore, if entries are being added to the APC queue faster than they can be processed, it is possible that a call to an alertable function will never return. This is especially possible with waitable timers, if the period is shorter than the amount of time required to execute the completion routine. When you are using a waitable timer with an APC, the thread that sets the timer should not wait on the handle of the timer. By doing so, you would cause the thread to wake up as a result of the timer becoming signaled rather than as the result of an entry being added to the APC queue. As a result, the thread is no longer in an alertable state and the completion routine is not called. In the following code, the call to SleepEx() awakens the thread when an entry is added to the thread's APC queue after the timer is set to the signaled state.

Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.

 

Using Waitable Timers with an Asynchronous Procedure Call Program Example: Creating new Win32 empty console mode application project

 

Then, add the source file and give it a suitable name.

 

Using Waitable Timers with an Asynchronous Procedure Call Program Example: Adding new C++ source file to the Win32 C++ project

 

Next, add the following source code.

 

#include <windows.h>

#include <stdio.h>

 

#define _SECOND 10000000

 

// struct

typedef struct _MYDATA {

   TCHAR *szText;

   DWORD dwValue;

} MYDATA;

 

// callback function

void CALLBACK TimerAPCProc(

   LPVOID lpArg,               // Data value

   DWORD dwTimerLowValue,      // Timer low value

   DWORD dwTimerHighValue )    // Timer high value

 

{

   MYDATA *pMyData = (MYDATA *)lpArg;

   static int i= 1;

 

   wprintf(L"TimerAPCProc() #%d callback function\nMessage: %s\nValue: %d\n\n", i, pMyData->szText, pMyData->dwValue);

   i++;

   MessageBeep(0);

}

 

void wmain( void )

{

   HANDLE          hTimer;

   BOOL            bSuccess;

   __int64         qwDueTime;

   LARGE_INTEGER   liDueTime;

   MYDATA          MyData;

 

   MyData.szText = L"This is my data";

   MyData.dwValue = 100;

 

   hTimer = CreateWaitableTimer(

           NULL,              // Default security attributes

           FALSE,             // Create auto-reset timer

           L"MyTimer");       // Name of waitable timer

   if (hTimer != NULL)

   {

         wprintf(L"CreateWaitableTimer() should be fine...\n");

      __try

      {

         // Create an integer that will be used to signal the timer 5 seconds from now.

         qwDueTime = -5 * _SECOND;

         // Copy the relative time into a LARGE_INTEGER.

         liDueTime.LowPart  = (DWORD) ( qwDueTime & 0xFFFFFFFF );

         liDueTime.HighPart = (LONG)  ( qwDueTime >> 32 );

 

         bSuccess = SetWaitableTimer(

            hTimer,           // Handle to the timer object

            &liDueTime,       // When timer will become signaled

            2000,             // Periodic timer interval of 2 seconds

            TimerAPCProc,     // Completion routine

            &MyData,          // Argument to the completion routine

            FALSE );          // Do not restore a suspended system

 

         if (bSuccess)

         {

            wprintf(L"SetWaitableTimer() should be OK! Waiting to signal...\n\n");

            for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 )

            {

               SleepEx(

                  INFINITE,     // Wait forever

                  TRUE );       // Put thread in an alertable state

            }

         }

         else

         {

            wprintf(L"SetWaitableTimer() failed with error %d\n", GetLastError());

         }

      }

      __finally

      {

         if(CloseHandle(hTimer) != 0)

                   wprintf(L"hTimer handle was closed successfully!\n");

             else

                   wprintf(L"Failed to clode hTimer handle, error %d", GetLastError());

      }

   }

   else

   {

      wprintf(L"CreateWaitableTimer() failed with error %d\n", GetLastError());

   }

}

 

Build and run the project. The following screenshot is a sample output.

 

Using Waitable Timers with an Asynchronous Procedure Call Program Example: A sample console program output

 

 

 

 

< Thread Synchronization 25 | Thread Synchronization Programming | Win32 Programming | Thread Synchronization 27 >