Friday, September 07, 2007

Windows, killableprocess, and the Case of the Suicidal Parent (or: How to kill a process and all of its subprocesses reliably on Windows)

The two major browsers available on the market today (Firefox and IE) are sometimes "suicidal parents" when launched from the command line. That is, they occasionally have to re-launch themselves, by spawning a new copy of their executable (firefox.exe or iexplore.exe) and killing the currently running process. In these cases, the orphaned child process lives on to become the GUI that you actually see on the screen.

Firefox commits suicide when it sees that it needs to make significant adjustments to its user profile directory, e.g. when it has detected new versions of your extensions/add-ons. IE commits suicide on Vista when running in Vista's new "Protected" mode. When you launch iexplore.exe from the command line on Vista in Protected mode, your iexplore.exe process spawns a copy of "ieuser.exe" (if one isn't running already) and allows THAT process to launch a new copy of iexplore.exe.

This suicidal behavior is invisible to normal end users, but it can be very difficult for people who want to automatically launch and kill browsers for automated testing, like I do. For example, if you spawn a suicidal parent in Java and then try to .destroy it, you'll run straight into Java bug 4770092 (Process.destroy does not kill multiple child processes).

Benjamin Smedberg posted an interesting blog entry about a way to use Python to handle child processes: killableprocess.py. On Windows, killableprocess.py handles child processes by launching them inside of a Windows "Job" object. Processes within a Job may be assigned special security privileges or restrictions, and, by default, whenever any process in a Job creates a subprocess, the subprocess becomes a member of the same Job. (Job objects were introduced in Windows 2000.) It's easy and convenient to terminate an entire Job at once, killing all processes within it, even if the processes are orphans of suicidal parents.

Using Jobs to manage a process tree is a great idea, but not everybody in the world has the Python runtime installed, so I decided to code up something like it in C++.

The compiled executable (killableprocess.exe) can be used to launch any other process you like, assigning that process to a new unnamed Job. killableprocess.exe will automatically terminate if every process in the Job has died. It's also designed to handle shutdown signals (Ctrl-C and Ctrl-Break), to terminate the Job as killableprocess.exe is being shutdown. As a fallback, it additionally registers a "job limit" with the operating system to automatically kill the Job if killableprocess.exe is forcibly terminated. (Beware... the job limit doesn't work if you use Process Explorer.)

You can use killableprocess.exe as a wrapper around other processes that you expect to be suicidal parents.

Download killableprocess.exe
Source code for killableprocess.exe