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