summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2010-06-24 04:12:01 +0100
committerandy <github@anarkystic.com>2010-06-24 04:12:01 +0100
commit8767608e335752fea4be6bebd6298bb49782f6d6 (patch)
treeeeffb73104b5436b3fa7a471431accdf1ff0173a
parent59bfa3a64d46b8a625e3b288f50a2f521c19e329 (diff)
A few missing files from the twisted patch
-rw-r--r--vendor/Twisted-10.0.0/twisted/internet/_sigchld.c101
-rw-r--r--vendor/Twisted-10.0.0/twisted/internet/_signals.py184
-rw-r--r--vendor/Twisted-10.0.0/twisted/internet/test/test_sigchld.py194
-rw-r--r--vendor/Twisted-10.0.0/twisted/topfiles/733.bugfix4
4 files changed, 483 insertions, 0 deletions
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 000000000..660182bd2
--- /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 <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);
+}
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 000000000..faf2e2445
--- /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<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
+
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 000000000..b7e492127
--- /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 000000000..22d80032c
--- /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).