##// END OF EJS Templates
avoid ctypes import in parentpoller when unnecessary
MinRK -
Show More
@@ -1,131 +1,136 b''
1 1 # Standard library imports.
2 import ctypes
2 try:
3 import ctypes
4 except:
5 ctypes = None
3 6 import os
4 7 import platform
5 8 import time
6 9 from thread import interrupt_main
7 10 from threading import Thread
8 11
9 12 from IPython.utils.warn import warn
10 13
11 14
12 15 class ParentPollerUnix(Thread):
13 16 """ A Unix-specific daemon thread that terminates the program immediately
14 17 when the parent process no longer exists.
15 18 """
16 19
17 20 def __init__(self):
18 21 super(ParentPollerUnix, self).__init__()
19 22 self.daemon = True
20 23
21 24 def run(self):
22 25 # We cannot use os.waitpid because it works only for child processes.
23 26 from errno import EINTR
24 27 while True:
25 28 try:
26 29 if os.getppid() == 1:
27 30 os._exit(1)
28 31 time.sleep(1.0)
29 32 except OSError, e:
30 33 if e.errno == EINTR:
31 34 continue
32 35 raise
33 36
34 37
35 38 class ParentPollerWindows(Thread):
36 39 """ A Windows-specific daemon thread that listens for a special event that
37 40 signals an interrupt and, optionally, terminates the program immediately
38 41 when the parent process no longer exists.
39 42 """
40 43
41 44 def __init__(self, interrupt_handle=None, parent_handle=None):
42 45 """ Create the poller. At least one of the optional parameters must be
43 46 provided.
44 47
45 48 Parameters:
46 49 -----------
47 50 interrupt_handle : HANDLE (int), optional
48 51 If provided, the program will generate a Ctrl+C event when this
49 52 handle is signaled.
50 53
51 54 parent_handle : HANDLE (int), optional
52 55 If provided, the program will terminate immediately when this
53 56 handle is signaled.
54 57 """
55 58 assert(interrupt_handle or parent_handle)
56 59 super(ParentPollerWindows, self).__init__()
60 if ctypes is None:
61 raise ImportError("ParentPollerWindows requires ctypes")
57 62 self.daemon = True
58 63 self.interrupt_handle = interrupt_handle
59 64 self.parent_handle = parent_handle
60 65
61 66 @staticmethod
62 67 def create_interrupt_event():
63 68 """ Create an interrupt event handle.
64 69
65 70 The parent process should use this static method for creating the
66 71 interrupt event that is passed to the child process. It should store
67 72 this handle and use it with ``send_interrupt`` to interrupt the child
68 73 process.
69 74 """
70 75 # Create a security attributes struct that permits inheritance of the
71 76 # handle by new processes.
72 77 # FIXME: We can clean up this mess by requiring pywin32 for IPython.
73 78 class SECURITY_ATTRIBUTES(ctypes.Structure):
74 79 _fields_ = [ ("nLength", ctypes.c_int),
75 80 ("lpSecurityDescriptor", ctypes.c_void_p),
76 81 ("bInheritHandle", ctypes.c_int) ]
77 82 sa = SECURITY_ATTRIBUTES()
78 83 sa_p = ctypes.pointer(sa)
79 84 sa.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES)
80 85 sa.lpSecurityDescriptor = 0
81 86 sa.bInheritHandle = 1
82 87
83 88 return ctypes.windll.kernel32.CreateEventA(
84 89 sa_p, # lpEventAttributes
85 90 False, # bManualReset
86 91 False, # bInitialState
87 92 '') # lpName
88 93
89 94 @staticmethod
90 95 def send_interrupt(interrupt_handle):
91 96 """ Sends an interrupt event using the specified handle.
92 97 """
93 98 ctypes.windll.kernel32.SetEvent(interrupt_handle)
94 99
95 100 def run(self):
96 101 """ Run the poll loop. This method never returns.
97 102 """
98 103 from _subprocess import WAIT_OBJECT_0, INFINITE
99 104
100 105 # Build the list of handle to listen on.
101 106 handles = []
102 107 if self.interrupt_handle:
103 108 handles.append(self.interrupt_handle)
104 109 if self.parent_handle:
105 110 handles.append(self.parent_handle)
106 111 arch = platform.architecture()[0]
107 112 c_int = ctypes.c_int64 if arch.startswith('64') else ctypes.c_int
108 113
109 114 # Listen forever.
110 115 while True:
111 116 result = ctypes.windll.kernel32.WaitForMultipleObjects(
112 117 len(handles), # nCount
113 118 (c_int * len(handles))(*handles), # lpHandles
114 119 False, # bWaitAll
115 120 INFINITE) # dwMilliseconds
116 121
117 122 if WAIT_OBJECT_0 <= result < len(handles):
118 123 handle = handles[result - WAIT_OBJECT_0]
119 124
120 125 if handle == self.interrupt_handle:
121 126 interrupt_main()
122 127
123 128 elif handle == self.parent_handle:
124 129 os._exit(1)
125 130 elif result < 0:
126 131 # wait failed, just give up and stop polling.
127 132 warn("""Parent poll failed. If the frontend dies,
128 133 the kernel may be left running. Please let us know
129 134 about your system (bitness, Python, etc.) at
130 135 ipython-dev@scipy.org""")
131 136 return
General Comments 0
You need to be logged in to leave comments. Login now