Problem in words:
For my application, I have a class that reads from a serial port. It uses Windows primitives for COM port handling and had a thread for asynchronous reading. I'm trying to convert this away from Windows primitives using Boost libraries such as Boost.Asio and Boost.Thread.
In the Windows port, my IO thread had several MFC CEvent variables, each of which represented a message: Read requested, Write requested, Read completed, Write completed, IO Cancelled. These were waited on with WaitForMultipleObjects.
The problem I have is that Boost.Thread seems to have analogues for neither CEvent nor WaitForMultipleObjects. The closest I have come is by discarding these and replacing the events with a set of booleans, and then using a condition_variable, which has its notify_all() function called whenever a boolean changes.
However, boost::condition_variable differs in one critical way from CEvent: if a CEvent is signalled while it is not being waited on, then the next wait on it immediately succeeds. With boost::condition_variable, any notify function is ignored if it is not waiting.
This means that there is always a gap between checking for the flags and waiting for the condition_variable in which a notification can be lost. This causes the thread to hang.
Does anybody know of a solution to this problem?
Problem in code:
// Old IO Thread
CEvent msg_cancel;
CEvent msg_read_req;
CEvent msg_write_req;
CEvent msg_read_comp;
CEvent msg_write_comp;
CEvent events[] = {
msg_cancel,
msg_read_req,
msg_write_req,
msg_read_comp,
msg_write_comp
};
bool cancel = false;
while (!cancel)
{
switch(WaitForMultipleObjects(5, events, false, INFINITE))
{
case WAIT_OBJECT_0 :
// msg_cancel
cancel = true;
break;
...
}
}
How to emulate that in Boost.Thread?
As you said, to resemble a windows style event you need a condition-variable plus a boolean flag. Of course you can combine several boolean flags into one if it satisfies your needs.
However, the problem you mentioned (condition variables never get an active
state where wait will immediately return) is usually solved that way:
condition-variable
mutex
main-thread:
lock(mutex) { start condition-signaling-thread }
while(some predicate) {
condition-variable.wait(mutex)
do-stuff
}
condition-signaling-thread:
loop:
lock(mutex) {
do-whatever
}
condition-variable.notify();
By having the second thread to wait until the mutex is unlocked by the thread which will handle the condition you can ensure that each condition is handled. (Note: In Java the notify() method has to be called within the lock, which, depending on implementation details, could result in worse performance if done in C++, but ensures that the programmer has at least once thought about how to synchronize the firing of the condition with the receiver).
The reason why boost.thread does not provide windows-style events (and posix-semaphores, btw) is that those primitives make it quite easy to screw up. If you do not plan to port your application to another platform, adapting your application to this different style may not be worth it.