Hello Cafe,
I would like to point out a problem common to all programming languages,
and that Haskell hasn't addressed yet while other languages have.
It is about what happens to file descriptors when the `exec()` syscall
is used (whenever you `readProcess`, `createProcess`, `system`, use any
form of `popen()`, Shake's `cmd` etc.).
(A Markdown-rendered version containing most of this email can be found
at https://github.com/ndmitchell/shake/issues/253.)
Take the following function
f :: IO ()
f = do
inSomeTemporaryDirectory $ do
BS.writeFile "mybinary" binaryContents
setPermissions "mybinary" (setOwnerExecutable True emptyPermissions)
_ <- readProcess "./mybinary" [] ""
return ()
If this is happening in parallel, e.g. using,
forkIO f >> forkIO f >> forkIO f >> threadDelay 5000000`
then on Linux the `readProcess` might often fail wit the error message
mybinary: Text file busy
This error means "Cannot execute the program 'mybinary' because it is
open for writing by some process".
How can this happen, given that we're writing all `mybinary` files in
completely separate temporary directories, and given that `BS.writeFile`
guarantees to close the file handle / file descriptor (`Fd`) before it
returns?
The answer is that by default, child processes on Unix (`fork()+exec()`)
inherit all open file descriptors of the parent process. An ordering
that leads to the problematic case could be:
* Thread 1 writes its file completely (opens and closes an Fd 1)
* Thread 2 starts writing its file (Fd 2 open for writing)
* Thread 1 executes "myBinary" (which calls `fork()` and `exec()`). Fd 2
is inherited by the child process
* Thread 2 finishes writing (closes its Fd 2)
* Thread 2 executes "myBinary", which fails with `Text file busy`
because an Fd is still open to it in the child of Process 1