I have a Delphi application running under Windows that needs to block until another thread has delivered data. In other words, I need to wait on a synchronization object that from a particular thread I can put into the non-signaled state as if another thread held ownership of the object, so that when I call WaitFor() the thread blocks until one of the other threads unblocks it (there are more than one potential thread that can unblock and it is not known in advance which one). Note, all the threads have access to a shared data area so access to a common synchronization object is not a problem.
I thought Events were the right choice until I read this troubling blog post by Raymond Chen on the the problems with PulseEvent():
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/05/346888.aspx
What is the correct synchronization technique to achieve this result? If you have a link to a Delphi or C/C++ example that would be great.
Events are perfectly fine, just don’t use PulseEvent. Not only did Microsoft hopelessly botch the implementation, but its use is complicated by inherent race conditions. If the event occurs just before the thread blocks on the event, the event will be lost. Instead, use manual-reset events.
To block:
B1) Set the event to blocking.
B2) Check to make sure the event hasn’t occurred yet and be sure you want to block on it. If necessary, put the event in a shared data structure where threads waking us will notice it.
B3) Block on the event.
To wake:
W1) Update shared data to reflect the fact that the event has occurred.
W2) Unblock the event. If necessary, traverse the shared data structure and unblock all events associated with whatever just happened.
Here, there’s no race condition. If the event occurs after B1 or B2 but before B3, the block in B3 won’t actually block because W2 will unblock it. If the event has already occurred before W1, the thread will never block because B2 will see the effects of W1 and we’ll never get to B3.
In most cases, operations B2 and W2 need to be done while holding a lock shared by all threads that handle events of this type.