haskell / process

Library for dealing with system processes
http://hackage.haskell.org/package/process
Other
87 stars 82 forks source link

What is the recommended way to gracefully stop a Haskell process? #119

Open gromakovsky opened 6 years ago

gromakovsky commented 6 years ago

The only function to stop a process I found is terminateProcess. According to the documentation, on Unix systems it will send the SIGTERM signal. I did some research and concluded that apparently when a Haskell process receives SIGTERM signal, it will not throw an exception to the main thread, but will stop everything rudely, so that, for example, second action passed to bracket will not be executed. That differs from SIGINT handling which causes UserInterrupt to be thrown. I used this code to test it. But I want to kill a process gracefully, to allow bracket to do all cleanup actions.

So I am wondering: shouldn't process have a function which will do it? Alternatively I can use System.Posix.Signals from unix to send SIGINT directly (and do something else on Windows). Or I can set a special handler for SIGTERM in my Haskell program which will behave akin to SIGINT. I would appreciate any recommendations.

snoyberg commented 6 years ago

If you need to send a specific signal, then I would recommend using the unix package directly. I'm not opposed to providing additional functions in System.Process to send other signals. However, keep in mind that this will not help at all if your Haskell process is killed by an executable compiled against an older version of the process library, or a different program that decides to send SIGTERM. Therefore, if this is really important to you, I would say adding a signal handler for SIGTERM is the right thing to do.

More generally: there's no way you can depend on cleanup actions ever being run in this manner. An external process may elect to kill your process with SIGKILL, for instance, or the machine may just shut down. Generally speaking, cleanup actions are useful for maintaining invariants within a single process, like ensuring locks are releases or file descriptors freed. However, cleanup actions which will have evidence outside of the process cannot ever be relied upon to be run, regardless of SIGINT vs SIGTERM.

gromakovsky commented 6 years ago

Thanks for the answer. It's not really important for me to run all cleanup actions during shutdown and I understand that the program may be killed with SIGKILL. However, in 99% cases one my application will kill another my application, so in these 99% cases I have full control and I was thinking that it's better to do cleanup actions in these 99% cases. For example, my application may create temporary files (e. g. using withSystemTempFile) and I want them to be deleted when the application is stopped. If those files are not deleted, it won't break any invariants, but they will take some space on disk and if it happens multiple time (with SIGTERM it may happen every time the application is closed), those files can consume significant amount of disk space. So I think my choice will be to add a signal handler for SIGTERM.

I'm not opposed to providing additional functions in System.Process to send other signals.

The main difficulty is to understand how to do gracefully stop a process on Windows. We will probably try to figure out how do it (there are some recommendations here) and will submit a PR if we succeed.

anacrolix commented 6 years ago

I've been looking into this as I seem to get orphaned processes when restarting with SIGTERM. I suspect Haskell only kills threads triggering process cleanups on SIGINT.

robx commented 2 years ago

I find this issue to be very confusing, @gromakovsky could you clarify? Is this about:

  1. I'm writing a Haskell program. How do I make it shut down cleanly, including generally cleaning up any resources?
  2. I'm writing a Haskell program that uses the process package. How do I make sure that it doesn't leave children behind?
  3. My Haskell program uses the process package to run some child processes. How do I make sure that those child processes clean up after themselves when they're terminated?

If it's about question 1, that doesn't seem to be an issue for the process package, since it's about child processes, not about the process itself. (An answer could be: install a SIGTERM handler that kills the main thread with an asynchronous exception. And make sure all your code is async-exception safe, i.e., put lots of bracket everywhere.)

For point 2, the answer would be to use withProcess (or something like it) -- that guarantees that children will be terminated.