views:

165

answers:

1

Hi all,

I'm trying to use the Win32 API to make a sub-thread that reads from STD_INPUT_HANDLE and pushes the bytes it reads into a socket. Because I want to be able to shut down this thread safely when it's time to exit, I'm using ReadFileEx() and overlapped I/O instead of plain old blocking ReadFile(). The idea is that my thread will wait in WaitForSingleObject() instead of in ReadFile(), and when the main thread wants the slave thread to go away, it will signal on that object, the slave thread will wake up and exit, and then the main thread can continue its shutdown sequence.

My problem is this: despite the documentation saying that ReadFileEx() is asynchronous and thus won't ever block... my slave thread is still blocking inside ReadFileEx(). (I inserted printf's in the event loop to verify where it was blocking) Because of this, my main thread can't shut the slave thread down, and thus main program never exits.

Am I doing something wrong, or is ReadFileEx() meant to block when reading from stdin? If the latter, what is the solution to the thread shutdown problem? The slave thread's entry function is below for your persual...

[... in the main thread, before the slave thread is spawned...]
_stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
_wakeupSignal = CreateEvent(0, false, false, 0);
[...]

VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap)
{
   printf("CompletedReadRoutine dwErr=%li cbBytesRead=%li overlap=%p\n", dwErr, cbBytesRead, lpOverLap);
}

void StdinDataIO :: IOThreadEntry()
{
   char buf[4096];
   OVERLAPPED olap;
   bool keepGoing = true;
   bool overlappedReadPending = false;
   while(keepGoing)
   {
      if (overlappedReadPending)
      {
         DWORD waitResult = WaitForSingleObjectEx(_wakeupSignal, INFINITE, true);
         switch(waitResult)
         {
            case WAIT_IO_COMPLETION:
            {
               overlappedReadPending = false;
               DWORD numBytesRead;
               if ((GetOverlappedResult(_stdinHandle, &olap, &numBytesRead, true) == false)||(SendData(_slaveSocket, buf, numBytesRead, true) != numBytesRead)) keepGoing = false;
            }
            break;

            default:
               keepGoing = false;
            break;
         }
      }
      else
      {
         memset(&olap, 0, sizeof(olap));
         if (ReadFileEx(_stdinHandle, buf, sizeof(buf), &olap, CompletedReadRoutine)) overlappedReadPending = true;
                                                                                 else keepGoing = false;
      }
   }
   if (overlappedReadPending) CancelIo(_stdinHandle);
   _slaveSocket.Reset();  // this alerts the main thread that we are gone
}
+2  A: 

From the documentation of ReadFileEx describing what kind of file handle you can pass in:

This file handle must have been created with the FILE_FLAG_OVERLAPPED flag and must have the GENERIC_READ access right.

Otherwise, ReadFileEx will block when you call it with a handle that is not opened with FILE_FLAG_OVERLAPPED.

You can alternatively call WaitForSingleObject or some other synchronization function on the handle returned by GetStdHandle and call _kbhit or some other combination of console event handling if you want to make sure there's input available.

MSN
I was afraid it was something like that. I don't think the CONIN$ trick will do what I want, since I need to be able to read not just from the keyboard but also from the parent process or file redirect...
Jeremy Friesner