Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 18 Aug 2014 11:26:40 +1000 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Pedro Giffuni <pfg@freebsd.org>
Cc:        svn-src-stable@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org, Andrey Chernov <ache@freebsd.org>, svn-src-stable-10@freebsd.org
Subject:   Re: svn commit: r270035 - stable/10/lib/libc/stdio
Message-ID:  <20140818102031.C948@besplex.bde.org>
In-Reply-To: <53F0FE68.6080501@freebsd.org>
References:  <201408160129.s7G1TojV024013@svn.freebsd.org> <53F0F263.7040202@freebsd.org> <53F0FE68.6080501@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 17 Aug 2014, Pedro Giffuni wrote:

>
> On 08/17/14 13:20, Andrey Chernov wrote:
>> On 16.08.2014 5:29, Pedro F. Giffuni wrote:
>>> Author: pfg
>>> Date: Sat Aug 16 01:29:49 2014
>>> New Revision: 270035
>>> URL: http://svnweb.freebsd.org/changeset/base/270035
>>> 
>>> Log:
>>>    MFC	r268924:
>>>    Update fflush(3) to return success on a read-only stream.
>>>       This is done for compliance with SUSv3. The changes cause
>>>    no secondary effects in the gnulib tests (we pass them).
>> ...
>>> @@ -122,6 +123,12 @@ __sflush(FILE *fp)
>>>   	for (; n > 0; n -= t, p += t) {
>>>   		t = _swrite(fp, (char *)p, n);
>>>   		if (t <= 0) {
>>> +			/* Reset _p and _w. */
>>> +			if (p > fp->_p)	/* Some was written. */
>>> +				memmove(fp->_p, p, n);
>>> +			fp->_p += n;
>>> +			if ((fp->_flags & (__SLBF | __SNBF)) == 0)
>>> +				fp->_w -= n;
>>>   			fp->_flags |= __SERR;
>>>   			return (EOF);
>>>   		}
>>> 
>> The description is incomplete. This code also does internal stdio
>> structure adjustment for partial write.
>> 
> Oh  yes, I forgot about that part.
>
> The story is that Apple only does this for EAGAIN but Bruce suggested it
> should be done for other errors as well.
>
> TBH, I wasn't going to merge this change but it seemed consistent to have
> all the changes that originated from Apple's libc together.

The tests for it seem to be missing too.

The other errors are mainly EINTR.

stdio is almost unusable in the presence of EAGAIN or EINTR.  Its
philosophy is to treat these as normal errors  push the error handling
up to the caller, but this means that almost any stdio operation can
fail in unexpected ways, and stdio provides no portable way to even
classify these errors.  Normally in BSD, EINTR rarely happens because
at least read() and write() are restarted after interrupts and files
with non-blocking i/o are rare.  Both using SA_RESTART to stop syscalls
being restarted and using O_NONBLOCK are at a lower level than stdio,
so applications that use them are mostly on their own.  But non-BSD
programs have to deal with EINTR (especially POSIX ones where EINTR
is not at a lower level than the system), an file descriptors with
O_NONBLOCK can be produced by users and enforced on stdio by fdopen()
(again in POSIX).  POSIX does document EAGAIN and EINTR as extensions
of C99 for stdio functions.

Non-stdio is difficult to use in the absence of these errors.  Signal
handling in top(1) is still broken by restarting read().  It used to
work in most cases using unsafe signal handling (clean up and exit in
the SIGINT handler).  It would work with no syscall restarting and
safe signal handling (just set a flag in the syscall and check it in
the main loop).  But FreeBSD has syscall restarting and safe signal
handling, so input waits unboundedly for a newline, EOF, or an actual
error after receiving a SIGINT (EOF handling is broken too).

It seems necessary for _any_ interactive program to turn off syscall
restarting around _any_ syscall that might block unboundedly, and then
handle the EINTRs that may occur from this.  This is nontrivial and
not done by most programs.  top(1) is relatively easy to fix since it
only has about place to change and this place doesn't use stdio.

I know too much about this since I once broke a version of stdio to
handle EAGAIN internally.  Any handling prevents the application seeing
the EAGAIN and handling it appropriately.  Stdio has no way to know if
the application wants to retry immediately, and there is no way to tell
it what to do.  Spinning retrying EAGAIN in stdio is just better than
what an average application will do.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20140818102031.C948>