X-Recipient: archive-cygwin AT delorie DOT com X-SWARE-Spam-Status: No, hits=-1.7 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_NONE,RCVD_IN_HOSTKARMA_YE X-Spam-Check-By: sourceware.org X-Mail-Handler: MailHop Outbound by DynDNS X-Report-Abuse-To: abuse AT dyndns DOT com (see http://www.dyndns.com/services/mailhop/outbound_abuse.html for abuse reporting information) X-MHO-User: U2FsdGVkX18m6NodZmpqpq+7T3gyx/an Date: Thu, 26 Apr 2012 20:17:18 -0400 From: Christopher Faylor To: cygwin AT cygwin DOT com Subject: Re: Cygwin passes through null writes to other software when redirecting standard input/output (i.e. piping) Message-ID: <20120427001718.GA10533@ednor.casa.cgf.cx> Reply-To: cygwin AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com References: <020501cd23f2$20f07620$62d16260$@motionview3d.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <020501cd23f2$20f07620$62d16260$@motionview3d.com> User-Agent: Mutt/1.5.20 (2009-06-14) Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: 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 On Thu, Apr 26, 2012 at 09:18:27PM -0000, James Johnston wrote: >========== SenderC.c: Sender program in Visual C++ 2008 ========== > >#include >int main() { > char * test = "Hello world!\n"; > DWORD written; > HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); /* Get standard output >file handle */ > Sleep(1000); /* wait for pipes to set up and for receiving app to >block on first ReadFile call */ > WriteFile(h, test, 0, &written, NULL); /* do null write */ > Sleep(1000); /* wait for receiving app to get the null write */ > WriteFile(h, test, lstrlenA(test), &written, NULL); /* print hello >message */ > return 0; >} > >========== ReceiverCPP.cpp: Receiver program in Visual C++ 2008 that >demonstrates bug in VC++ 2008 runtime / STL ========== > >#include >#include >using namespace std; >int main() { > /* you have to use a retry loop, for exact same reasons given for C# >receiver program: > there is no way to tell difference between end-of-file and null >write. */ > for (int i = 0; i < 10; i++) { > string str; > /* BUG: cin will indicate end-of-file on a null write. */ > getline(cin, str); > if (cin.eof()) { > cout << "Got end-of-file" << endl; > } else { > cout << "Got line " << str << endl; > } > cin.clear(); /* future getline calls will always immediately >fail without attempting another read unless we clear EOF/fail flags */ > } > return 0; >} > >========== Test results ========== > >The test programs are designed so that they can be run in any combination >from the command prompt. The output from a sender is piped to the input of >a receiver. Each combination delivers identical output to the other >combinations: > > * ./SenderCS | ./ReceiverCS > * ./SenderCS | ./ReceiverCPP > * ./SenderC | ./ReceiverCS > * ./SenderC | ./ReceiverCPP > >Output from Cygwin will always be: > >Got end-of-file >Got line Hello world! >Got end-of-file >Got end-of-file > Nope, it won't always be that because I get what's expected. I built the C++ files using mingw g++. Although I actually expected the reader to honor the null byte, it did not. Perhaps you are using a different version of Windows than I am or a different runtime. What you are seeing may be because Cygwin was changed to use message-type pipes a couple of revisions ago. This is not going to change. The change was adopted to fix a problem with Cygwin programs and those are obviously our #1 priority. >This is wrong, because the program received end-of-file before it was >actually at the end of the input stream, due to the bug in its runtime's >handling of return values from ReadFile API. I did not do any tests using >standard error, but I assume Cygwin redirects standard error in the same way >it redirects standard output, in which case it would have the same problem. > >[moved] > >I think a workable fix would be for Cygwin not to pass through null writes >it receives on an output/error pipe. For example, somewhere in Cygwin I >assume there is a loop that calls ReadFile to read the redirected standard >output from the first program, and then calls WriteFile to send this output >to the second program's standard input. If the call to WriteFile was >skipped if it would write zero bytes (i.e. so Cygwin doesn't do null writes >itself), I think it would fix the problem and work around all these buggy >runtimes. There's no way that Cygwin could know to "skip" a call to WriteFile(). Cygwin doesn't interpose itself in the middle of a pipe. That would be truly disastrous. If it somehow looked at every pipe read/write rather than just allowing I/O to flow from one end to the other, the mailing list would be even more filled with people complaining that Cygwin is slow. -- 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