
quoth Niklas Hambüchen, ...
The scope of this program is quite general unfortunately: It will happen for any program that uses parallel threads, and that runs two or more external processes at some time. It cannot be fixed by the part that starts the external process (e.g. you can't write a reliable `readProcess` function that doesn't have this problem, since the problem is rooted in the Fds, and there is no version of `exec()` that doesn't inherit parent Fds).
This problem is a general problem in C on Unix, and was discovered quite late.
I believe it has actually been a familiar issue for decades. I don't have any code handy to check, but I'm pretty sure the UNIX system(3) and popen(3) functions closed extraneous file descriptors back in the early '90s, and probably had been doing it for some time by then. I believe this approach to the problem is supported in System.Process, via close_fds. Implementation is a walk through open FDs, in the child fork, closing anything not called for by the procedure's parameters prior to the exec. That approach has the advantage that it applies to all file descriptors, whether created by open(2) or by other means - socket, dup(2), etc. I like this already implemented solution much better than adding a new flag to "all" opens (really only those opens that occur within the Haskell runtime, while of course not for external library FDs.) The O_CLOEXEC proposal wouldn't be the worst or most gratuitous way Haskell tampers with normal UNIX parameters, but any time you do that, you set up the conditions for breaking something that works in C, which I hate to see happen with Haskell. Donn