Adam Olsen added the comment:
version 2, adds to Doc/library/signal.rst. It also tweaks the
set_wakeup_fd's docstring.
I haven't verified that my formatting in signal.rst is correct.
Specifically, the '\0' should be checked.
Added file: http://bugs.python.org/file8916/python2.6-set_wakeup_fd2.diff
__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1583>
__________________________________
Index: Doc/library/signal.rst
===================================================================
--- Doc/library/signal.rst (revision 59455)
+++ Doc/library/signal.rst (working copy)
@@ -110,6 +110,20 @@
:manpage:`signal(2)`.)
+.. function:: set_wakeup_fd(fd)
+
+ Set the wakeup fd to *fd*. When a signal is received, a `'\0'` byte is written
+ to the fd. This can be used by a library to wakeup a poll or select call,
+ allowing the signal to be fully processed.
+
+ The old wakeup fd is returned. *fd* must be non-blocking. It is up to the
+ library to remove any bytes before calling poll or select again.
+
+ When threads are enabled, this function can only be called from the main thread;
+ attempting to call it from other threads will cause a :exc:`ValueError`
+ exception to be raised.
+
+
.. function:: signal(signalnum, handler)
Set the handler for signal *signalnum* to the function *handler*. *handler* can
Index: Lib/test/test_signal.py
===================================================================
--- Lib/test/test_signal.py (revision 59455)
+++ Lib/test/test_signal.py (working copy)
@@ -165,12 +165,59 @@
self.assertRaises(TypeError, signal.signal,
signal.SIGUSR1, None)
+class WakeupSignalTests(unittest.TestCase):
+ TIMEOUT_FULL = 10
+ TIMEOUT_HALF = 5
+
+ def test_wakeup_fd_early(self):
+ import select
+
+ signal.alarm(1)
+ before_time = time.time()
+ # We attempt to get a signal during the sleep,
+ # before select is called
+ time.sleep(self.TIMEOUT_FULL)
+ mid_time = time.time()
+ self.assert_(mid_time - before_time < self.TIMEOUT_HALF)
+ select.select([self.read], [], [], self.TIMEOUT_FULL)
+ after_time = time.time()
+ self.assert_(after_time - mid_time < self.TIMEOUT_HALF)
+
+ def test_wakeup_fd_during(self):
+ import select
+
+ signal.alarm(1)
+ before_time = time.time()
+ # We attempt to get a signal during the select call
+ self.assertRaises(select.error, select.select,
+ [self.read], [], [], self.TIMEOUT_FULL)
+ after_time = time.time()
+ self.assert_(after_time - before_time < self.TIMEOUT_HALF)
+
+ def setUp(self):
+ import fcntl
+
+ self.alrm = signal.signal(signal.SIGALRM, lambda x,y:None)
+ self.read, self.write = os.pipe()
+ flags = fcntl.fcntl(self.write, fcntl.F_GETFL, 0)
+ flags = flags | os.O_NONBLOCK
+ fcntl.fcntl(self.write, fcntl.F_SETFL, flags)
+ self.old_wakeup = signal.set_wakeup_fd(self.write)
+
+ def tearDown(self):
+ signal.set_wakeup_fd(self.old_wakeup)
+ os.close(self.read)
+ os.close(self.write)
+ signal.signal(signal.SIGALRM, self.alrm)
+
+
def test_main():
if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos':
raise test_support.TestSkipped("Can't test signal on %s" % \
sys.platform)
- test_support.run_unittest(BasicSignalTests, InterProcessSignalTests)
+ test_support.run_unittest(BasicSignalTests, InterProcessSignalTests,
+ WakeupSignalTests)
if __name__ == "__main__":
Index: Modules/signalmodule.c
===================================================================
--- Modules/signalmodule.c (revision 59455)
+++ Modules/signalmodule.c (working copy)
@@ -12,6 +12,8 @@
#include <signal.h>
+#include <sys/stat.h>
+
#ifndef SIG_ERR
#define SIG_ERR ((PyOS_sighandler_t)(-1))
#endif
@@ -75,6 +77,8 @@
PyObject *func;
} Handlers[NSIG];
+static sig_atomic_t wakeup_fd = -1;
+
static int is_tripped = 0; /* Speed up sigcheck() when none tripped */
static PyObject *DefaultHandler;
@@ -112,6 +116,7 @@
static void
signal_handler(int sig_num)
{
+ const char dummy_byte = '\0';
#ifdef WITH_THREAD
#ifdef WITH_PTH
if (PyThread_get_thread_ident() != main_thread) {
@@ -125,6 +130,8 @@
is_tripped++;
Handlers[sig_num].tripped = 1;
Py_AddPendingCall(checksignals_witharg, NULL);
+ if (wakeup_fd != -1)
+ write(wakeup_fd, &dummy_byte, 1);
#ifdef WITH_THREAD
}
#endif
@@ -264,6 +271,39 @@
anything else -- the callable Python object used as a handler");
+static PyObject *
+signal_set_wakeup_fd(PyObject *self, PyObject *args)
+{
+ struct stat buf;
+ int fd, old_fd;
+ if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd))
+ return NULL;
+#ifdef WITH_THREAD
+ if (PyThread_get_thread_ident() != main_thread) {
+ PyErr_SetString(PyExc_ValueError,
+ "set_wakeup_fd only works in main thread");
+ return NULL;
+ }
+#endif
+ if (fd != -1 && fstat(fd, &buf) != 0) {
+ PyErr_SetString(PyExc_ValueError, "invalid fd");
+ return NULL;
+ }
+ old_fd = wakeup_fd;
+ wakeup_fd = fd;
+ return PyLong_FromLong(old_fd);
+}
+
+PyDoc_STRVAR(set_wakeup_fd_doc,
+"set_wakeup_fd(fd) -> fd\n\
+\n\
+Sets the fd to be written to (with '\\0') when a signal\n\
+comes in. A library can use this to wakeup select or poll.\n\
+The previous fd is returned.\n\
+\n\
+The fd must be non-blocking.");
+
+
/* List of functions defined in the module */
static PyMethodDef signal_methods[] = {
#ifdef HAVE_ALARM
@@ -271,6 +311,7 @@
#endif
{"signal", signal_signal, METH_VARARGS, signal_doc},
{"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc},
+ {"set_wakeup_fd", signal_set_wakeup_fd, METH_VARARGS, set_wakeup_fd_doc},
#ifdef HAVE_PAUSE
{"pause", (PyCFunction)signal_pause,
METH_NOARGS,pause_doc},
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com