changeset: 91728:a0564da6181c tag: tip user: Victor Stinner date: Sun Jul 20 13:05:54 2014 +0200 files: Lib/test/test_signal.py Modules/_testcapimodule.c Modules/signalmodule.c PCbuild/pythoncore.vcxproj description: signal.set_wakeup_fd() now accepts a socket diff -r f83adc06f486 -r a0564da6181c Lib/test/test_signal.py --- a/Lib/test/test_signal.py Fri Jul 18 09:14:55 2014 -0500 +++ b/Lib/test/test_signal.py Sun Jul 20 13:05:54 2014 +0200 @@ -416,6 +416,44 @@ class WakeupSignalTests(unittest.TestCas signal.pthread_sigmask(signal.SIG_UNBLOCK, (signum1, signum2)) """, signal.SIGUSR1, signal.SIGUSR2, ordered=False) + def test_socket(self): + # use a subprocess to have only one thread + code = """if 1: + import os + import signal + import socket + import struct + import _testcapi + + signum = signal.SIGINT + signals = (signum,) + + def handler(signum, frame): + pass + + signal.signal(signum, handler) + + read, write = socket.socketpair() + read.setblocking(False) + write.setblocking(False) + signal.set_wakeup_fd(write.fileno()) + + _testcapi.raise_signal(signum) + + data = read.recv(1) + if not data: + raise Exception("no signum written") + raised = struct.unpack('B', data) + if raised != signals: + raise Exception("%r != %r" % (raised, signals)) + + read.close() + write.close() + """ + support.import_module('_testcapi') + + assert_python_ok('-c', code) + @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class SiginterruptTest(unittest.TestCase): diff -r f83adc06f486 -r a0564da6181c Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Fri Jul 18 09:14:55 2014 -0500 +++ b/Modules/_testcapimodule.c Sun Jul 20 13:05:54 2014 +0200 @@ -11,6 +11,7 @@ #include #include "structmember.h" #include "datetime.h" +#include #ifdef WITH_THREAD #include "pythread.h" @@ -3063,6 +3064,21 @@ exit: } #endif /* WITH_THREAD */ +static PyObject* +test_raise_signal(PyObject* self, PyObject *args) +{ + int signum, err; + + if (PyArg_ParseTuple(args, "i:raise_signal", &signum) < 0) + return NULL; + + err = raise(signum); + if (err) + return PyErr_SetFromErrno(PyExc_OSError); + + Py_RETURN_NONE; +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, @@ -3198,6 +3214,8 @@ static PyMethodDef TestMethods[] = { {"docstring_with_signature_with_defaults", (PyCFunction)test_with_docstring, METH_NOARGS, docstring_with_signature_with_defaults}, + {"raise_signal", + (PyCFunction)test_raise_signal, METH_VARARGS}, #ifdef WITH_THREAD {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O, PyDoc_STR("set_error_class(error_class) -> None")}, diff -r f83adc06f486 -r a0564da6181c Modules/signalmodule.c --- a/Modules/signalmodule.c Fri Jul 18 09:14:55 2014 -0500 +++ b/Modules/signalmodule.c Sun Jul 20 13:05:54 2014 +0200 @@ -15,6 +15,10 @@ #endif #endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + #ifdef HAVE_SIGNAL_H #include #endif @@ -88,6 +92,7 @@ static volatile struct { } Handlers[NSIG]; static volatile sig_atomic_t wakeup_fd = -1; +static volatile sig_atomic_t wakeup_use_socket = 0; /* Speed up sigcheck() when none tripped */ static volatile sig_atomic_t is_tripped = 0; @@ -188,21 +193,30 @@ static void trip_signal(int sig_num) { unsigned char byte; - int rc = 0; + Py_ssize_t rc = 0; Handlers[sig_num].tripped = 1; if (wakeup_fd != -1) { byte = (unsigned char)sig_num; - while ((rc = write(wakeup_fd, &byte, 1)) == -1 && errno == EINTR); - if (rc == -1) - Py_AddPendingCall(report_wakeup_error, (void *) (Py_intptr_t) errno); + if (wakeup_use_socket) { + do { + rc = send(wakeup_fd, &byte, 1, 0); + } while (rc < 0 && errno == EINTR); + } + else { + do { + rc = write(wakeup_fd, &byte, 1); + } while (rc < 0 && errno == EINTR); + } + if (rc < 0) + Py_AddPendingCall(report_wakeup_error, (void *)(Py_intptr_t)errno); } - if (is_tripped) - return; - /* Set is_tripped after setting .tripped, as it gets - cleared in PyErr_CheckSignals() before .tripped. */ - is_tripped = 1; - Py_AddPendingCall(checksignals_witharg, NULL); + if (!is_tripped) { + /* Set is_tripped after setting .tripped, as it gets + cleared in PyErr_CheckSignals() before .tripped. */ + is_tripped = 1; + Py_AddPendingCall(checksignals_witharg, NULL); + } } static void @@ -423,26 +437,81 @@ signal_siginterrupt(PyObject *self, PyOb #endif -static PyObject * -signal_set_wakeup_fd(PyObject *self, PyObject *args) +static int +set_wakeup_fd(int fd, int *old_fd) { - struct stat buf; - int fd, old_fd; - if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd)) - return NULL; + struct stat st; + int is_socket; +#ifdef MS_WINDOWS + int res; + int res_size = sizeof res; + PyObject *mod; +#endif + #ifdef WITH_THREAD if (PyThread_get_thread_ident() != main_thread) { PyErr_SetString(PyExc_ValueError, "set_wakeup_fd only works in main thread"); - return NULL; + return -1; } #endif - if (fd != -1 && (!_PyVerify_fd(fd) || fstat(fd, &buf) != 0)) { - PyErr_SetString(PyExc_ValueError, "invalid fd"); + + is_socket = 0; + if (fd != -1) { +#ifdef MS_WINDOWS + /* Import the _socket module to call WSAStartup() */ + mod = PyImport_ImportModuleNoBlock("_socket"); + if (mod == NULL) + return -1; + Py_DECREF(mod); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, + (char *)&res, &res_size) != 0) { + if (WSAGetLastError() != WSAENOTSOCK) { + PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError()); + return -1; + } + + if (!_PyVerify_fd(fd)) { + PyErr_SetString(PyExc_ValueError, "invalid fd"); + return -1; + } + + if (fstat(fd, &st) != 0) { + PyErr_SetString(PyExc_ValueError, "invalid fd"); + return -1; + } + + is_socket = 0; + } + else + is_socket = 1; +#else + if (fstat(fd, &st) != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + is_socket = S_ISSOCK(st.st_mode); +#endif + } + + *old_fd = wakeup_fd; + wakeup_fd = fd; + wakeup_use_socket = is_socket; + return 0; +} + +static PyObject * +signal_set_wakeup_fd(PyObject *self, PyObject *args) +{ + int fd, old_fd; + + if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd)) return NULL; - } - old_fd = wakeup_fd; - wakeup_fd = fd; + + if (set_wakeup_fd(fd, &old_fd) < 0) + return NULL; + return PyLong_FromLong(old_fd); } @@ -459,10 +528,12 @@ The fd must be non-blocking."); int PySignal_SetWakeupFd(int fd) { - int old_fd = wakeup_fd; - if (fd < 0) - fd = -1; - wakeup_fd = fd; + int old_fd; + + if (set_wakeup_fd(fd, &old_fd) < 0) { + PyErr_Clear(); + return -2; + } return old_fd; } diff -r f83adc06f486 -r a0564da6181c PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj Fri Jul 18 09:14:55 2014 -0500 +++ b/PCbuild/pythoncore.vcxproj Sun Jul 20 13:05:54 2014 +0200 @@ -176,7 +176,7 @@ "$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) $(OutDir)$(PyDllName).dll libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 @@ -212,7 +212,7 @@ IF %ERRORLEVEL% NEQ 0 ( "$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 @@ -247,7 +247,7 @@ IF %ERRORLEVEL% NEQ 0 ( "$(SolutionDir)make_buildinfo.exe" Debug "$(IntDir)" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 @@ -285,7 +285,7 @@ IF %ERRORLEVEL% NEQ 0 ( "$(SolutionDir)make_buildinfo.exe" Debug "$(IntDir)" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 @@ -317,7 +317,7 @@ IF %ERRORLEVEL% NEQ 0 ( "$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) $(OutDir)$(PyDllName).dll libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 @@ -353,7 +353,7 @@ IF %ERRORLEVEL% NEQ 0 ( "$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 MachineX64 @@ -386,7 +386,7 @@ IF %ERRORLEVEL% NEQ 0 ( "$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) $(OutDir)$(PyDllName).dll libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 @@ -422,7 +422,7 @@ IF %ERRORLEVEL% NEQ 0 ( "$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\" - $(IntDir)getbuildinfo.o;%(AdditionalDependencies) + $(IntDir)getbuildinfo.o;ws2_32.lib;%(AdditionalDependencies) libc;%(IgnoreSpecificDefaultLibraries) 0x1e000000 MachineX64