non-blocking reads of stdin in native child of cygwin process
Tue May 15 16:35:00 GMT 2012
In situations like this, it's useful to examine the .NET Framework and see
how the Microsoft implementation works. Generally, I find that the
implementations in the framework seem good and cover edge cases that I might
not normally think about.
In this case, the System.Console class has a method called
OpenStandardInput(). This method calls GetStdHandle and then returns a new
System.IO.__ConsoleStream, which derives from System.IO.Stream. Yes, it
supports asynchronous reads. But the usage of __ConsoleStream instead of
FileStream is your first clue that something special has to be done.
Normally you would use a FileStream to wrap a file handle, but they don't do
But, the returned Stream class still has a BeginRead method, you say? In
this case, __ConsoleStream only provides a Read method implementation; the
Read method is defined to be synchronous. __ConsoleStream falls back on the
base Stream class to implement the BeginRead/EndRead methods. And these
base implementations do nothing more than call the Read method in a
background thread in the thread pool.
So to summarize, the .NET Framework handles asynchronous reads from the
console by doing them in a background thread. They aren't doing overlapped
I/O, anything involving WaitForSingleObject, or anything like that. If they
wanted to do that, I would have expected them to use FileStream with
overlapped I/O or something, but they didn't because the standard handles
require special handling and perhaps this is a reason why.
I think synchronous reads/writes are probably the only way to safely work
with the standard handles - if Microsoft did this, then this conclusion
seems likely. You just don't know what kind of file handle you are being
given, so you have to fall back to a least common denominator. For example,
the handle was probably not opened with overlapped I/O.
> -----Original Message-----
> Sent: Monday, May 14, 2012 22:52
> Subject: non-blocking reads of stdin in native child of cygwin process
> I can do a non-blocking read from standard input in a native C program run
> a cmd window. It involves WaitForSingleObject on the return value from
> GetStdHandle(STD_INPUT_HANDLE). I could use some sample code for
> doing the equivalent thing when run from mintty/bash or cygwin.bat/bash.
> The story of what I've tried so far follows.
WaitForSingleObject says you can do it on console input, but not other file
handles like pipes, files, etc. Therefore I would think the behavior you
describe would be quite unsafe, as it breaks the assumption that you can
redirect standard input to something other than console input.
> When running a native C program from mintty/bash or cygwin.bat/bash
> GetStdHandle returns a pipe, not a console handle so
> WaitForMultipleObjects doesn't work...or at least not the same way. The
> docs don't say it works on named pipes and for me it returns
> WAIT_OBJECT_0 whether there's something to read or not.
Because in general, you're supposed to use overlapped I/O for this instead
of just blindly calling WaitForSingleObject on the pipe itself. The
overlapped functions will signal an event when they finish, which you can
wait on. But you can't wait on the pipe itself. Unfortunately, you can't
just assume your standard handles are overlapped, so I don't see a way
around this other than synchronously calling ReadFile in a background
> I've got my hands on the name of the pipe via GetNamedPipeInfo and
> GetFileInformationByHandleEx -- something like \\pipe\.\cygwin-
> dcb128a9c32912c6-pty1-from-master. This matches what I see in
> usr/src/cygwin-1.7.15/winsup/cygwin/tty.cc (tty::not_allocated) and
> I'm running with an elevated token but I still get access denied trying to
> that handle in CreateFileW. I'm not sure this is the right call to make
> works for me in other scenarios for non-blocking IO on named pipes (when
> followed by ReadFile w/ an OVERLAPPED structure, ERROR_IO_PENDING,
> WaitForMultipleObjects, GetOverlappedResult, etc.).
Now your program is diving deep into undocumented/undefined behavior.
GetStdHandle says nothing about how you can assume the return value is a
pipe, and how you can do all these things with the pipe you are doing. It
just says you get a file handle, which you have to use as-is. You shouldn't
try to do things like try to "re-open" the standard handles, or figure out
what object the handle is for (file name, pipe name, etc.).
In this case, Cygwin set up a pipe between two programs, just like any other
shell might do (e.g. Windows Command Prompt). Typically this will happen by
calling CreateNamedPipe to make a new named pipe, and then CreateFile to
connect to the pipe. That gives two file handles - the output of one
program can then be redirected into the input of another by appropriately
assigning those handles to the two children's standard output/input handles.
You can't just call CreateFile on the pipe again, because that would require
the other end (i.e. standard output of the other process) to have a second
file handle for listening on. It's nonsense.
Problem reports: http://cygwin.com/problems.html
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple
More information about the Cygwin