[racket] adding other objects to custodian

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Sun Jul 4 11:43:49 EDT 2010

At Sat, 03 Jul 2010 22:03:33 -0400, Neil Van Dyke wrote:
> I want to be sure that a host OS subprocess created with "subprocess" is 
> killed when thread of my program in which it was created terminates.

This is a common request for built-in default functionality. Each time
I try to add it, I get stuck on three issues:

  * Under Unix and Mac OS X, there are multiple ways to terminate a
    process: SIGTERM (can be ignored), SIGKILL (can't be ignored), and
    SIGINT (usually ends a non-interactive program, anyway). At first
    glance, the more custodian-like shutdown is SIGKILL, but if you
    think of a subprocess as a kind of untrusted code, then SIGTERM or
    SIGINT may be more appropriate --- so that the program can clean up
    before exiting. Under Windows, we have only the equivalent of
    SIGKILL, though.

 * If you use `process' or `system' instead of `process*' or `system*',
   then there may be an intermediate shell process between Racket and
   process that the programmer probably has in mind. Killing the shell
   process (with either SIGKILL or SIGTERM) doesn't kill the
   subprocesses, and shells seem to ignore SIGINT without passing it
   along to a subprocess.

   At the same time, if the command is simple enough, a Unix `sh' will
   typically replace the shell process with the target program, so
   SIGKILL and SIGTERM would work as expected. That behavior of the
   shell is not gauranteed, as far as I can tell. So, in the common
   case of `system' and `process' under Unix, custodian-based
   termination will look like it works better than it actually does,
   which is misleading in a worrying way.

 * A subprocess can create any number of sub-subprocesses itself, and
   there's no way to kill those, which isn't very custodian-like.

With all that in mind, I don't think there's a good default behavior
that tries to terminate a subprocess when a custodian is shut down. We
could add a parameter that determines how `subprocess' interacts with
the custodian: SIGINT, SIGKILL, or nothing (i.e., the current
behavior). I think the default should be nothing; having to set a
parameter encourages a programmer to become informed on the issues of
OS-level processes --- or, at least, explicitly accept best-effort
termination that could just as well make things worse instead of

Any opinions?

Meanwhile, on the question of how to do this now:

> Currently, I create a custodian for the thread and explicitly call 
> "custodian-shutdown-all" at the end of the thread's lifetime (in a 
> "dynamic-wind" cleanup thunk).
> Ideally, I would like to use this same custodian to kill the subprocess, 
> but I do not see how.  "make-custodian-box" does not seem to involve 
> "custodian-shutdown-all" evaluating a closure in the box or executing a 
> will.
> If using the current custodian is not possible, then I think I have to 
> do something like make a will executor and execute it from my 
> "dynamic-wind" cleanup thunk.

That sounds ok. Also, you could use the FFI and scheme_add_managed().

I started to suggest that you use custodian boxes and have a thread
that waits until a box is emptied to terminate the corresponding
process. That would work better if custodian boxes were synchronizable
events; I will make custodian boxes act as events, so that strategy
could work in the future.

Posted on the users mailing list.