A few missing files from the twisted patch
This commit is contained in:
101
vendor/Twisted-10.0.0/twisted/internet/_sigchld.c
vendored
Normal file
101
vendor/Twisted-10.0.0/twisted/internet/_sigchld.c
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Twisted Matrix Laboratories.
|
||||
* See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
static int sigchld_pipe_fd = -1;
|
||||
|
||||
static void got_signal(int sig) {
|
||||
int saved_errno = errno;
|
||||
int ignored_result;
|
||||
|
||||
/* write() errors are unhandled. If the buffer is full, we don't
|
||||
* care. What about other errors? */
|
||||
ignored_result = write(sigchld_pipe_fd, "x", 1);
|
||||
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(install_sigchld_handler_doc, "\
|
||||
install_sigchld_handler(fd)\n\
|
||||
\n\
|
||||
Installs a SIGCHLD handler which will write a byte to the given fd\n\
|
||||
whenever a SIGCHLD occurs. This is done in C code because the python\n\
|
||||
signal handling system is not reliable, and additionally cannot\n\
|
||||
specify SA_RESTART.\n\
|
||||
\n\
|
||||
Please ensure fd is in non-blocking mode.\n\
|
||||
");
|
||||
|
||||
static PyObject *
|
||||
install_sigchld_handler(PyObject *self, PyObject *args) {
|
||||
int fd, old_fd;
|
||||
struct sigaction sa;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i:install_sigchld_handler", &fd)) {
|
||||
return NULL;
|
||||
}
|
||||
old_fd = sigchld_pipe_fd;
|
||||
sigchld_pipe_fd = fd;
|
||||
|
||||
if (fd == -1) {
|
||||
sa.sa_handler = SIG_DFL;
|
||||
} else {
|
||||
sa.sa_handler = got_signal;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
/* mask all signals so I don't worry about EINTR from the write. */
|
||||
sigfillset(&sa.sa_mask);
|
||||
}
|
||||
if (sigaction(SIGCHLD, &sa, 0) != 0) {
|
||||
sigchld_pipe_fd = old_fd;
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
}
|
||||
return PyLong_FromLong(old_fd);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(is_default_handler_doc, "\
|
||||
Return 1 if the SIGCHLD handler is SIG_DFL, 0 otherwise.\n\
|
||||
");
|
||||
|
||||
static PyObject *
|
||||
is_default_handler(PyObject *self, PyObject *args) {
|
||||
/*
|
||||
* This implementation is necessary since the install_sigchld_handler
|
||||
* function above bypasses the Python signal handler installation API, so
|
||||
* CPython doesn't notice that the handler has changed and signal.getsignal
|
||||
* won't return an accurate result.
|
||||
*/
|
||||
struct sigaction sa;
|
||||
|
||||
if (sigaction(SIGCHLD, NULL, &sa) != 0) {
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
}
|
||||
|
||||
return PyLong_FromLong(sa.sa_handler == SIG_DFL);
|
||||
}
|
||||
|
||||
static PyMethodDef sigchld_methods[] = {
|
||||
{"installHandler", install_sigchld_handler, METH_VARARGS,
|
||||
install_sigchld_handler_doc},
|
||||
{"isDefaultHandler", is_default_handler, METH_NOARGS,
|
||||
is_default_handler_doc},
|
||||
/* sentinel */
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
||||
static const char _sigchld_doc[] = "\n\
|
||||
This module contains an API for receiving SIGCHLD via a file descriptor.\n\
|
||||
";
|
||||
|
||||
PyMODINIT_FUNC
|
||||
init_sigchld(void) {
|
||||
/* Create the module and add the functions */
|
||||
Py_InitModule3(
|
||||
"twisted.internet._sigchld", sigchld_methods, _sigchld_doc);
|
||||
}
|
184
vendor/Twisted-10.0.0/twisted/internet/_signals.py
vendored
Normal file
184
vendor/Twisted-10.0.0/twisted/internet/_signals.py
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
# -*- test-case-name: twisted.test.test_process,twisted.internet.test.test_process -*-
|
||||
# Copyright (c) 2010 Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
This module provides a uniform interface to the several mechanisms which are
|
||||
possibly available for dealing with signals.
|
||||
|
||||
This module is used to integrate child process termination into a
|
||||
reactor event loop. This is a challenging feature to provide because
|
||||
most platforms indicate process termination via SIGCHLD and do not
|
||||
provide a way to wait for that signal and arbitrary I/O events at the
|
||||
same time. The naive implementation involves installing a Python
|
||||
SIGCHLD handler; unfortunately this leads to other syscalls being
|
||||
interrupted (whenever SIGCHLD is received) and failing with EINTR
|
||||
(which almost no one is prepared to handle). This interruption can be
|
||||
disabled via siginterrupt(2) (or one of the equivalent mechanisms);
|
||||
however, if the SIGCHLD is delivered by the platform to a non-main
|
||||
thread (not a common occurrence, but difficult to prove impossible),
|
||||
the main thread (waiting on select() or another event notification
|
||||
API) may not wake up leading to an arbitrary delay before the child
|
||||
termination is noticed.
|
||||
|
||||
The basic solution to all these issues involves enabling SA_RESTART
|
||||
(ie, disabling system call interruption) and registering a C signal
|
||||
handler which writes a byte to a pipe. The other end of the pipe is
|
||||
registered with the event loop, allowing it to wake up shortly after
|
||||
SIGCHLD is received. See L{twisted.internet.posixbase._SIGCHLDWaker}
|
||||
for the implementation of the event loop side of this solution. The
|
||||
use of a pipe this way is known as the U{self-pipe
|
||||
trick<http://cr.yp.to/docs/selfpipe.html>}.
|
||||
|
||||
The actual solution implemented in this module depends on the version
|
||||
of Python. From version 2.6, C{signal.siginterrupt} and
|
||||
C{signal.set_wakeup_fd} allow the necessary C signal handler which
|
||||
writes to the pipe to be registered with C{SA_RESTART}. Prior to 2.6,
|
||||
the L{twisted.internet._sigchld} extension module provides similar
|
||||
functionality.
|
||||
|
||||
If neither of these is available, a Python signal handler is used
|
||||
instead. This is essentially the naive solution mentioned above and
|
||||
has the problems described there.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
try:
|
||||
from signal import set_wakeup_fd, siginterrupt
|
||||
except ImportError:
|
||||
set_wakeup_fd = siginterrupt = None
|
||||
|
||||
try:
|
||||
import signal
|
||||
except ImportError:
|
||||
signal = None
|
||||
|
||||
from twisted.python.log import msg
|
||||
|
||||
try:
|
||||
from twisted.internet._sigchld import installHandler as _extInstallHandler, \
|
||||
isDefaultHandler as _extIsDefaultHandler
|
||||
except ImportError:
|
||||
_extInstallHandler = _extIsDefaultHandler = None
|
||||
|
||||
|
||||
class _Handler(object):
|
||||
"""
|
||||
L{_Handler} is a signal handler which writes a byte to a file descriptor
|
||||
whenever it is invoked.
|
||||
|
||||
@ivar fd: The file descriptor to which to write. If this is C{None},
|
||||
nothing will be written.
|
||||
"""
|
||||
def __init__(self, fd):
|
||||
self.fd = fd
|
||||
|
||||
|
||||
def __call__(self, *args):
|
||||
"""
|
||||
L{_Handler.__call__} is the signal handler. It will write a byte to
|
||||
the wrapped file descriptor, if there is one.
|
||||
"""
|
||||
if self.fd is not None:
|
||||
try:
|
||||
os.write(self.fd, '\0')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def _installHandlerUsingSignal(fd):
|
||||
"""
|
||||
Install a signal handler which will write a byte to C{fd} when
|
||||
I{SIGCHLD} is received.
|
||||
|
||||
This is implemented by creating an instance of L{_Handler} with C{fd}
|
||||
and installing it as the signal handler.
|
||||
|
||||
@param fd: The file descriptor to which to write when I{SIGCHLD} is
|
||||
received.
|
||||
@type fd: C{int}
|
||||
"""
|
||||
if fd == -1:
|
||||
previous = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
else:
|
||||
previous = signal.signal(signal.SIGCHLD, _Handler(fd))
|
||||
if isinstance(previous, _Handler):
|
||||
return previous.fd
|
||||
return -1
|
||||
|
||||
|
||||
|
||||
def _installHandlerUsingSetWakeup(fd):
|
||||
"""
|
||||
Install a signal handler which will write a byte to C{fd} when
|
||||
I{SIGCHLD} is received.
|
||||
|
||||
This is implemented by installing an instance of L{_Handler} wrapped
|
||||
around C{None}, setting the I{SIGCHLD} handler as not allowed to
|
||||
interrupt system calls, and using L{signal.set_wakeup_fd} to do the
|
||||
actual writing.
|
||||
|
||||
@param fd: The file descriptor to which to write when I{SIGCHLD} is
|
||||
received.
|
||||
@type fd: C{int}
|
||||
"""
|
||||
if fd == -1:
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
else:
|
||||
signal.signal(signal.SIGCHLD, _Handler(None))
|
||||
siginterrupt(signal.SIGCHLD, False)
|
||||
return set_wakeup_fd(fd)
|
||||
|
||||
|
||||
|
||||
def _isDefaultHandler():
|
||||
"""
|
||||
Determine whether the I{SIGCHLD} handler is the default or not.
|
||||
"""
|
||||
return signal.getsignal(signal.SIGCHLD) == signal.SIG_DFL
|
||||
|
||||
|
||||
|
||||
def _cannotInstallHandler(fd):
|
||||
"""
|
||||
Fail to install a signal handler for I{SIGCHLD}.
|
||||
|
||||
This implementation is used when the supporting code for the other
|
||||
implementations is unavailable (on Python versions 2.5 and older where
|
||||
neither the L{twisted.internet._sigchld} extension nor the standard
|
||||
L{signal} module is available).
|
||||
|
||||
@param fd: Ignored; only for compatibility with the other
|
||||
implementations of this interface.
|
||||
|
||||
@raise RuntimeError: Always raised to indicate no I{SIGCHLD} handler can
|
||||
be installed.
|
||||
"""
|
||||
raise RuntimeError("Cannot install a SIGCHLD handler")
|
||||
|
||||
|
||||
|
||||
def _cannotDetermineDefault():
|
||||
raise RuntimeError("No usable signal API available")
|
||||
|
||||
|
||||
|
||||
if set_wakeup_fd is not None:
|
||||
msg('using set_wakeup_fd')
|
||||
installHandler = _installHandlerUsingSetWakeup
|
||||
isDefaultHandler = _isDefaultHandler
|
||||
elif _extInstallHandler is not None:
|
||||
msg('using _sigchld')
|
||||
installHandler = _extInstallHandler
|
||||
isDefaultHandler = _extIsDefaultHandler
|
||||
elif signal is not None:
|
||||
msg('using signal module')
|
||||
installHandler = _installHandlerUsingSignal
|
||||
isDefaultHandler = _isDefaultHandler
|
||||
else:
|
||||
msg('nothing unavailable')
|
||||
installHandler = _cannotInstallHandler
|
||||
isDefaultHandler = _cannotDetermineDefault
|
||||
|
194
vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py
vendored
Normal file
194
vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
# Copyright (c) 2010 Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.internet._sigchld}, an alternate, superior SIGCHLD
|
||||
monitoring API.
|
||||
"""
|
||||
|
||||
import os, signal, errno
|
||||
|
||||
from twisted.python.log import msg
|
||||
from twisted.trial.unittest import TestCase
|
||||
from twisted.internet.fdesc import setNonBlocking
|
||||
from twisted.internet._signals import installHandler, isDefaultHandler
|
||||
from twisted.internet._signals import _extInstallHandler, _extIsDefaultHandler
|
||||
from twisted.internet._signals import _installHandlerUsingSetWakeup, \
|
||||
_installHandlerUsingSignal, _isDefaultHandler
|
||||
|
||||
|
||||
class SIGCHLDTestsMixin:
|
||||
"""
|
||||
Mixin for L{TestCase} subclasses which defines several tests for
|
||||
I{installHandler} and I{isDefaultHandler}. Subclasses are expected to
|
||||
define C{self.installHandler} and C{self.isDefaultHandler} to invoke the
|
||||
implementation to be tested.
|
||||
"""
|
||||
|
||||
if getattr(signal, 'SIGCHLD', None) is None:
|
||||
skip = "Platform does not have SIGCHLD"
|
||||
|
||||
def installHandler(self, fd):
|
||||
"""
|
||||
Override in a subclass to install a SIGCHLD handler which writes a byte
|
||||
to the given file descriptor. Return the previously registered file
|
||||
descriptor.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def isDefaultHandler(self):
|
||||
"""
|
||||
Override in a subclass to determine if the current SIGCHLD handler is
|
||||
SIG_DFL or not. Return True if it is SIG_DFL, False otherwise.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def pipe(self):
|
||||
"""
|
||||
Create a non-blocking pipe which will be closed after the currently
|
||||
running test.
|
||||
"""
|
||||
read, write = os.pipe()
|
||||
self.addCleanup(os.close, read)
|
||||
self.addCleanup(os.close, write)
|
||||
setNonBlocking(read)
|
||||
setNonBlocking(write)
|
||||
return read, write
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Save the current SIGCHLD handler as reported by L{signal.signal} and
|
||||
the current file descriptor registered with L{installHandler}.
|
||||
"""
|
||||
handler = signal.getsignal(signal.SIGCHLD)
|
||||
if handler != signal.SIG_DFL:
|
||||
self.signalModuleHandler = handler
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
else:
|
||||
self.signalModuleHandler = None
|
||||
|
||||
self.oldFD = self.installHandler(-1)
|
||||
|
||||
if self.signalModuleHandler is not None and self.oldFD != -1:
|
||||
msg("SIGCHLD setup issue: %r %r" % (self.signalModuleHandler, self.oldFD))
|
||||
raise RuntimeError("You used some signal APIs wrong! Try again.")
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Restore whatever signal handler was present when setUp ran.
|
||||
"""
|
||||
# If tests set up any kind of handlers, clear them out.
|
||||
self.installHandler(-1)
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
|
||||
# Now restore whatever the setup was before the test ran.
|
||||
if self.signalModuleHandler is not None:
|
||||
signal.signal(signal.SIGCHLD, self.signalModuleHandler)
|
||||
elif self.oldFD != -1:
|
||||
self.installHandler(self.oldFD)
|
||||
|
||||
|
||||
def test_isDefaultHandler(self):
|
||||
"""
|
||||
L{isDefaultHandler} returns true if the SIGCHLD handler is SIG_DFL,
|
||||
false otherwise.
|
||||
"""
|
||||
self.assertTrue(self.isDefaultHandler())
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
|
||||
self.assertFalse(self.isDefaultHandler())
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
self.assertTrue(self.isDefaultHandler())
|
||||
signal.signal(signal.SIGCHLD, lambda *args: None)
|
||||
self.assertFalse(self.isDefaultHandler())
|
||||
|
||||
|
||||
def test_returnOldFD(self):
|
||||
"""
|
||||
L{installHandler} returns the previously registered file descriptor.
|
||||
"""
|
||||
read, write = self.pipe()
|
||||
oldFD = self.installHandler(write)
|
||||
self.assertEqual(self.installHandler(oldFD), write)
|
||||
|
||||
|
||||
def test_uninstallHandler(self):
|
||||
"""
|
||||
C{installHandler(-1)} removes the SIGCHLD handler completely.
|
||||
"""
|
||||
read, write = self.pipe()
|
||||
self.assertTrue(self.isDefaultHandler())
|
||||
self.installHandler(write)
|
||||
self.assertFalse(self.isDefaultHandler())
|
||||
self.installHandler(-1)
|
||||
self.assertTrue(self.isDefaultHandler())
|
||||
|
||||
|
||||
def test_installHandler(self):
|
||||
"""
|
||||
The file descriptor passed to L{installHandler} has a byte written to
|
||||
it when SIGCHLD is delivered to the process.
|
||||
"""
|
||||
read, write = self.pipe()
|
||||
self.installHandler(write)
|
||||
|
||||
exc = self.assertRaises(OSError, os.read, read, 1)
|
||||
self.assertEqual(exc.errno, errno.EAGAIN)
|
||||
|
||||
os.kill(os.getpid(), signal.SIGCHLD)
|
||||
|
||||
self.assertEqual(len(os.read(read, 5)), 1)
|
||||
|
||||
|
||||
|
||||
class DefaultSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||
"""
|
||||
Tests for whatever implementation is selected for the L{installHandler}
|
||||
and L{isDefaultHandler} APIs.
|
||||
"""
|
||||
installHandler = staticmethod(installHandler)
|
||||
isDefaultHandler = staticmethod(isDefaultHandler)
|
||||
|
||||
|
||||
|
||||
class ExtensionSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||
"""
|
||||
Tests for the L{twisted.internet._sigchld} implementation of the
|
||||
L{installHandler} and L{isDefaultHandler} APIs.
|
||||
"""
|
||||
try:
|
||||
import twisted.internet._sigchld
|
||||
except ImportError:
|
||||
skip = "twisted.internet._sigchld is not available"
|
||||
|
||||
installHandler = _extInstallHandler
|
||||
isDefaultHandler = _extIsDefaultHandler
|
||||
|
||||
|
||||
|
||||
class SetWakeupSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||
"""
|
||||
Tests for the L{signal.set_wakeup_fd} implementation of the
|
||||
L{installHandler} and L{isDefaultHandler} APIs.
|
||||
"""
|
||||
# Check both of these. On Ubuntu 9.10 (to take an example completely at
|
||||
# random), Python 2.5 has set_wakeup_fd but not siginterrupt.
|
||||
if (getattr(signal, 'set_wakeup_fd', None) is None
|
||||
or getattr(signal, 'siginterrupt', None) is None):
|
||||
skip = "signal.set_wakeup_fd is not available"
|
||||
|
||||
installHandler = staticmethod(_installHandlerUsingSetWakeup)
|
||||
isDefaultHandler = staticmethod(_isDefaultHandler)
|
||||
|
||||
|
||||
|
||||
class PlainSignalModuleSIGCHLDTests(SIGCHLDTestsMixin, TestCase):
|
||||
"""
|
||||
Tests for the L{signal.signal} implementation of the L{installHandler}
|
||||
and L{isDefaultHandler} APIs.
|
||||
"""
|
||||
installHandler = staticmethod(_installHandlerUsingSignal)
|
||||
isDefaultHandler = staticmethod(_isDefaultHandler)
|
4
vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix
vendored
Normal file
4
vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
On POSIX platforms, reactors now support child processes in a way
|
||||
which doesn't cause other syscalls to sometimes fail with EINTR (if
|
||||
running on Python 2.6 or if Twisted's extension modules have been
|
||||
built).
|
Reference in New Issue
Block a user