Event Object Notification (Windows Embedded CE 6.0)
1/6/2010
Windows Embedded CE uses event objects to complete the following tasks:
- Notify a thread when to perform its task.
- Indicate that an event has occurred.
For example, a thread that writes to a buffer resets the event object to a signaled state when it finishes 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);
The following table describes the parameters.
Parameter | Description |
---|---|
lpName |
Specifies the name of the event object or to leave the object unnamed. |
bManualReset |
Specifies whether the event object resets itself from a signaled state to a nonsignaled state, or whether it requires a manual reset. |
bInitialState |
Specifies 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 CreateEvent created or opened an object, call the GetLastError function immediately after calling CreateEvent.
If GetLastError returns ERROR_ALREADY_EXISTS, the call opened an event. In other words, if the name specified in a call to CreateEvent matches the name of an event object, the function returns the handle of the 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 would get 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 resets the event. For manual events, PulseEvent unblocks all threads that are waiting for the event. For automatic events, PulseEvent unblocks only one thread.
If an event object can reset itself, use SetEvent to signal the reset. The event object is then reset to the nonsignaled state when one thread is unblocked after waiting for that event.
To manually reset an event, use the ResetEvent function.
To close an event object, call CloseHandle. If the event object is named, Windows Embedded CE maintains a reference 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 a multiple-object wait function to wait for the state of an event object 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 a nonsignaled state when it is writing to the buffer, and it then resets the object to a signaled state when it finishes 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 a signaled state when it is not reading from the buffer.
// Use event objects to protect 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 Embedded CE
0, // Must be 0 in Windows Embedded 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.
}
}
}
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 a nonsignaled state. 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 current read operations. When the loop of calls to WaitForSingleObject is complete, the master thread can safely write to the buffer.
After it finishes writing, the master thread sets hGlobalWriteEvent and all reader-thread events to a signaled state, which enables the reader threads to resume their read operations.
The example does not use the WaitForMultipleObjects function because in Windows Embedded CE the fWaitAll flag must be set to FALSE. For more information about wait functions, see Wait Functions.
// Use WaitForSingleObject to stop reads during write.
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 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;
}
}
}
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 complete, the reader thread auto-reset event is reset to a nonsignaled state. This blocks the master thread from writing to the buffer until the reader thread uses the SetEvent function to reset the event state to a signaled state.
// Block master thread from writing to the buffer until ready:
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.
}
}
}
}
The following code example defines WriteToBuffer:
static _inline int
WriteToBuffer(PBUFFER pBuffer, PVOID pvBuf, DWORD cbBuf)
The following code example shows how to use WriteToBuffer:
static _inline int
WriteALetter(PBUFFER pBuffer, P_LTR pLetter)
{
return (WriteToBuffer(pBuffer, pLetter, sizeof(P_LTR)));
}