parentpoller.py
142 lines
| 4.9 KiB
| text/x-python
|
PythonLexer
epatters
|
r3027 | # Standard library imports. | ||
MinRK
|
r6127 | try: | ||
import ctypes | ||||
except: | ||||
ctypes = None | ||||
epatters
|
r3027 | import os | ||
Min Ragan-Kelley
|
r4253 | import platform | ||
epatters
|
r3027 | import time | ||
Thomas Kluyver
|
r13354 | try: | ||
from _thread import interrupt_main # Py 3 | ||||
except ImportError: | ||||
from thread import interrupt_main # Py 2 | ||||
epatters
|
r3027 | from threading import Thread | ||
Min RK
|
r4258 | from IPython.utils.warn import warn | ||
epatters
|
r3027 | |||
class ParentPollerUnix(Thread): | ||||
Bernardo B. Marques
|
r4872 | """ A Unix-specific daemon thread that terminates the program immediately | ||
epatters
|
r3027 | when the parent process no longer exists. | ||
""" | ||||
def __init__(self): | ||||
super(ParentPollerUnix, self).__init__() | ||||
self.daemon = True | ||||
Bernardo B. Marques
|
r4872 | |||
epatters
|
r3027 | 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) | ||||
Matthias BUSSONNIER
|
r7787 | except OSError as e: | ||
epatters
|
r3027 | 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. | ||||
""" | ||||
Bernardo B. Marques
|
r4872 | |||
epatters
|
r3027 | 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 | ||||
Bernardo B. Marques
|
r4872 | If provided, the program will terminate immediately when this | ||
epatters
|
r3027 | handle is signaled. | ||
""" | ||||
assert(interrupt_handle or parent_handle) | ||||
super(ParentPollerWindows, self).__init__() | ||||
MinRK
|
r6127 | if ctypes is None: | ||
raise ImportError("ParentPollerWindows requires ctypes") | ||||
epatters
|
r3027 | 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): | ||||
Bernardo B. Marques
|
r4872 | _fields_ = [ ("nLength", ctypes.c_int), | ||
("lpSecurityDescriptor", ctypes.c_void_p), | ||||
epatters
|
r3027 | ("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. | ||||
""" | ||||
MinRK
|
r8557 | try: | ||
from _winapi import WAIT_OBJECT_0, INFINITE | ||||
except ImportError: | ||||
from _subprocess import WAIT_OBJECT_0, INFINITE | ||||
epatters
|
r3027 | |||
# 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) | ||||
Min Ragan-Kelley
|
r4253 | arch = platform.architecture()[0] | ||
c_int = ctypes.c_int64 if arch.startswith('64') else ctypes.c_int | ||||
epatters
|
r3027 | |||
# Listen forever. | ||||
while True: | ||||
result = ctypes.windll.kernel32.WaitForMultipleObjects( | ||||
len(handles), # nCount | ||||
Min Ragan-Kelley
|
r4253 | (c_int * len(handles))(*handles), # lpHandles | ||
epatters
|
r3027 | 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) | ||||
Min Ragan-Kelley
|
r4253 | elif result < 0: | ||
Min RK
|
r4258 | # wait failed, just give up and stop polling. | ||
warn("""Parent poll failed. If the frontend dies, | ||||
the kernel may be left running. Please let us know | ||||
about your system (bitness, Python, etc.) at | ||||
ipython-dev@scipy.org""") | ||||
return | ||||