mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
Issue #22018: On Windows, signal.set_wakeup_fd() now also supports sockets.
A side effect is that Python depends to the WinSock library.
This commit is contained in:
parent
c6aceeed29
commit
115171086a
6 changed files with 294 additions and 29 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue