[racket] adding other objects to custodian
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
better.
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.