View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0000816 | 1003.1(2013)/Issue7+TC1 | System Interfaces | public | 2014-01-17 21:38 | 2019-08-29 16:09 |
Reporter | eblake | Assigned To | |||
Priority | normal | Severity | Objection | Type | Clarification Requested |
Status | Closed | Resolution | Accepted | ||
Name | Eric Blake | ||||
Organization | Red Hat | ||||
User Reference | ebb.fflush | ||||
Section | fflush | ||||
Page Number | 850 | ||||
Line Number | 28391 | ||||
Interp Status | --- | ||||
Final Accepted Text | See desired action | ||||
Summary | 0000816: does fflush(NULL) affect seekable read streams? | ||||
Description | As a C extension, POSIX requires fflush(stream) to affect the underlying position of a seekable file descriptor associated with any input stream. But it is not obvious whether this effect is also required for fflush(NULL), even though such behavior is observable and desirable on POSIX platforms. The existing wording describes fflush(NULL) in terms of actions "defined above", but where the above text only describes output streams, and the CX behavior of input streams is described below, so someone could argue that input streams are unaffected by such a flush, but that contradicts existing behavior. I noticed the problem while trying to explain why the program below violates XSH 2.5.1 (page 496) and therefore exhibits undefined behavior when called with 0 arguments [argc==1] (in particular, Solaris reports different output than glibc, and the program goes into an infinite loop on any build of cygwin from 2013. Cygwin has since been patched to behave more like glibc, although I still contend that glibc has never fully obeyed the rules regarding fflush on exit in the first place). The reason that the argc==1 case is undefined is that the first handle (the use of the stream prior to fork()) used fgetc() to alter the position of the stream, but did not fflush or fseek to reflect that position back to the underlying fd; the second stream then also alters the position of the stream (via the implicit flush done by the implicit exit() at the end of main) leaving the next byte read from the parents stream indeterminate. My immediate suggestion to insert an fflush(NULL) prior to fork() won't work if fflush(NULL) doesn't guarantee an effect on input streams [the behavior demonstrated with argc==3 - and evidence of the glibc bug of not flushing on exit], but for all systems that already comply with POSIX, my testing showed identical to the behavior of fflush(fp) [argc==2], which is why I prefer mandating the behavior even if it further points out problems in the glibc implementation. $ cat foo.c #include <stdio.h> #include <unistd.h> int main(int argc, char **argv) { FILE *fp = fopen("foo.c", "r"); printf("parent: %d\n", (int)getpid()); fgetc(fp); switch (argc) { case 2: fflush(fp); break; case 3: fflush(NULL); break; } switch (fork()) { case -1: _exit(1); case 0: break; default: sleep(1); } printf("process %d, offset %d\n", (int)getpid(), (int)lseek(fileno(fp), 0, SEEK_CUR)); return 0; } Linux: $ ./foo parent: 24952 process 24953, offset 526 process 24952, offset 526 $ ./foo 1 parent: 24960 process 24961, offset 1 process 24960, offset 1 $ ./foo 1 2 parent: 25702 process 25703, offset 526 process 25702, offset 526 Solaris: $ ./foo parent: 16333 process 16334, offset 526 process 16333, offset 1 $ ./foo 1 parent: 16336 process 16337, offset 1 process 16336, offset 1 $ ./foo 1 2 parent: 16347 process 16348, offset 1 process 16347, offset 1 Intuitively, just as: putc('a'); if (cond) fflush(NULL); fork(); exit(); needs 'cond' to be non-zero to guarantee that 'a' is not output twice, the counterpart for reading needs fflush(NULL) before fork to guarantee that 'a' is not read twice. And the reason that Linux behavior is buggy is that we require that: { sed -n 1q; sed -n 1q; cat; } < file behave the same as tail -n+3 file for any seekable file, but because Linux fails to reset the position of fds on normal exit(), the subsequent processes in the group do not pick up processing where the earlier processes left off. | ||||
Desired Action | On page 850 (XSH fflush DESCRIPTION), swap lines 28391-2 with lines 28393-7. The resulting order of the two paragraphs (as shown below) then makes it clear that fflush(NULL) must affect underlying positions of seekable fds associated with read streams:For a stream open for reading, if the file is not already at EOF, and the file is one capable of seeking, the file offset of the underlying open file description shall be set to the file position of the stream, and any characters pushed back onto the stream by ungetc( ) or ungetwc( ) that have not subsequently been read from the stream shall be discarded (without further changing the file offset). If stream is a null pointer, fflush( ) shall perform this flushing action on all streams for which the behavior is defined above. | ||||
Tags | tc2-2008 |
|
For anyone that read the original problem description, I ended up tweaking it to fix some errors in my writeup that I noticed in subsequent testing. |
Date Modified | Username | Field | Change |
---|---|---|---|
2014-01-17 21:38 | eblake | New Issue | |
2014-01-17 21:38 | eblake | Name | => Eric Blake |
2014-01-17 21:38 | eblake | Organization | => Red Hat |
2014-01-17 21:38 | eblake | User Reference | => ebb.fflush |
2014-01-17 21:38 | eblake | Section | => fflush |
2014-01-17 21:38 | eblake | Page Number | => 850 |
2014-01-17 21:38 | eblake | Line Number | => 28391 |
2014-01-17 21:38 | eblake | Interp Status | => --- |
2014-01-17 21:52 | eblake | Description Updated | |
2014-01-17 22:03 | eblake | Description Updated | |
2014-01-17 22:04 | eblake | Note Added: 0002108 | |
2014-01-17 22:13 | eblake | Relationship added | related to 0000087 |
2014-03-13 16:00 | nick | Final Accepted Text | => See desired action |
2014-03-13 16:00 | nick | Status | New => Resolved |
2014-03-13 16:00 | nick | Resolution | Open => Accepted |
2014-03-13 16:00 | nick | Tag Attached: tc2-2008 | |
2014-03-13 16:01 | nick | Desired Action Updated | |
2019-06-10 08:54 | agadmin | Status | Resolved => Closed |
2019-08-29 16:09 | eblake | Relationship added | related to 0000701 |