diff --git a/vendor/Twisted-10.0.0/twisted/internet/_sigchld.c b/vendor/Twisted-10.0.0/twisted/internet/_sigchld.c new file mode 100644 index 00000000..660182bd --- /dev/null +++ b/vendor/Twisted-10.0.0/twisted/internet/_sigchld.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010 Twisted Matrix Laboratories. + * See LICENSE for details. + */ + +#include +#include + +#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); +} diff --git a/vendor/Twisted-10.0.0/twisted/internet/_signals.py b/vendor/Twisted-10.0.0/twisted/internet/_signals.py new file mode 100644 index 00000000..faf2e244 --- /dev/null +++ b/vendor/Twisted-10.0.0/twisted/internet/_signals.py @@ -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}. + +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 + diff --git a/vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py b/vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py new file mode 100644 index 00000000..b7e49212 --- /dev/null +++ b/vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py @@ -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) diff --git a/vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix b/vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix new file mode 100644 index 00000000..22d80032 --- /dev/null +++ b/vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix @@ -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).