[Haskell-cafe] Discussion: The CLOEXEC problem

Alexander Kjeldaas alexander.kjeldaas at gmail.com
Fri Jul 24 22:29:18 UTC 2015


On Mon, Jul 20, 2015 at 3:07 PM, Niklas Hambüchen <mail at nh2.me> wrote:

> 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
>
>
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()?

Alexander
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20150725/b71051b6/attachment.html>


More information about the Haskell-Cafe mailing list