|
|
- # Copyright (c) IPython Development Team.
- # Distributed under the terms of the Modified BSD License.
-
- try:
- import ctypes
- except:
- ctypes = None
- import os
- import platform
- import signal
- import time
- try:
- from _thread import interrupt_main # Py 3
- except ImportError:
- from thread import interrupt_main # Py 2
- from threading import Thread
-
- from traitlets.log import get_logger
-
- import warnings
-
- 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:
- get_logger().warning("Parent appears to have exited, shutting down.")
- 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
-
- def run(self):
- """ Run the poll loop. This method never returns.
- """
- try:
- from _winapi import WAIT_OBJECT_0, INFINITE
- except ImportError:
- 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:
- # check if signal handler is callable
- # to avoid 'int not callable' error (Python issue #23395)
- if callable(signal.getsignal(signal.SIGINT)):
- interrupt_main()
-
- elif handle == self.parent_handle:
- get_logger().warning("Parent appears to have exited, shutting down.")
- os._exit(1)
- elif result < 0:
- # wait failed, just give up and stop polling.
- warnings.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
|