parentpoller.py
119 lines
| 4.1 KiB
| text/x-python
|
PythonLexer
epatters
|
r3027 | # Standard library imports. | ||
import ctypes | ||||
import os | ||||
import time | ||||
from thread import interrupt_main | ||||
from threading import Thread | ||||
class ParentPollerUnix(Thread): | ||||
""" A Unix-specific daemon thread that terminates the program immediately | ||||
when the parent process no longer exists. | ||||
""" | ||||
def __init__(self): | ||||
super(ParentPollerUnix, self).__init__() | ||||
self.daemon = True | ||||
def run(self): | ||||
# We cannot use os.waitpid because it works only for child processes. | ||||
from errno import EINTR | ||||
while True: | ||||
try: | ||||
if os.getppid() == 1: | ||||
os._exit(1) | ||||
time.sleep(1.0) | ||||
except OSError, e: | ||||
if e.errno == EINTR: | ||||
continue | ||||
raise | ||||
class ParentPollerWindows(Thread): | ||||
""" A Windows-specific daemon thread that listens for a special event that | ||||
signals an interrupt and, optionally, terminates the program immediately | ||||
when the parent process no longer exists. | ||||
""" | ||||
def __init__(self, interrupt_handle=None, parent_handle=None): | ||||
""" Create the poller. At least one of the optional parameters must be | ||||
provided. | ||||
Parameters: | ||||
----------- | ||||
interrupt_handle : HANDLE (int), optional | ||||
If provided, the program will generate a Ctrl+C event when this | ||||
handle is signaled. | ||||
parent_handle : HANDLE (int), optional | ||||
If provided, the program will terminate immediately when this | ||||
handle is signaled. | ||||
""" | ||||
assert(interrupt_handle or parent_handle) | ||||
super(ParentPollerWindows, self).__init__() | ||||
self.daemon = True | ||||
self.interrupt_handle = interrupt_handle | ||||
self.parent_handle = parent_handle | ||||
@staticmethod | ||||
def create_interrupt_event(): | ||||
""" Create an interrupt event handle. | ||||
The parent process should use this static method for creating the | ||||
interrupt event that is passed to the child process. It should store | ||||
this handle and use it with ``send_interrupt`` to interrupt the child | ||||
process. | ||||
""" | ||||
# Create a security attributes struct that permits inheritance of the | ||||
# handle by new processes. | ||||
# FIXME: We can clean up this mess by requiring pywin32 for IPython. | ||||
class SECURITY_ATTRIBUTES(ctypes.Structure): | ||||
_fields_ = [ ("nLength", ctypes.c_int), | ||||
("lpSecurityDescriptor", ctypes.c_void_p), | ||||
("bInheritHandle", ctypes.c_int) ] | ||||
sa = SECURITY_ATTRIBUTES() | ||||
sa_p = ctypes.pointer(sa) | ||||
sa.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES) | ||||
sa.lpSecurityDescriptor = 0 | ||||
sa.bInheritHandle = 1 | ||||
return ctypes.windll.kernel32.CreateEventA( | ||||
sa_p, # lpEventAttributes | ||||
False, # bManualReset | ||||
False, # bInitialState | ||||
'') # lpName | ||||
@staticmethod | ||||
def send_interrupt(interrupt_handle): | ||||
""" Sends an interrupt event using the specified handle. | ||||
""" | ||||
ctypes.windll.kernel32.SetEvent(interrupt_handle) | ||||
def run(self): | ||||
""" Run the poll loop. This method never returns. | ||||
""" | ||||
from _subprocess import WAIT_OBJECT_0, INFINITE | ||||
# Build the list of handle to listen on. | ||||
handles = [] | ||||
if self.interrupt_handle: | ||||
handles.append(self.interrupt_handle) | ||||
if self.parent_handle: | ||||
handles.append(self.parent_handle) | ||||
# Listen forever. | ||||
while True: | ||||
result = ctypes.windll.kernel32.WaitForMultipleObjects( | ||||
len(handles), # nCount | ||||
(ctypes.c_int * len(handles))(*handles), # lpHandles | ||||
False, # bWaitAll | ||||
INFINITE) # dwMilliseconds | ||||
if WAIT_OBJECT_0 <= result < len(handles): | ||||
handle = handles[result - WAIT_OBJECT_0] | ||||
if handle == self.interrupt_handle: | ||||
interrupt_main() | ||||
elif handle == self.parent_handle: | ||||
os._exit(1) | ||||