Your workaround does not work as written, due to the double-close bug you cited. Double-close is harmless in single-threaded programs as long as there are no intervening operations which could open new file descriptors (the second close will just fail harmlessly with EBADF
) but they are critical bugs in multi-threaded programs. Consider this scenario:
- Thread A calls
close(n)
. - Thread B calls
open
and it returnsn
which gets stored asint fd1
. - Thread A calls
close(n)
again. - Thread B calls
open
again and it returnsn
again, which gets stored asfd2
. - Thread B now attempts to write to
fd1
and actually writes into the file opened by the second call toopen
instead of the one first opened.
This can lead to massive file corruption, information leak (imagine writing a password to a socket instead of a local file), etc.
However, the problem is easy to fix. Instead of calling fdopen
twice with the same file descriptor, simply use dup
to copy it and pass the copy to fdopen
. With this simple fix, stdio is perfectly usable with sockets. It's not suitable for asynchronous event loop usage still, but if you're using threads for IO, it works great.
Edit: I think I skipped answering your question 1. What happens if you violate the rules about how to switch between input and output on a stdio stream is undefined behavior. This means testing it and seeing that it "works" is not meaningful; it could mean either:
The C implementation you're using provides a definition (as part of its documentation) for what happens in this case, and it matches the behavior you wanted. In this case, you can use it, but your code will not be portable to other implementations. Doing so is considered very bad practice for this reason. Or,
You just got the result you expected by chance, usually as a side effect of how the relevant functionality is implemented internally on the implementation you're using. In this case, there's no guarantee that it doesn't have corner cases that fail to behave as you expected, or that it will continue to work the same way in future releases, etc.