##// END OF EJS Templates
Merge pull request #1471 from minrk/connection...
Merge pull request #1471 from minrk/connection simplify IPython.parallel connections and enable Controller Resume Rolls back two-stage connection info, putting more information into the connection files. This makes it easier to use hand-crafted ssh tunnels, as all ports are read from the file, rather than from the reply to registration/connection requests. It is no longer possible to connect to the Controller without a connection file. Adding the serialization method to the connection file also makes it harder for custom serialization to result in a mismatch in configuration between the various objects.

File last commit:

r7787:28b538a9
r7962:bff463b5 merge
Show More
parentpoller.py
136 lines | 4.7 KiB | text/x-python | PythonLexer
# Standard library imports.
try:
import ctypes
except:
ctypes = None
import os
import platform
import time
from thread import interrupt_main
from threading import Thread
from IPython.utils.warn import warn
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 as 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__()
if ctypes is None:
raise ImportError("ParentPollerWindows requires ctypes")
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)
arch = platform.architecture()[0]
c_int = ctypes.c_int64 if arch.startswith('64') else ctypes.c_int
# Listen forever.
while True:
result = ctypes.windll.kernel32.WaitForMultipleObjects(
len(handles), # nCount
(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)
elif result < 0:
# 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