<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jul 20, 2015 at 3:07 PM, Niklas Hambüchen <span dir="ltr"><<a href="mailto:mail@nh2.me" target="_blank">mail@nh2.me</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hello Cafe,<br>
<br>
I would like to point out a problem common to all programming languages,<br>
and that Haskell hasn't addressed yet while other languages have.<br>
<br>
It is about what happens to file descriptors when the `exec()` syscall<br>
is used (whenever you `readProcess`, `createProcess`, `system`, use any<br>
form of `popen()`, Shake's `cmd` etc.).<br>
<br>
(A Markdown-rendered version containing most of this email can be found<br>
at <a href="https://github.com/ndmitchell/shake/issues/253" rel="noreferrer" target="_blank">https://github.com/ndmitchell/shake/issues/253</a>.)<br>
<br>
Take the following function<br>
<br>
f :: IO ()<br>
f = do<br>
  inSomeTemporaryDirectory $ do<br>
    BS.writeFile "mybinary" binaryContents<br>
    setPermissions "mybinary" (setOwnerExecutable True emptyPermissions)<br>
    _ <- readProcess "./mybinary" [] ""<br>
   return ()<br>
<br>
If this is happening in parallel, e.g. using,<br>
<br>
  forkIO f >> forkIO f >> forkIO f >> threadDelay 5000000`<br>
<br>
then on Linux the `readProcess` might often fail wit the error message<br>
<br>
  mybinary: Text file busy<br>
<br>
This error means "Cannot execute the program 'mybinary' because it is<br>
open for writing by some process".<br>
<br>
How can this happen, given that we're writing all `mybinary` files in<br>
completely separate temporary directories, and given that `BS.writeFile`<br>
guarantees to close the file handle / file descriptor (`Fd`) before it<br>
returns?<br>
<br>
The answer is that by default, child processes on Unix (`fork()+exec()`)<br>
inherit all open file descriptors of the parent process. An ordering<br>
that leads to the problematic case could be:<br>
<br>
* Thread 1 writes its file completely (opens and closes an Fd 1)<br>
* Thread 2 starts writing its file (Fd 2 open for writing)<br>
* Thread 1 executes "myBinary" (which calls `fork()` and `exec()`). Fd 2<br>
is inherited by the child process<br>
* Thread 2 finishes writing (closes its Fd 2)<br>
* Thread 2 executes "myBinary", which fails with `Text file busy`<br>
because an Fd is still open to it in the child of Process 1<br>
<br></blockquote><div><br><div>I think CLOEXEC should be the default, but it doesn't seem to solve your problem. What if thread 2 executes "myBinary" before thread 1 called exec()?<br></div></div><br><div>Alexander<br></div></div></div></div>