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