Yesterday, I wrote about Windows CE Events in Windows CE: Synchronization Objects – Events. Today I thought I would write some sample code to demonstrate the behavior differences between event types (manual and auto reset) and the functions for signaling the event (PulseEvent() and SetEvent()).
I started by defining some macros to make the code easier to read. The call to CreateEvent() takes some parameters that are simply TRUE or FALSE. When you are writing the code and reviewing the documentation these seem obvious, but later the code is not so easy to read. Then I created a typedef for a structure that I will pass into the thread. The reason for this is that it will allow me to write a single function but have it identify itself in my debug output because the data will be unique.
The thread is rather simple. It waits for the event to be signaled and then outputs information about why the call to WaitForSingleObject() returned. Note that the thread doesn’t even have a loop, once the wait returns, the thread returns and exits.
#include <windows.h>
 
#define AUTORESET FALSE
#define MANUALRESET     TRUE
#define START_SIGNALED TRUE
#define START_NONSIGNALED     FALSE
 
typedef struct __ThreadData__
{
      HANDLE Event;
      TCHAR *ThreadID;
      HANDLE hThread;
      DWORD Timeout;
} ThreadData;
 
static DWORD WINAPI EventThread( LPVOID lpParam)
{
      DWORD WFSO_Ret;
      ThreadData *pData = (ThreadData *)lpParam;
     
      WFSO_Ret = WaitForSingleObject( pData->Event, pData->Timeout);
 
      if( WFSO_Ret == WAIT_TIMEOUT )
            RETAILMSG( 1, (TEXT("\t\t%s timeout\n"), pData->ThreadID ));
      else if ( WFSO_Ret == WAIT_OBJECT_0 )
            RETAILMSG( 1, (TEXT("\t\t%s event signaled\n"), pData->ThreadID ));
      else
            RETAILMSG( 1, (TEXT("\t\t%s error %d\n"), pData->ThreadID, GetLastError() ));
      return TRUE;
}
Then the main function exersises the event by creating the event, starting the threads and signaling the event. It runs through the following tests first for auto reset events, and then for manual reset events:
1.       Simple test of SetEvent() with one thread running and waiting for the event
2.       Test that the thread times out waiting for an non-signaled event
3.       Test of SetEvent() with two threads waiting for the event
4.       Test of PulseEvent() with two threads waiting for the event
You will notice in the code that I added calls to Sleep() after starting the threads and before calling SetEvent() or PulseEvent(). This is necessary to ensure that the threads are waiting for the event before signaling the event.   See my comments at the end about why I set the Sleep() timeouts the way that I did.
The code looks like this:
int wmain(int argc, _TCHAR* argv[])
{
      ThreadData ThreadData1;
      ThreadData ThreadData2;
      HANDLE Event;
      RETAILMSG( 1, (TEXT("Starting Thread Test\n")));
 
      Event = CreateEvent(NULL, AUTORESET, START_NONSIGNALED, NULL );
 
      ThreadData1.Event = Event;
      ThreadData1.ThreadID = TEXT("Thread1");
      ThreadData1.Timeout = 10000;
 
      ThreadData2.Event = Event;
      ThreadData2.ThreadID = TEXT("Thread2");
      ThreadData2.Timeout = 10000;
 
      RETAILMSG( 1, (TEXT("Start Auto Reset Tests\n")));
      RETAILMSG( 1, (TEXT("\tTesting one thread\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread,&ThreadData1, 0, NULL );
      Sleep(500);
      SetEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
 
      RETAILMSG( 1, (TEXT("\tTesting one thread with timeout\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread, &ThreadData1, 0, NULL );
      Sleep( 1500 );
      SetEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
 
      RETAILMSG( 1, (TEXT("\tTesting two threads with SetEvent\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread,&ThreadData1, 0, NULL );
      ThreadData2.hThread = CreateThread(NULL, 0, EventThread,&ThreadData2, 0, NULL );
      Sleep(5000);
      SetEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
      WaitForSingleObject( ThreadData2.hThread, INFINITE );
      CloseHandle( ThreadData2.hThread );
 
      RETAILMSG( 1, (TEXT("\tTesting two threads with PulseEvent\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread,&ThreadData1, 0, NULL );
      ThreadData2.hThread = CreateThread(NULL, 0, EventThread,&ThreadData2, 0, NULL );
      Sleep(5000);
      PulseEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
      WaitForSingleObject( ThreadData2.hThread, INFINITE );
      CloseHandle( ThreadData2.hThread );
      CloseHandle(Event);
 
 
 
      RETAILMSG( 1, (TEXT("Start Manual Reset Tests\n")));
 
      Event = CreateEvent(NULL, MANUALRESET, START_NONSIGNALED, NULL );
 
      ThreadData1.Event = Event;
      ThreadData2.Event = Event;
 
 
      RETAILMSG( 1, (TEXT("\tTesting one thread\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread,&ThreadData1, 0, NULL );
      Sleep(500);
      SetEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
      ResetEvent(Event);
 
      RETAILMSG( 1, (TEXT("\tTesting one thread with timeout\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread, &ThreadData1, 0, NULL );
      Sleep( 1500 );
      SetEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
      ResetEvent(Event);
 
      RETAILMSG( 1, (TEXT("\tTesting two threads with SetEvent\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread,&ThreadData1, 0, NULL );
      ThreadData2.hThread = CreateThread(NULL, 0, EventThread,&ThreadData2, 0, NULL );
      Sleep(500);
      SetEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
      WaitForSingleObject( ThreadData2.hThread, INFINITE );
      CloseHandle( ThreadData2.hThread );
      ResetEvent(Event);
 
      RETAILMSG( 1, (TEXT("\tTesting two threads with PulseEvent\n")));
      ThreadData1.hThread = CreateThread(NULL, 0, EventThread,&ThreadData1, 0, NULL );
      ThreadData2.hThread = CreateThread(NULL, 0, EventThread,&ThreadData2, 0, NULL );
      Sleep(500);
      PulseEvent( Event );
      WaitForSingleObject( ThreadData1.hThread, INFINITE );
      CloseHandle( ThreadData1.hThread );
      WaitForSingleObject( ThreadData2.hThread, INFINITE );
      CloseHandle( ThreadData2.hThread );
      ResetEvent(Event);
      CloseHandle(Event);
 
      return 0;
}
The debug output from this is:
Starting Thread Test
Start Auto Reset Tests
                Testing one thread
                                Thread1 event signaled
                Testing one thread with timeout
                                Thread1 event signaled
                Testing two threads with SetEvent
                                Thread2 event signaled
                                Thread1 timeout
                Testing two threads with PulseEvent
                                Thread2 event signaled
                                Thread1 timeout
Start Manual Reset Tests
                Testing one thread
                                Thread1 event signaled
                Testing one thread with timeout
                                Thread1 event signaled
                Testing two threads with SetEvent
                                Thread2 event signaled
                                Thread1 event signaled
                Testing two threads with PulseEvent
                                Thread2 event signaled
                                Thread1 event signaled
I have to admit that I tested this using Visual Studio 2005 and the Pocket PC 2003 emulator. There appear to be some side effects of doing so, primarily that nothing worked as it should have. I guess that is why I don’t use the emulator much for testing, but I didn’t have a device handy. So you might notice some odd timeouts, but they seem to have been necessary on the debugger to allow threads to run and get to the point were they waited for the event.
 
Copyright © 2009 – Bruce Eitman
All Rights Reserved