On Mon, Jun 18, 2012 at 08:17:25PM -0600, Eric Blake wrote: > On 06/18/2012 06:01 PM, Rich Felker wrote: > > > > > If the "closeout" approach works best for coreutils, that's the > > business of the coreutils' maintainers, not my business. However, as I > > discussed on the musl list, I think it's bad design, and I would > > highly recommend other projects not follow it. > > And that's where I disagree - the POSIX folks _specifically_ recommend > the closeout approach of an atexit() handler:
Yes, they also recommend invoking extremely serious UB (aliasing violation, which GCC _will_ miscompile) when using dlsym to obtain a function pointer... With that said, they have a good point, but it's also arguable that random parts of the program should not be using stdin/stdout. Conceptually, these streams "belong to" the main program flow, and except in really simple programs, they probably should not be used explicitly (either by name, or by calling stdio functions that explicitly use them) except in main or a similar function; other functions should just get a FILE * from the caller. > But our argument is that __fpending (well, 'fpending') _should_ be > portable, and I am in the process of proposing it for standardization in > the next version of POSIX because it is so useful. Are you proposing that it be called __fpending or fpending? > Any program that treats stdout as just like any other file risks closing > stdout too early, and then causing undefined behavior in the rest of the > program when some other standard interface accidentally ends up using fd If you're not trating stdout special at all, just as an ordinary stream like any other, then accessing it after closing it is a major logic bug in your program equivalent to accessing a FILE* obtained by fopen after calling fclose on it. With that said, in order to avoid the situation, the way I would actually deal with it is with minimal special-casing, something like: fflush(f); if (ferror(f)) report_error(); if (f!=stdout) fclose(f); I'm aware that this does not detect errors from the actual close syscall, but I'm unconvinced that it's beneficial to do so; after a successful flush, I consider it the operating system's data loss, not the application's, if the data fails to end up on permanent storage. Even if close() returned zero, you could still get this situation with hardware failures, etc. > 1. The atexit() handler is the easiest way to guarantee that things are > closed properly on all normal exit paths. But it also runs on failure exit paths, which is probably undesirable for many programs. Unless you use extra global-var hacks to disable it from running.. > >> 'closein' is similar - an attempt to fix an issue that affects many > >> programs, > >> once and for all. By Eric Blake. > > > > I think closein is just a no-op for conformant implementations. exit > > implicitly closes all streams, including stdin, > > But the implicit close by exit(), while properly repositioning stdin (on > working implementations; glibc is broken but I fixed cygwin to be > working), has the drawback of silently eating errors if something went > wrong. If you WANT to detect read errors, and consolidate the error > detection into a single ferror() location rather than littering the rest > of your code with harder-to-maintain checks, then closein is the way to Anywhere you read, you could hit EOF, so you already need to be testing for that. Once you get to the "we can't read anymore" code, it's trivial to check ferror(f). And again it can be done in general without any special attention to whether the file is stdin. > > Incidentally, I suspect musl is _not_ currently handling this case > > correctly. Does gnulib have some tests that assert the required > > behavior, which I could use to test the current behavior and any > > efforts to fix it if it's wrong? > > tests/test-closein.{c,sh} > > are sufficient to test the closein module; if you comment out the > atexit() call, it will be sufficient to demonstrate that glibc leaves > stdin at the wrong file offset on exit(); it is also sufficient to OK. I'll see if I can fix this on our side. Rich