Imported Upstream version 12.1.0
[contrib/python-twisted.git] / twisted / internet / _signals.py
1 # -*- test-case-name: twisted.test.test_process,twisted.internet.test.test_process -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 This module provides a uniform interface to the several mechanisms which are
7 possibly available for dealing with signals.
8
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.
23
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>}.
32
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
38 functionality.
39
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.
43 """
44
45 import os
46
47 try:
48     from signal import set_wakeup_fd, siginterrupt
49 except ImportError:
50     set_wakeup_fd = siginterrupt = None
51
52 try:
53     import signal
54 except ImportError:
55     signal = None
56
57 from twisted.python.log import msg
58
59 try:
60     from twisted.internet._sigchld import installHandler as _extInstallHandler, \
61         isDefaultHandler as _extIsDefaultHandler
62 except ImportError:
63     _extInstallHandler = _extIsDefaultHandler = None
64
65
66 class _Handler(object):
67     """
68     L{_Handler} is a signal handler which writes a byte to a file descriptor
69     whenever it is invoked.
70
71     @ivar fd: The file descriptor to which to write.  If this is C{None},
72         nothing will be written.
73     """
74     def __init__(self, fd):
75         self.fd = fd
76
77
78     def __call__(self, *args):
79         """
80         L{_Handler.__call__} is the signal handler.  It will write a byte to
81         the wrapped file descriptor, if there is one.
82         """
83         if self.fd is not None:
84             try:
85                 os.write(self.fd, '\0')
86             except:
87                 pass
88
89
90
91 def _installHandlerUsingSignal(fd):
92     """
93     Install a signal handler which will write a byte to C{fd} when
94     I{SIGCHLD} is received.
95
96     This is implemented by creating an instance of L{_Handler} with C{fd}
97     and installing it as the signal handler.
98
99     @param fd: The file descriptor to which to write when I{SIGCHLD} is
100         received.
101     @type fd: C{int}
102     """
103     if fd == -1:
104         previous = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
105     else:
106         previous = signal.signal(signal.SIGCHLD, _Handler(fd))
107     if isinstance(previous, _Handler):
108         return previous.fd
109     return -1
110
111
112
113 def _installHandlerUsingSetWakeup(fd):
114     """
115     Install a signal handler which will write a byte to C{fd} when
116     I{SIGCHLD} is received.
117
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
121     actual writing.
122
123     @param fd: The file descriptor to which to write when I{SIGCHLD} is
124         received.
125     @type fd: C{int}
126     """
127     if fd == -1:
128         signal.signal(signal.SIGCHLD, signal.SIG_DFL)
129     else:
130         signal.signal(signal.SIGCHLD, _Handler(None))
131         siginterrupt(signal.SIGCHLD, False)
132     return set_wakeup_fd(fd)
133
134
135
136 def _isDefaultHandler():
137     """
138     Determine whether the I{SIGCHLD} handler is the default or not.
139     """
140     return signal.getsignal(signal.SIGCHLD) == signal.SIG_DFL
141
142
143
144 def _cannotInstallHandler(fd):
145     """
146     Fail to install a signal handler for I{SIGCHLD}.
147
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).
152
153     @param fd: Ignored; only for compatibility with the other
154         implementations of this interface.
155
156     @raise RuntimeError: Always raised to indicate no I{SIGCHLD} handler can
157         be installed.
158     """
159     raise RuntimeError("Cannot install a SIGCHLD handler")
160
161
162
163 def _cannotDetermineDefault():
164     raise RuntimeError("No usable signal API available")
165
166
167
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
180 else:
181     msg('nothing unavailable')
182     installHandler = _cannotInstallHandler
183     isDefaultHandler = _cannotDetermineDefault
184