X-Recipient: archive-cygwin AT delorie DOT com X-SWARE-Spam-Status: No, hits=-1.2 required=5.0 tests=AWL,BAYES_00,KHOP_THREADED,RCVD_IN_DNSWL_NONE,RCVD_IN_HOSTKARMA_YE,RCVD_IN_SORBS_WEB,SPF_HELO_PASS X-Spam-Check-By: sourceware.org From: "James Johnston" To: References: <4FB18C8F DOT 6030107 AT dbyron DOT com> In-Reply-To: <4FB18C8F.6030107@dbyron.com> Subject: RE: non-blocking reads of stdin in native child of cygwin process Date: Tue, 15 May 2012 16:34:39 -0000 Message-ID: <05d601cd32b8$a065f4a0$e131dde0$@motionview3d.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com 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 it here. 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. Comments below... > -----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 in > 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 thread. > 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 pipe.cc > (fhandler_pipe::create). > > I'm running with an elevated token but I still get access denied trying to use > that handle in CreateFileW. I'm not sure this is the right call to make but it > 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 FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple