After some research there was no definitive answer as to how to have a child process understand that the parent process has died/crashed/exited under Windows which can make the child process run unattended. There are some proposals as in:
Jobs: Python: how to kill child process(es) when parent dies?
Psutil: subprocess: deleting child processes in Windows
Multiprocessing: Kill Child Process if Parent is killed in Python
Always involving a known parent which has started a child. But there are cases in which the child doesn't know it is a child, because it is not conceived as a child and the parent makes no effort to kill the children.
Furthermore, there is no control of the parent. Practical case:
- Cygwin running under Windows
- Windows Python 1st in the path
- Python executable installed via
setuptools
entry_points facility.
As mentioned above the Python to be executed is the Windows one. The setuptools-generated executable will find it and execute it as subprocess with the asocciated script.
Because one is running under Cygwin the following may fail:
- Pressing
Ctrl-c
will kill the parent (the stub setuptools executable)
- But will leave the child running (to be found in the process list as
python.exe
)
In this case and as mentioned above, it isn't possible to control the parent and the child doesn't know it's a child (because it may also be directly executed as a Python script)
The solution is to do as follows
import sys
def win_wait_for_parent(raise_exceptions=False):
if not sys.platform == 'win32':
return True
# When started under cygwin, the parent process will die leaving a child
# hanging around. The process has to be waited upon
import ctypes
from ctypes.wintypes import DWORD, BOOL, HANDLE
import os
import threading
INFINITE = -1
SYNCHRONIZE = 0x00100000
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
kernel32.OpenProcess.argtypes = (DWORD, BOOL, DWORD)
kernel32.OpenProcess.restype = HANDLE
kernel32.WaitForSingleObject.argtypes = (HANDLE, DWORD)
kernel32.WaitForSingleObject.restype = DWORD
phandle = kernel32.OpenProcess(SYNCHRONIZE, 0, os.getppid())
def check_parent():
# Get a token with right access to parent and wait for it to be
# signaled (die). Exit ourselves then
kernel32.WaitForSingleObject(phandle, INFINITE)
os._exit(0)
if not phandle:
if raise_exceptions:
raise ctypes.WinError(ctypes.get_last_error())
return False
threading.Thread(target=check_parent).start()
return True
which runs in a separate thread if the PID of the process is not the same as the PID of the parent waiting for the parent to be signaled (death). This works under Python 3.3, where os.getppid()
does actually returnt the PID of the parent under window
It requires no modification of the parent and the child doesn't need to be coded in advance as a child, because a check is done as to where the thread has to run or no.
-- refactored as a function and added improvements from comments