Topics waiting for events do not always trigger an event signal

I have an application in which multiple threads are waiting for the same event object for a signal. The problem that I see, apparently, is one of the types of race conditions in which sometimes the wait states of some threads ( WaitForMultipleObjects ) return as a result of the event state of the event and the wait state of the other threads do not seem to see the event signal, because they don't come back. These events were created using CreateEvent as objects of the reset event.

My application processes these events in such a way that when the event object is signaled, its owner stream is responsible for resetting the signal state of the event object, as shown in the following code fragment. Other threads waiting in the same event do not try to reset its signal state.

 switch ( dwObjectWaitState = ::WaitForMultipleObjects( i, pHandles, FALSE, INFINITE ) ) { case WAIT_OBJECT_0 + BAS_MESSAGE_READY_EVT_ID: ::ResetEvent( pHandles[BAS_MESSAGE_READY_EVT_ID] ); /* handles the event */ break; } 

In other words, the problem I am seeing is similar to what is described in the Remarks section for PulseEvent on the MSDN website :

If a PulseEvent call is made while the thread has been removed from the standby state, the thread will not be released, because PulseEvent releases only those threads that are currently waiting. Therefore, PulseEvent is unreliable and new applications should not be used. Use state variables instead.

If this is what happens, the only solution I see is for each thread to register its use of the given event object with this stream of the object owner, so that the owner stream can determine when the signal state of the event object is safely reset.

Is there a better way to do this? Thanks.

+4
source share
3 answers

Use a conditional variable, as described in PulseEvent. The only problem is that the natural conditional variable on the windows has been implemented since Vista, so an old system like XP does not. But you can emulate a conditional variable using some other synchronization objects ( http://www1.cse.wustl.edu/~schmidt/win32-cv-1.html ), but I think the easiest way is to use a conditional variable from boost libraries and its notify_all method to wake up all threads ( http://www.boost.org/doc/libs/1_41_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref )

Another possibility (but not very pretty) is to create one event for each thread, and when you have PulseEvent right now, you can call SetEvent for all of them. For this solution, it is likely that auto-reset events will work better.

+1
source

Yes, there is a better way:

[...] Use condition variables instead.

http://msdn.microsoft.com/en-us/library/ms682052(v=vs.85).aspx

Look for WakeAllConditionVariable specifically

+1
source

Why PulseEvent () is unreliable and what to do with it

The auto-reset event is king!

PulseEvent appeared only in Windows NT 4.0. It did not exist in the original Windows NT 3.1. In contrast, robust features like CreateEvent, SetEvent, and WaitForMultipleObjects existed from the very beginning of Windows NT, so consider using them.

The CreateEvent function has an argument bManualReset. If this parameter is TRUE, the function creates a reset event object that requires the use of the ResetEvent function to set the status of the event without signaling. This is not what you need. If this parameter is FALSE, the function creates an auto-reset event object, and the system automatically resets the event state to a non-signal after the release of one waiting thread.

These auto-reset events are very reliable and easy to use.

If you expect an auto-reset event object with WaitForMultipleObjects or WaitForSingleObject, it reliably resets the event when you exit these wait functions.

So, create events as follows:

 EventHandle := CreateEvent(nil, FALSE, FALSE, nil); 

Wait for an event from one thread and make a SetEvent from another thread. It is very simple and very reliable.

Never call ResetEvent (as it will automatically reset) or PulseEvent (since it is not reliable and deprecated). Even Microsoft has recognized that PulseEvent should not be used. See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684914(v=vs.85).aspx

This function is unreliable and should not be used, because only those threads that are in the waiting state at the time of PulseEvent call will be notified. If they are in any other state, they will not be notified, and you can never know exactly what the state of the stream is. Waiting for a thread in a synchronization object can be temporarily removed from the idle state by asynchronously invoking kernel mode procedures, and then returning to the idle state after the APC completes. If a PulseEvent call occurs during the time that the thread was removed from the idle state, the thread will not be released, because the PulseEvent only releases threads that are waiting at the time it is called.

You can learn more about asynchronous kernel mode calls in the following links:

We have never used PulseEvent in our applications. As for auto-reset events, we use them with Windows NT 3.51, and they work very well.

What to do if several threads are waiting for one object

Unfortunately, your case is a little more complicated. You have several threads waiting for an event, and you need to make sure that all threads have actually received a notification. There is no other reliable way than creating your own event for each thread.

You wrote theat "the only solution I see is for each thread to register its use of this event object with this thread of the object owner." It is right.

You also wrote that "the owner thread can determine when the signal state of the event object is safely reset" - this is impractical and unsafe. It is best to use auto-reset events, so they will automatically reset.

So, you will need to have as many events as there are threads. In addition, you will need to save the list of registered streams. Therefore, to notify all threads, you will need to do a SetEvent in a loop for all event descriptors. This is a very fast, reliable and cheap way. Events are much cheaper than streams. Thus, the number of threads is a problem, not the number of events. There are practically no restrictions on kernel objects - the processor limit for kernel descriptors is 2 ^ 24.

0
source

All Articles