Reputation: 1100
I would like to send a "signal/notify" from one process to multiple other processes, however there doesn't seem to be a direct solution to what I would like do using Win API:
A condition variable cannot be shared between processes.
An event object seems to either signal from one thread to another (using auto-reset event) or to multiple (Manual-reset event), but then I need to manually turn off the signal. It might then be difficult to know when all the "observers" have acknowledge the signal.
I've also used Boost's IPC synchronization, but I don't really like the implementation since the mutexes doesn't have an "abandon"-state as the windows counter-part (if a software crash using the mutex it is difficult to recover from it).
Any suggestions? Basically I would like to use a condition variable, but it should work across multiple processes. Thank you!
Clarification:
Basically this is what I'm doing: I'm updating a shared memory from one process (let's call it the controller) which need to notify multiple processes (the observers) that the shared memory has been updated.
What I did when I used boost was to use a shared (IPC) mutex and a conditional variable. The condition variable would wait in a separate thread for each observer until it has been notified by the controller to read from the shared memory. After an observer has read from the shared memory it would go back to wait for another notification from the controller. How can I do this using Win API?
Upvotes: 3
Views: 2087
Reputation: 36308
The simplest approach is to broadcast a window message, using RegisterWindowMessage and PostMessage. Each observer would have a separate thread that creates an invisible window and runs a message processing loop, notifying the main thread as appropriate when signaled by the controller.
The main limitation with this approach is that the observers and controller would all have to be running on the same desktop and in the same user context and privilege level.
If for whatever reason you want to avoid using the GUI, and stick to kernel synchronization functions, I think you would need multiple event objects.
One simple way to do this would be to put a counter in shared memory and use that to determine the event object name. We'll also use a single mutex to coordinate access, and start out with a single manual-reset event object, initially unset.
When the controller wants to signal the observers, it would:
Claim the mutex.
Signal and then close the current event object. (Note that the object will continue to exist until the last observer has closed it.)
Increment the counter.
Create a new manual-reset event object, initially unset, to be used for the next signal.
Release the mutex.
When an observer wants to wait for a signal:
Claim the mutex.
Open the event object indicated by the current value of the counter.
Release the mutex.
Wait for the event object to be signaled, then close it.
(You could make this a bit more efficient by eliminating the mutex and using interlocked operations instead. You'd just need to be a bit more careful about the order in which you do things, and handle edge cases like when the event object is deleted just before you try to open it.)
Yet another option would be to have one auto-reset event object per observer. But then the observers would need to register with the controller, and the controller would have to detect and clean up after observers that terminated unexpectedly. This wouldn't be particularly difficult, but a full description would be tedious and I don't think you'd gain anything much over the previous suggestion.
A variant of this would use named pipes instead. The advantage of named pipes is that you can take care of registering the observers and notifying them with the same API, and that (if you've done everything right) you automatically get notified if the other end dies unexpectedly. The disadvantage is that the API has a fairly steep learning curve.
There are of course also solutions that use a fixed number of kernel objects, some of which are described here. However, it isn't obvious to me how to adapt these to the situation where one of the observers might exit unexpectedly.
For the record, although I've attempted to answer the question as asked, in your particular situation I suspect you will need to prevent the controller from modifying the shared memory while any of the observers are still reading it, and I think that will have a significant effect on what approaches are feasible. This isn't trivial; for one thing, you'll need to think about what to do with observers that take an unexpected amount of time while reading from the shared memory - should the controller eventually time out? If so, should it kill the observer in question, or let it run but ignore it, or ...?
(Since I don't know exactly what this modified scenario would look like, I haven't given it a great deal of thought, but my first instinct would be to go with the named pipes.)
Upvotes: 1