Event Objects

Windows CE uses event objects to notify a thread when to perform its task or to indicate that a particular event has occurred. For example, a thread that writes to a buffer resets the event object to signaled when it has finished writing. By using an event object to notify the thread that its task is finished, the thread can immediately start performing other tasks.

To create an event object, call the CreateEvent function. The following code example shows the CreateEvent function prototype.

HANDLE CreateEvent (NULL, BOOL bManualReset, BOOL bInitialState,
                    LPTSTR lpName);

Use the lpName parameter to name the event object or to leave the object unnamed. The bManualReset parameter enables you to specify whether the event object automatically resets itself from a signaled state to a nonsignaled state or whether it will require a manual reset. Use the bInitialState parameter to specify whether the event object is created in the signaled or nonsignaled state.

Named events can be shared with other processes. Threads in other processes can open a handle to an existing event object by specifying its name in a call to CreateEvent. All named synchronization objects are stored in the same queue. To determine whether a CreateEvent function created a new object or opened an existing object, you can call the GetLastError function immediately after calling CreateEvent. If GetLastError returns ERROR_ALREADY_EXISTS, the call opened an existing event. In other words, if the name specified in a call to CreateEvent matches the name of an existing event object, the function returns the handle of the existing object. When you use this technique for event objects, none of the calling processes should request immediate ownership of the event, because it is uncertain which process actually gets ownership.

To signal an event, use the SetEvent or PulseEvent function. SetEvent does not automatically reset the event object to a nonsignaled state. PulseEvent signals the event, and then it resets the event. For manual events, PulseEvent unblocks all threads waiting on the event. For automatic events, PulseEvent unblocks only one thread.

If an event object can reset itself, you need only use SetEvent to signal. The event object is then automatically reset to the nonsignaled state when one thread is unblocked after waiting on that event. To manually reset an event, use the ResetEvent function.

To close an event object, call the CloseHandle function. If the event object is named, Windows CE maintains a use count on the object, and you must make one call to CloseHandle for each call to CreateEvent.

A single thread can specify different event objects in several simultaneous overlapped operations. If this is the case, use one of the multiple-object wait functions to wait for the state of any one of the event objects to be signaled. You can also use event objects in a number of situations to notify a waiting thread of the occurrence of an event. For example, communications devices use an event object to signal their completion.

The following code example shows an application that uses event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. The master thread uses the CreateEvent function to create a manual-reset event object. The master thread sets the event object to nonsignaled when it is writing to the buffer, and it then resets the object to signaled when it has finished writing. The master thread then creates several reader threads and an auto-reset event object for each thread. Each reader thread sets its event object to signaled when it is not reading from the buffer.

VOID ReadThreadFunction (LPVOID lpParam);  // Forward declaration

#define NUMTHREADS 4

HANDLE  hGlobalWriteEvent;
HANDLE  hReadEvents[NUMTHREADS];

void CreateEventsAndThreads (void)
{
  HANDLE hThread;   // Newlycreated thread handle
  DWORD dwThreadID; // Newlycreated thread ID value
  int i;            // For-loop counter

  hGlobalWriteEvent = CreateEvent (NULL,  // No security attributes
                                   TRUE,  // Manual-reset event
                                   TRUE,  // Initial state is signaled
                                   TEXT("WriteEvent"));
                                          // Object name
  if (hGlobalWriteEvent == NULL)
  {
    // Your code to deal with the error goes here.
  }

  for (i = 0; i < NUMTHREADS; i++)
  {
    hReadEvents[i] = CreateEvent (NULL,   // No security attributes
                                  FALSE,  // Auto-reset event
                                  TRUE,   // Initial state is signaled
                                  NULL);  // Object not named
    if (hReadEvents[i] == NULL)
    {
      // Your code to deal with the error goes here.
    }

    hThread = CreateThread (
                  NULL,               // No security attributes 
                                      // in Windows CE
                  0,                  // Must be 0 under Windows CE
                  (LPTHREAD_START_ROUTINE) ReadThreadFunction
                  &hReadEvents[i],    // Pass new thread's event handle
                  0,                  // Thread runs immediately
                  &dwThreadID);       // Returned ID value (ignored)

    if (hThread == NULL) 
    {
      // Your code to deal with the error goes here.
    }
  }
} // End of CreateEventsAndThreads example code

In the following code example, before the master thread writes to the shared buffer, it uses ResetEvent to set the state of hGlobalWriteEvent, which is an application-defined global variable, to nonsignaled. This blocks the reader threads from starting a read operation. The master thread then uses the WaitForSingleObject function to wait for all reader threads to finish any current read operations. When the loop of calls to WaitForSingleObject is completed, the master thread can safely write to the buffer. After it has finished writing, it sets hGlobalWriteEvent and all the reader-thread events to signaled, which enables the reader threads to resume their read operations. The example does not use the WaitForMultipleObjects function because in Windows CE the fWaitAll flag must be set to FALSE. For more information about wait functions, see "Wait Functions."

int WriteToBuffer (void)
{
  DWORD dwWaitResult;
  int i;

  // Block all read threads from starting any new operations.
  if (!ResetEvent (hGlobalWriteEvent))
  {
    // Your code to deal with the error goes here.
  }

  // Wait for all of the read events to be signaled (threads done).
  for (i = 0; i < NUMTHREADS; i++)
  {
    dwWaitResult = WaitForSingleObject (hReadEvents[i], INFINITE);
    if (WAIT_OBJECT_0 != dwWaitResult)
    {
      // Your code to deal with the error goes here.
    }
  }

  // Now it is okay to write to the shared buffer, so that code
  // goes here.


  // Put all the events back to signaled (so read threads can run).
  for(i = 0; i < NUMTHREADS; i++)
  {
    if (!SetEvent (hReadEvents[i]))
    {
      // Your code to deal with the error goes here.
    }
  }

  if (!SetEvent (hGlobalWriteEvent))
  {
    // Your code to deal with the error goes here.
  }

  return 1;
} // End of WriteToBuffer example code

In the following code example, before starting a read operation, each reader thread uses the WaitForSingleObject function to wait for the application-defined global variable, hGlobalWriteEvent, and then uses it again to wait for its own read event to be signaled. When the loop of calls to WaitForSingleObject is completed, the reader thread's auto-reset event has been reset to nonsignaled. This blocks the master thread from writing to the buffer until the reader thread uses the SetEvent function to set the event's state back to signaled.

VOID ReadThreadFunction (LPVOID lpParam)
{
  DWORD dwWaitResult;
  HANDLE hEvent;
  BOOL bStayInLoop;

  hEvent = * (HANDLE *) lpParam;   // This thread's read event

  bStayInLoop = TRUE;

  while (bStayInLoop)
  {
    // First, wait for the master thread's event.
    WaitForSingleObject (hGlobalWriteEvent, INFINITE);
    if (WAIT_OBJECT_0 != dwWaitResult)
    {
      // Your code to deal with the error goes here.
    }

    // Now, wait for this thread's event object.
    WaitForSingleObject (hEvent, INFINITE);
    if (WAIT_OBJECT_0 != dwWaitResult)
    {
      // Your code to deal with the error goes here.
    }

    // Now it is okay to read the shared buffer -- the code to do
    // that goes here.  When it is time for the thread to quit, set
    // the bStayInLoop variable to FALSE.

    // Now, signal that this thread is done with the buffer.
    if (!SetEvent (hEvent))
    {
      // Your code to deal with the error goes here.
    }
  }
} // End of ReadThreadFunction example code