c - Proper error handling for fclose impossible (according to manpage)? -


so i'm studying fclose manpage quite while , conclusion if fclose interrupted signal, according manpage there no way recover...? missing point?

usually, unbuffered posix functions (open, close, write, etc...) there way recover signal interruption (eintr) restarting call; in contrast documentation of buffered calls states after failed fclose attempt try has undefined behavior... no hint how recover instead. "unlucky" if signal interrupts fclose? data might lost , can't sure whether file descriptor closed or not. know buffer deallocated, file descriptor? think large scale applications use lot's of fd's simultaneously , run problems if fd's not freed -> assume there must clean solution problem.

so let's assume i'm writing library , it's not allowed use sigaction , sa_restart , lots of signals sent, how recover if fclose interrupted? idea call close in loop (instead of fclose) after fclose failed eintr? documentation of fclose doesn't mention state of file descriptor; undefined not helpful though... if fd closed , call close again, weird hard-to-debug side-effects occur naturally rather ignore case doing wrong thing... again, there no unlimited number of file descriptors available, , resource leakage sort of bug (at least me).

of course could check one specific implementation of fclose can't believe designed stdio , didn't think problem? documentation bad or design of function?

this corner case bugs me :(

eintr , close()

in fact, there problems close(), not fclose().

posix states close() returns eintr, means application may retry call. things more complicated in linux. see this article on lwn , this post.

[...] posix eintr semantics not possible on linux. file descriptor passed close() de-allocated in processing of system call , same descriptor have been handed out thread time close() returns.

this blog post , this answer explain why it's not idea retry close() failed eintr. in linux, can nothing meaningful if close() failed eintr (or einprogress).

also note close() asynchronous in linux. e.g., umount may return ebusy after closing last opened descriptor on filesystem since it's not yet released in kernel. see interesting discussion here: page 1, page 2.


eintr , fclose()

posix states fclose():

after call fclose(), use of stream results in undefined behavior.

whether or not call succeeds, stream shall disassociated file , buffer set setbuf() or setvbuf() function shall disassociated stream. if associated buffer automatically allocated, shall deallocated.

i believe means if close() failed, fclose() should free resources , produce no leaks. it's true @ least glibc , uclibc implementations.


reliable error handling

  • call fflush() before fclose().

    since can't determine if fclose() failed when called fflush() or close(), have explicitly call fflush() before fclose() ensure userspace buffer sent kernel.

  • don't retry after eintr.

    if fclose() failed eintr, can not retry close() , can not retry fclose().

  • call fsync() if need.

    • if care data integrity, should call fsync() or fdatasync() before calling fclose() 1.
    • if don't, ignore eintr fclose().

notes

  • if fflush() , fsync() succeeded , fclose() failed eintr, no data lost , no leaks occur.

  • you should ensure file object not used between fflush() , fclose() calls thread 2.


[1] see "everything wanted know fsync()" article explains why fsync() may asynchronous operation.

[2] can call flockfile() before calling fflush() , fclose(). should work fclose() correctly.


Comments