Question about Win32 ##Event## synchronization object
First let me introduce the application scene:
I have a service application which is spying the status of something, while also have multiple applications waiting for the status to change. Once the status is changed, each application will read the status value (via a named FileMap object) and execute corresponding actions, and then wait for the status to be changed again.
So I used a named Event object to do the synchronization work. All applications are waiting for this event to be signaled, and the service application will set this event to be signaled when that status is changed.
I need to guarantee that when the status is changed, each waiting application will be released and is released only once!
I have tried with these 2 methods
- Create a manual reset event;
- When the status is changed, first call SetEvent, then call ResetEvent immediately.
- Create a manual reset event;
- When the status is changed, call PulseEvent.
Both methods seem work well during the test. But I think neither of them is reliable because:
For ## Method 1 ##, maybe some of the waiting threads won't get chance to be executed before the ResetEvent function is called.
For ## Method 2 ##, Microsoft has claimed PulseEvent is unreliable and should not be used.
Is there any workable solution for this case? Any advice is welcome.
There is no way this can be safely implemented with O(1) synchronization primitives.
To inform N applications about new status change use N events. Service should set them all, and every application should reset its corresponding event upon handling the current status change.
To wait until all applications handle new status change service can use another set of N events and WaitForMultipleObjects with bWaitAll==TRUE. Each application should set its corresponding event.
So, service does a loop: observe status change, write to shared memory, set all A events, wait on all B events, reset all B events, continue loop. Every application does a loop: wait on its A(i) event, reset A(i), handle status change, set B(i), continue loop.
Both A and B events can be of auto-resetting type. Then you don't have to reset anything.
If you feel green and don't want to waste resources you can use some sort of reversed semaphore instead of B set of events. This can be implemented with one shared counter synchronized by mutex and one event (B') to notify the service. Instead of waiting on whole B set the service waits on B' and when wait is over set the counter to N. Instead of setting its B(i) event each application should decrement the counter, and if counter drops to zero that last application should set only B'.
You can't bypass the A set of events. The problem is not in setting the A event but in resetting. By resetting its A(i) event the application will not miss another status change. And it is not helpful to use semaphore here.
Note that this solution does not take into account possible crash of an application. If that happens the service will wait forever for non-existing application to respond.
One problem with method #1 (even if it's unlikely, timing-wise) is that you can't make the guarantee that an application "is released only once" - it would be possible for a thread that's waiting on the event to be released when you call SetEvent(),do it's work, then try to wait on the event again before your thread gets around to calling ResetEvent(). I ResetEvent() hasn't occurred yet, the thread will not block (essentially being released more than once).
You may need a second event object that's in the non-signaled state for threads to wait on during the SetEvent()/ResetEvent() sequence that occurs on your 'real' event object. Essentially, each consumer thread needs to wait for both events sequentially, with only one of the 2 events signaled at any moment. Normally only one of the events would be in the reset state where threads would block, but there will be a brief period during the SetEvent()/ResetEvent() sequence of the 'main' event object where both events would be a reset state.
However, one important thing or you to consider is the requirement that "each waiting application will be released". How will your overall application deal with a thread that was just about to block on the status changed event, but hadn't quite gotten there (so it's not actually an application waiting to be released yet)? How is that different than if SetEvent() doesn't guarantee that all threads blocked on the event will be run when the event is signaled? You have a race condition of sorts either way.