Cleanly replace current Haskell process (exec)

Viewed 8102

I'm writing a Haskell program whose sole purpose is to execute another (non-Haskell) program, after gathering some info to determine what arguments that program should be executed with.

Replacing a running Haskell process is easy enough using executeFile, but I'm wondering if there's a way to ensure the Haskell program properly cleans up after itself before it's replaced.

For example, Control.Exception.finally is normally useful to ensure cleanup actions are performed even if an exception occurs, but it will have no effect if we exec a whole different process:

module Main (main) where

import Control.Exception    (finally)
import System.Posix.Process (executeFile)

main = finally (do putStrLn "opening file descriptors"
                   putStrLn "writing temporary files"
                   executeFile "ls" True [] Nothing)
               (putStrLn "cleaning up")

The above example will never print "cleaning up".

Is there a recommended way to perform such a maneuver "cleanly"?

2 Answers

You need to put the executeFile after finally, because it completely replaces any code from the Haskell program.

main = do
    finally (putStrLn "opening" >> putStrLn "writing") (putStrLn "cleaning up")
    executeFile "ls" True [] Nothing

The only exception that can occur from executeFile is a problem with the exec itself, but you have to proceed as if the exec will work and you aren't coming back to the Haskell code; it's a one-way goto, not a subroutine call.

There's really nothing Haskell-specific about this. If you want to do things after a process is done, you can't just exec(3)/executeFile it. You must instead fork(2)/forkProcess, then in the parent, do waitpid(2)/getProcessStatus on the child, and do your cleanup once that returns.


Edit: The above is to clean up after the new process is done. If you don't care about that, and just want to clean up when your Haskell is done but the new process is still running, then chepner's answer will work.

Related