diff options
| author | Victor Stinner <victor.stinner@gmail.com> | 2014-07-29 21:31:34 (GMT) | 
|---|---|---|
| committer | Victor Stinner <victor.stinner@gmail.com> | 2014-07-29 21:31:34 (GMT) | 
| commit | 115171086a5ded14a2bc2519e7f774a755e7ab04 (patch) | |
| tree | 18b06fd83c4738802f71d70e6368312a22826202 /Modules/signalmodule.c | |
| parent | c6aceeed29f5bcb238cc3ab0a1010f34648666cb (diff) | |
| download | cpython-115171086a5ded14a2bc2519e7f774a755e7ab04.zip cpython-115171086a5ded14a2bc2519e7f774a755e7ab04.tar.gz cpython-115171086a5ded14a2bc2519e7f774a755e7ab04.tar.bz2 | |
Issue #22018: On Windows, signal.set_wakeup_fd() now also supports sockets.
A side effect is that Python depends to the WinSock library.
Diffstat (limited to 'Modules/signalmodule.c')
| -rw-r--r-- | Modules/signalmodule.c | 183 | 
1 files changed, 165 insertions, 18 deletions
| diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index c4f0121..61b3330 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -7,6 +7,9 @@  #ifndef MS_WINDOWS  #include "posixmodule.h"  #endif +#ifdef MS_WINDOWS +#include "socketmodule.h"   /* needed for SOCKET_T */ +#endif  #ifdef MS_WINDOWS  #include <windows.h> @@ -87,7 +90,20 @@ static volatile struct {      PyObject *func;  } Handlers[NSIG]; +#ifdef MS_WINDOWS +#define INVALID_FD ((SOCKET_T)-1) + +static volatile struct { +    SOCKET_T fd; +    int use_send; +    int send_err_set; +    int send_errno; +    int send_win_error; +} wakeup = {INVALID_FD, 0, 0}; +#else +#define INVALID_FD (-1)  static volatile sig_atomic_t wakeup_fd = -1; +#endif  /* Speed up sigcheck() when none tripped */  static volatile sig_atomic_t is_tripped = 0; @@ -172,7 +188,7 @@ checksignals_witharg(void * unused)  }  static int -report_wakeup_error(void *data) +report_wakeup_write_error(void *data)  {      int save_errno = errno;      errno = (int) (Py_intptr_t) data; @@ -184,25 +200,86 @@ report_wakeup_error(void *data)      return 0;  } +#ifdef MS_WINDOWS +static int +report_wakeup_send_error(void* Py_UNUSED(data)) +{ +    PyObject *res; + +    if (wakeup.send_win_error) { +        /* PyErr_SetExcFromWindowsErr() invokes FormatMessage() which +           recognizes the error codes used by both GetLastError() and +           WSAGetLastError */ +        res = PyErr_SetExcFromWindowsErr(PyExc_OSError, wakeup.send_win_error); +    } +    else { +        errno = wakeup.send_errno; +        res = PyErr_SetFromErrno(PyExc_OSError); +    } + +    assert(res == NULL); +    wakeup.send_err_set = 0; + +    PySys_WriteStderr("Exception ignored when trying to send to the " +                      "signal wakeup fd:\n"); +    PyErr_WriteUnraisable(NULL); + +    return 0; +} +#endif   /* MS_WINDOWS */ +  static void  trip_signal(int sig_num)  {      unsigned char byte; -    int rc = 0; +    int fd; +    Py_ssize_t rc;      Handlers[sig_num].tripped = 1; -    if (wakeup_fd != -1) { + +#ifdef MS_WINDOWS +    fd = Py_SAFE_DOWNCAST(wakeup.fd, SOCKET_T, int); +#else +    fd = wakeup_fd; +#endif + +    if (fd != INVALID_FD) {          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); +#ifdef MS_WINDOWS +        if (wakeup.use_send) { +            do { +                rc = send(fd, &byte, 1, 0); +            } while (rc < 0 && errno == EINTR); + +            /* we only have a storage for one error in the wakeup structure */ +            if (rc < 0 && !wakeup.send_err_set) { +                wakeup.send_err_set = 1; +                wakeup.send_errno = errno; +                wakeup.send_win_error = GetLastError(); +                Py_AddPendingCall(report_wakeup_send_error, NULL); +            } +        } +        else +#endif +        { +            byte = (unsigned char)sig_num; +            do { +                rc = write(fd, &byte, 1); +            } while (rc < 0 && errno == EINTR); + +            if (rc < 0) { +                Py_AddPendingCall(report_wakeup_write_error, +                                  (void *)(Py_intptr_t)errno); +            } +        } +    } + +    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);      } -    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);  }  static void @@ -426,10 +503,29 @@ signal_siginterrupt(PyObject *self, PyObject *args)  static PyObject *  signal_set_wakeup_fd(PyObject *self, PyObject *args)  { -    struct stat buf; +#ifdef MS_WINDOWS +    PyObject *fdobj; +    SOCKET_T fd, old_fd; +    int res; +    int res_size = sizeof res; +    PyObject *mod; +    struct stat st; +    int is_socket; + +    if (!PyArg_ParseTuple(args, "O:set_wakeup_fd", &fdobj)) +        return NULL; + +    fd = PyLong_AsSocket_t(fdobj); +    if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) +        return NULL; +#else      int fd, old_fd; +    struct stat st; +      if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd))          return NULL; +#endif +  #ifdef WITH_THREAD      if (PyThread_get_thread_ident() != main_thread) {          PyErr_SetString(PyExc_ValueError, @@ -438,28 +534,72 @@ signal_set_wakeup_fd(PyObject *self, PyObject *args)      }  #endif +#ifdef MS_WINDOWS +    is_socket = 0; +    if (fd != INVALID_FD) { +        /* Import the _socket module to call WSAStartup() */ +        mod = PyImport_ImportModuleNoBlock("_socket"); +        if (mod == NULL) +            return NULL; +        Py_DECREF(mod); + +        /* test the socket */ +        if (getsockopt(fd, SOL_SOCKET, SO_ERROR, +                       (char *)&res, &res_size) != 0) { +            int err = WSAGetLastError(); +            if (err != WSAENOTSOCK) { +                PyErr_SetExcFromWindowsErr(PyExc_OSError, err); +                return NULL; +            } + +            if (!_PyVerify_fd(fd)) { +                PyErr_SetString(PyExc_ValueError, "invalid fd"); +                return NULL; +            } + +            if (fstat(fd, &st) != 0) { +                PyErr_SetFromErrno(PyExc_OSError); +                return NULL; +            } +        } +        else +            is_socket = 1; +    } + +    old_fd = wakeup.fd; +    wakeup.fd = fd; +    wakeup.use_send = is_socket; + +    if (old_fd != INVALID_FD) +        return PyLong_FromSocket_t(old_fd); +    else +        return PyLong_FromLong(-1); +#else      if (fd != -1) {          if (!_PyVerify_fd(fd)) {              PyErr_SetString(PyExc_ValueError, "invalid fd");              return NULL;          } -        if (fstat(fd, &buf) != 0) -            return PyErr_SetFromErrno(PyExc_OSError); +        if (fstat(fd, &st) != 0) { +            PyErr_SetFromErrno(PyExc_OSError); +            return NULL; +        }      }      old_fd = wakeup_fd;      wakeup_fd = fd;      return PyLong_FromLong(old_fd); +#endif  }  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\ +Sets the fd to be written to (with the signal number) when a signal\n\  comes in.  A library can use this to wakeup select or poll.\n\ -The previous fd is returned.\n\ +The previous fd or -1 is returned.\n\  \n\  The fd must be non-blocking."); @@ -467,10 +607,17 @@ The fd must be non-blocking.");  int  PySignal_SetWakeupFd(int fd)  { -    int old_fd = wakeup_fd; +    int old_fd;      if (fd < 0)          fd = -1; + +#ifdef MS_WINDOWS +    old_fd = Py_SAFE_DOWNCAST(wakeup.fd, SOCKET_T, int); +    wakeup.fd = fd; +#else +    old_fd = wakeup_fd;      wakeup_fd = fd; +#endif      return old_fd;  } | 
