I am writing a class (PipeReader
) to handle named pipes on Windows. The class uses asynchronous IO to read from the pipe.
So far I have been using the class in a thread without an event loop and I had to wait on the IO to finish using SleepEx()
and it worked.
Now I have a second thread with an event loop and a second instance of the PipeReader
class, however the completion routine of the second instance is never called.
Call to CreateFile()
:
handle = CreateFile(fullServerName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
Call to ReadFileEx()
:
BOOL status = ReadFileEx(m_handle, ptr, readSize, &m_overlapped, &PipeReader::readFileCompleted);
Code for waiting in thread 1, that works:
while (SleepEx(msecs < 0 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) {
if (m_readFinished) // Set to true in the completion routine
return true;
msecs = // decrease msecs by the amount elapsed
if (!msecs)
break;
}
Code of the event loop in thread 2 where the completion routine is never called:
forever()
{
forever()
{
bool haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
if (haveMessage)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
break; //No message leave loop
}
}
HANDLE handles[1];
handles[0] = m_hwnd;
DWORD result = MsgWaitForMultipleObjectsEx(1, handles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE|MWMO_INPUTAVAILABLE);
}
If I replace the calle to MsgWaitForMultipleObjectsEx()
by :
WaitForSingleObjectEx(m_hwnd, INFINITE, TRUE);
it still doesn't work. However if I use SleepEx()
in the event loop it works.
This is because m_hwnd
is not a kernel handle. So WaitForSingleObjectEx
fails with WAIT_FAILED
and GetLastError
returns ERROR_INVALID_HANDLE
- as result the thread never enters alertable wait and APC is never executed. You need to have another message loop, for example:
for ( ; ; ) {
MSG msg;
switch(MsgWaitForMultipleObjectsEx(0, 0, INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE| MWMO_ALERTABLE))
{
case STATUS_WAIT_0 :
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
switch(msg.message) {
case WM_QUIT:
return;
default:
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
case WAIT_IO_COMPLETION :
// apc called
continue;
default: return ;
}