1 # -*- test-case-name: twisted.test.test_process,twisted.internet.test.test_process -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 This module provides a uniform interface to the several mechanisms which are
7 possibly available for dealing with signals.
9 This module is used to integrate child process termination into a
10 reactor event loop. This is a challenging feature to provide because
11 most platforms indicate process termination via SIGCHLD and do not
12 provide a way to wait for that signal and arbitrary I/O events at the
13 same time. The naive implementation involves installing a Python
14 SIGCHLD handler; unfortunately this leads to other syscalls being
15 interrupted (whenever SIGCHLD is received) and failing with EINTR
16 (which almost no one is prepared to handle). This interruption can be
17 disabled via siginterrupt(2) (or one of the equivalent mechanisms);
18 however, if the SIGCHLD is delivered by the platform to a non-main
19 thread (not a common occurrence, but difficult to prove impossible),
20 the main thread (waiting on select() or another event notification
21 API) may not wake up leading to an arbitrary delay before the child
22 termination is noticed.
24 The basic solution to all these issues involves enabling SA_RESTART
25 (ie, disabling system call interruption) and registering a C signal
26 handler which writes a byte to a pipe. The other end of the pipe is
27 registered with the event loop, allowing it to wake up shortly after
28 SIGCHLD is received. See L{twisted.internet.posixbase._SIGCHLDWaker}
29 for the implementation of the event loop side of this solution. The
30 use of a pipe this way is known as the U{self-pipe
31 trick<http://cr.yp.to/docs/selfpipe.html>}.
33 The actual solution implemented in this module depends on the version
34 of Python. From version 2.6, C{signal.siginterrupt} and
35 C{signal.set_wakeup_fd} allow the necessary C signal handler which
36 writes to the pipe to be registered with C{SA_RESTART}. Prior to 2.6,
37 the L{twisted.internet._sigchld} extension module provides similar
40 If neither of these is available, a Python signal handler is used
41 instead. This is essentially the naive solution mentioned above and
42 has the problems described there.
48 from signal import set_wakeup_fd, siginterrupt
50 set_wakeup_fd = siginterrupt = None
57 from twisted.python.log import msg
60 from twisted.internet._sigchld import installHandler as _extInstallHandler, \
61 isDefaultHandler as _extIsDefaultHandler
63 _extInstallHandler = _extIsDefaultHandler = None
66 class _Handler(object):
68 L{_Handler} is a signal handler which writes a byte to a file descriptor
69 whenever it is invoked.
71 @ivar fd: The file descriptor to which to write. If this is C{None},
72 nothing will be written.
74 def __init__(self, fd):
78 def __call__(self, *args):
80 L{_Handler.__call__} is the signal handler. It will write a byte to
81 the wrapped file descriptor, if there is one.
83 if self.fd is not None:
85 os.write(self.fd, '\0')
91 def _installHandlerUsingSignal(fd):
93 Install a signal handler which will write a byte to C{fd} when
94 I{SIGCHLD} is received.
96 This is implemented by creating an instance of L{_Handler} with C{fd}
97 and installing it as the signal handler.
99 @param fd: The file descriptor to which to write when I{SIGCHLD} is
104 previous = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
106 previous = signal.signal(signal.SIGCHLD, _Handler(fd))
107 if isinstance(previous, _Handler):
113 def _installHandlerUsingSetWakeup(fd):
115 Install a signal handler which will write a byte to C{fd} when
116 I{SIGCHLD} is received.
118 This is implemented by installing an instance of L{_Handler} wrapped
119 around C{None}, setting the I{SIGCHLD} handler as not allowed to
120 interrupt system calls, and using L{signal.set_wakeup_fd} to do the
123 @param fd: The file descriptor to which to write when I{SIGCHLD} is
128 signal.signal(signal.SIGCHLD, signal.SIG_DFL)
130 signal.signal(signal.SIGCHLD, _Handler(None))
131 siginterrupt(signal.SIGCHLD, False)
132 return set_wakeup_fd(fd)
136 def _isDefaultHandler():
138 Determine whether the I{SIGCHLD} handler is the default or not.
140 return signal.getsignal(signal.SIGCHLD) == signal.SIG_DFL
144 def _cannotInstallHandler(fd):
146 Fail to install a signal handler for I{SIGCHLD}.
148 This implementation is used when the supporting code for the other
149 implementations is unavailable (on Python versions 2.5 and older where
150 neither the L{twisted.internet._sigchld} extension nor the standard
151 L{signal} module is available).
153 @param fd: Ignored; only for compatibility with the other
154 implementations of this interface.
156 @raise RuntimeError: Always raised to indicate no I{SIGCHLD} handler can
159 raise RuntimeError("Cannot install a SIGCHLD handler")
163 def _cannotDetermineDefault():
164 raise RuntimeError("No usable signal API available")
168 if set_wakeup_fd is not None:
169 msg('using set_wakeup_fd')
170 installHandler = _installHandlerUsingSetWakeup
171 isDefaultHandler = _isDefaultHandler
172 elif _extInstallHandler is not None:
173 msg('using _sigchld')
174 installHandler = _extInstallHandler
175 isDefaultHandler = _extIsDefaultHandler
176 elif signal is not None:
177 msg('using signal module')
178 installHandler = _installHandlerUsingSignal
179 isDefaultHandler = _isDefaultHandler
181 msg('nothing unavailable')
182 installHandler = _cannotInstallHandler
183 isDefaultHandler = _cannotDetermineDefault