obsidiansystems / obelisk

Functional reactive web and mobile applications, with batteries included.
https://reflex-frp.org
BSD 3-Clause "New" or "Revised" License
954 stars 104 forks source link

ob run: Child processes are orphaned on SIGHUP #893

Open plt-amy opened 2 years ago

plt-amy commented 2 years ago

(On Linux,) when terminating ob run with SIGHUP - e.g. because your terminal is being closed - the child ghcid process stays alive, and keeps the port occupied. Repro is essentially:

$ ob run # in basically any obelisk project
[...]
Frontend running on http://localhost:8000/

Then pkill -SIGHUP ob and verify that ghcid is still running, e.g. with pgrep or simply by causing a reload and verifying that the frontend is still running/updating:

$ pgrep ghcid
257475
$ pkill -SIGHUP ob
$ pgrep ghcid
257475

I think it'd be more natural for the ghcid process to be terminated when ob run is killed with SIGHUP, just like it's terminated when ob run is killed with SIGTERM.

mankyKitty commented 2 years ago

Similar issue: #633

parenthetical commented 1 year ago

Looking into this some more. (Caveat: I don't know much about process handling.)

Something interesting I noticed in that SIGHUP'ing the ghcid process rather than the Obelisk process leads to the same behavior, leaving behind a ghc process like above. Does that mean could be a ghcid problem?

The runGhcid' function usesObelisk.Command.Project.nixShellWithoutPkgs. InliningrunProcess_` there looks like this:

nixShellWithoutPkgs
  :: MonadObelisk m
  => FilePath -- ^ Path to project root
  -> Bool -- ^ Should this be a pure shell?
  -> Bool -- ^ Should we chdir to the package root in the shell?
  -> Map Text FilePath -- ^ Package names mapped to their paths
  -> String -- ^ Shell attribute to use (e.g. @"ghc"@, @"ghcjs"@, etc.)
  -> Maybe String -- ^ If 'Just' run the given command; otherwise just open the interactive shell
  -> m ()
nixShellWithoutPkgs root isPure chdirToRoot packageNamesAndPaths shellAttr command = do
  pc <- mkObNixShellProc root isPure chdirToRoot packageNamesAndPaths shellAttr command
  bracketOnError
    (createProcess pc)
    (liftIO . Proc.cleanupProcess)
    (\((_, _, _, ph)) -> throwExitCode pc =<< waitForProcess ph)

Changing cleanupProcess to terminateProcess or terminating the process in a SIGHUP handler doesn't change the fact that the ghc process is left:


This is as far as I got before running out of ideas. 
     (\((_, _, _, ph)) -> do
        liftIO $ installHandler 1 (CatchInfo (\_ -> Proc.terminateProcess ph)) Nothing
        throwExitCode pc =<< waitForProcess ph)