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
Method 1
- Create a manual reset event;
- When the status is changed, first call SetEvent, then call ResetEvent immediately.
Method 2
- 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.