mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #12328: Under Windows, refactor handling of Ctrl-C events and
make _multiprocessing.win32.WaitForMultipleObjects interruptible when the wait_flag parameter is false. Patch by sbt.
This commit is contained in:
parent
ce4a9da705
commit
6dd381eb62
8 changed files with 85 additions and 99 deletions
|
@ -8,6 +8,12 @@ extern "C" {
|
||||||
PyAPI_FUNC(int) PyOS_InterruptOccurred(void);
|
PyAPI_FUNC(int) PyOS_InterruptOccurred(void);
|
||||||
PyAPI_FUNC(void) PyOS_InitInterrupts(void);
|
PyAPI_FUNC(void) PyOS_InitInterrupts(void);
|
||||||
PyAPI_FUNC(void) PyOS_AfterFork(void);
|
PyAPI_FUNC(void) PyOS_AfterFork(void);
|
||||||
|
PyAPI_FUNC(int) _PyOS_IsMainThread(void);
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
/* windows.h is not included by Python.h so use void* instead of HANDLE */
|
||||||
|
PyAPI_FUNC(void*) _PyOS_SigintEvent(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,6 +387,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #12328: Under Windows, refactor handling of Ctrl-C events and
|
||||||
|
make _multiprocessing.win32.WaitForMultipleObjects interruptible when
|
||||||
|
the wait_flag parameter is false. Patch by sbt.
|
||||||
|
|
||||||
- Issue #13322: Fix BufferedWriter.write() to ensure that BlockingIOError is
|
- Issue #13322: Fix BufferedWriter.write() to ensure that BlockingIOError is
|
||||||
raised when the wrapped raw file is non-blocking and the write would block.
|
raised when the wrapped raw file is non-blocking and the write would block.
|
||||||
Previous code assumed that the raw write() would raise BlockingIOError, but
|
Previous code assumed that the raw write() would raise BlockingIOError, but
|
||||||
|
|
|
@ -53,30 +53,6 @@ mp_SetError(PyObject *Type, int num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Windows only
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
|
||||||
|
|
||||||
/* On Windows we set an event to signal Ctrl-C; compare with timemodule.c */
|
|
||||||
|
|
||||||
HANDLE sigint_event = NULL;
|
|
||||||
|
|
||||||
static BOOL WINAPI
|
|
||||||
ProcessingCtrlHandler(DWORD dwCtrlType)
|
|
||||||
{
|
|
||||||
SetEvent(sigint_event);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* MS_WINDOWS */
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All platforms
|
|
||||||
*/
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
multiprocessing_address_of_buffer(PyObject *self, PyObject *obj)
|
multiprocessing_address_of_buffer(PyObject *self, PyObject *obj)
|
||||||
{
|
{
|
||||||
|
@ -165,17 +141,6 @@ PyInit__multiprocessing(void)
|
||||||
if (!temp)
|
if (!temp)
|
||||||
return NULL;
|
return NULL;
|
||||||
PyModule_AddObject(module, "win32", temp);
|
PyModule_AddObject(module, "win32", temp);
|
||||||
|
|
||||||
/* Initialize the event handle used to signal Ctrl-C */
|
|
||||||
sigint_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
||||||
if (!sigint_event) {
|
|
||||||
PyErr_SetFromWindowsErr(0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!SetConsoleCtrlHandler(ProcessingCtrlHandler, TRUE)) {
|
|
||||||
PyErr_SetFromWindowsErr(0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Add configuration macros */
|
/* Add configuration macros */
|
||||||
|
|
|
@ -100,7 +100,6 @@ PyObject *mp_SetError(PyObject *Type, int num);
|
||||||
extern PyObject *BufferTooShort;
|
extern PyObject *BufferTooShort;
|
||||||
extern PyTypeObject SemLockType;
|
extern PyTypeObject SemLockType;
|
||||||
extern PyTypeObject PipeConnectionType;
|
extern PyTypeObject PipeConnectionType;
|
||||||
extern HANDLE sigint_event;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Miscellaneous
|
* Miscellaneous
|
||||||
|
|
|
@ -62,7 +62,8 @@ semlock_acquire(SemLockObject *self, PyObject *args, PyObject *kwds)
|
||||||
int blocking = 1;
|
int blocking = 1;
|
||||||
double timeout;
|
double timeout;
|
||||||
PyObject *timeout_obj = Py_None;
|
PyObject *timeout_obj = Py_None;
|
||||||
DWORD res, full_msecs, msecs, start, ticks;
|
DWORD res, full_msecs, nhandles;
|
||||||
|
HANDLE handles[2], sigint_event;
|
||||||
|
|
||||||
static char *kwlist[] = {"block", "timeout", NULL};
|
static char *kwlist[] = {"block", "timeout", NULL};
|
||||||
|
|
||||||
|
@ -96,53 +97,40 @@ semlock_acquire(SemLockObject *self, PyObject *args, PyObject *kwds)
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check whether we can acquire without blocking */
|
/* check whether we can acquire without releasing the GIL and blocking */
|
||||||
if (WaitForSingleObject(self->handle, 0) == WAIT_OBJECT_0) {
|
if (WaitForSingleObject(self->handle, 0) == WAIT_OBJECT_0) {
|
||||||
self->last_tid = GetCurrentThreadId();
|
self->last_tid = GetCurrentThreadId();
|
||||||
++self->count;
|
++self->count;
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
msecs = full_msecs;
|
/* prepare list of handles */
|
||||||
start = GetTickCount();
|
nhandles = 0;
|
||||||
|
handles[nhandles++] = self->handle;
|
||||||
for ( ; ; ) {
|
if (_PyOS_IsMainThread()) {
|
||||||
HANDLE handles[2] = {self->handle, sigint_event};
|
sigint_event = _PyOS_SigintEvent();
|
||||||
|
assert(sigint_event != NULL);
|
||||||
/* do the wait */
|
handles[nhandles++] = sigint_event;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
ResetEvent(sigint_event);
|
|
||||||
res = WaitForMultipleObjects(2, handles, FALSE, msecs);
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
|
|
||||||
/* handle result */
|
|
||||||
if (res != WAIT_OBJECT_0 + 1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* got SIGINT so give signal handler a chance to run */
|
|
||||||
Sleep(1);
|
|
||||||
|
|
||||||
/* if this is main thread let KeyboardInterrupt be raised */
|
|
||||||
if (PyErr_CheckSignals())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* recalculate timeout */
|
|
||||||
if (msecs != INFINITE) {
|
|
||||||
ticks = GetTickCount();
|
|
||||||
if ((DWORD)(ticks - start) >= full_msecs)
|
|
||||||
Py_RETURN_FALSE;
|
|
||||||
msecs = full_msecs - (ticks - start);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* do the wait */
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
if (sigint_event != NULL)
|
||||||
|
ResetEvent(sigint_event);
|
||||||
|
res = WaitForMultipleObjects(nhandles, handles, FALSE, full_msecs);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
/* handle result */
|
/* handle result */
|
||||||
switch (res) {
|
switch (res) {
|
||||||
case WAIT_TIMEOUT:
|
case WAIT_TIMEOUT:
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0 + 0:
|
||||||
self->last_tid = GetCurrentThreadId();
|
self->last_tid = GetCurrentThreadId();
|
||||||
++self->count;
|
++self->count;
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
|
case WAIT_OBJECT_0 + 1:
|
||||||
|
errno = EINTR;
|
||||||
|
return PyErr_SetFromErrno(PyExc_IOError);
|
||||||
case WAIT_FAILED:
|
case WAIT_FAILED:
|
||||||
return PyErr_SetFromWindowsErr(0);
|
return PyErr_SetFromWindowsErr(0);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -679,6 +679,7 @@ win32_WaitForMultipleObjects(PyObject* self, PyObject* args)
|
||||||
DWORD result;
|
DWORD result;
|
||||||
PyObject *handle_seq;
|
PyObject *handle_seq;
|
||||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
|
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
|
||||||
|
HANDLE sigint_event = NULL;
|
||||||
Py_ssize_t nhandles, i;
|
Py_ssize_t nhandles, i;
|
||||||
int wait_flag;
|
int wait_flag;
|
||||||
int milliseconds = INFINITE;
|
int milliseconds = INFINITE;
|
||||||
|
@ -696,10 +697,10 @@ win32_WaitForMultipleObjects(PyObject* self, PyObject* args)
|
||||||
nhandles = PySequence_Length(handle_seq);
|
nhandles = PySequence_Length(handle_seq);
|
||||||
if (nhandles == -1)
|
if (nhandles == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (nhandles < 0 || nhandles >= MAXIMUM_WAIT_OBJECTS) {
|
if (nhandles < 0 || nhandles >= MAXIMUM_WAIT_OBJECTS - 1) {
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"need at most %zd handles, got a sequence of length %zd",
|
"need at most %zd handles, got a sequence of length %zd",
|
||||||
MAXIMUM_WAIT_OBJECTS, nhandles);
|
MAXIMUM_WAIT_OBJECTS - 1, nhandles);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
for (i = 0; i < nhandles; i++) {
|
for (i = 0; i < nhandles; i++) {
|
||||||
|
@ -711,14 +712,27 @@ win32_WaitForMultipleObjects(PyObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
handles[i] = h;
|
handles[i] = h;
|
||||||
}
|
}
|
||||||
|
/* If this is the main thread then make the wait interruptible
|
||||||
|
by Ctrl-C unless we are waiting for *all* handles */
|
||||||
|
if (!wait_flag && _PyOS_IsMainThread()) {
|
||||||
|
sigint_event = _PyOS_SigintEvent();
|
||||||
|
assert(sigint_event != NULL);
|
||||||
|
handles[nhandles++] = sigint_event;
|
||||||
|
}
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
if (sigint_event != NULL)
|
||||||
|
ResetEvent(sigint_event);
|
||||||
result = WaitForMultipleObjects((DWORD) nhandles, handles,
|
result = WaitForMultipleObjects((DWORD) nhandles, handles,
|
||||||
(BOOL) wait_flag, (DWORD) milliseconds);
|
(BOOL) wait_flag, (DWORD) milliseconds);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
if (result == WAIT_FAILED)
|
if (result == WAIT_FAILED)
|
||||||
return PyErr_SetExcFromWindowsErr(PyExc_IOError, 0);
|
return PyErr_SetExcFromWindowsErr(PyExc_IOError, 0);
|
||||||
|
else if (sigint_event != NULL && result == WAIT_OBJECT_0 + nhandles - 1) {
|
||||||
|
errno = EINTR;
|
||||||
|
return PyErr_SetFromErrno(PyExc_IOError);
|
||||||
|
}
|
||||||
|
|
||||||
return PyLong_FromLong((int) result);
|
return PyLong_FromLong((int) result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,10 @@ static PyObject *IntHandler;
|
||||||
|
|
||||||
static PyOS_sighandler_t old_siginthandler = SIG_DFL;
|
static PyOS_sighandler_t old_siginthandler = SIG_DFL;
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
static HANDLE sigint_event = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_GETITIMER
|
#ifdef HAVE_GETITIMER
|
||||||
static PyObject *ItimerError;
|
static PyObject *ItimerError;
|
||||||
|
|
||||||
|
@ -229,6 +233,11 @@ signal_handler(int sig_num)
|
||||||
/* Issue #10311: asynchronously executing signal handlers should not
|
/* Issue #10311: asynchronously executing signal handlers should not
|
||||||
mutate errno under the feet of unsuspecting C code. */
|
mutate errno under the feet of unsuspecting C code. */
|
||||||
errno = save_errno;
|
errno = save_errno;
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
if (sig_num == SIGINT)
|
||||||
|
SetEvent(sigint_event);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1253,6 +1262,11 @@ PyInit_signal(void)
|
||||||
Py_DECREF(x);
|
Py_DECREF(x);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
/* Create manual-reset event, initially unset */
|
||||||
|
sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
Py_DECREF(m);
|
Py_DECREF(m);
|
||||||
m = NULL;
|
m = NULL;
|
||||||
|
@ -1397,3 +1411,25 @@ PyOS_AfterFork(void)
|
||||||
PyThread_ReInitTLS();
|
PyThread_ReInitTLS();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyOS_IsMainThread(void)
|
||||||
|
{
|
||||||
|
#ifdef WITH_THREAD
|
||||||
|
return PyThread_get_thread_ident() == main_thread;
|
||||||
|
#else
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
void *_PyOS_SigintEvent(void)
|
||||||
|
{
|
||||||
|
/* Returns a manual-reset event which gets tripped whenever
|
||||||
|
SIGINT is received.
|
||||||
|
|
||||||
|
Python.h does not include windows.h so we do cannot use HANDLE
|
||||||
|
as the return type of this function. We use void* instead. */
|
||||||
|
return sigint_event;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -21,19 +21,6 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "pythread.h"
|
#include "pythread.h"
|
||||||
|
|
||||||
/* helper to allow us to interrupt sleep() on Windows*/
|
|
||||||
static HANDLE hInterruptEvent = NULL;
|
|
||||||
static BOOL WINAPI PyCtrlHandler(DWORD dwCtrlType)
|
|
||||||
{
|
|
||||||
SetEvent(hInterruptEvent);
|
|
||||||
/* allow other default handlers to be called.
|
|
||||||
Default Python handler will setup the
|
|
||||||
KeyboardInterrupt exception.
|
|
||||||
*/
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
static long main_thread;
|
|
||||||
|
|
||||||
#if defined(__BORLANDC__)
|
#if defined(__BORLANDC__)
|
||||||
/* These overrides not needed for Win32 */
|
/* These overrides not needed for Win32 */
|
||||||
#define timezone _timezone
|
#define timezone _timezone
|
||||||
|
@ -955,15 +942,6 @@ PyInit_time(void)
|
||||||
/* Set, or reset, module variables like time.timezone */
|
/* Set, or reset, module variables like time.timezone */
|
||||||
PyInit_timezone(m);
|
PyInit_timezone(m);
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
|
||||||
/* Helper to allow interrupts for Windows.
|
|
||||||
If Ctrl+C event delivered while not sleeping
|
|
||||||
it will be ignored.
|
|
||||||
*/
|
|
||||||
main_thread = PyThread_get_thread_ident();
|
|
||||||
hInterruptEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
||||||
SetConsoleCtrlHandler( PyCtrlHandler, TRUE);
|
|
||||||
#endif /* MS_WINDOWS */
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
PyStructSequence_InitType(&StructTimeType,
|
PyStructSequence_InitType(&StructTimeType,
|
||||||
&struct_time_type_desc);
|
&struct_time_type_desc);
|
||||||
|
@ -1036,18 +1014,14 @@ floatsleep(double secs)
|
||||||
* by Guido, only the main thread can be interrupted.
|
* by Guido, only the main thread can be interrupted.
|
||||||
*/
|
*/
|
||||||
ul_millis = (unsigned long)millisecs;
|
ul_millis = (unsigned long)millisecs;
|
||||||
if (ul_millis == 0 ||
|
if (ul_millis == 0 || !_PyOS_IsMainThread())
|
||||||
main_thread != PyThread_get_thread_ident())
|
|
||||||
Sleep(ul_millis);
|
Sleep(ul_millis);
|
||||||
else {
|
else {
|
||||||
DWORD rc;
|
DWORD rc;
|
||||||
|
HANDLE hInterruptEvent = _PyOS_SigintEvent();
|
||||||
ResetEvent(hInterruptEvent);
|
ResetEvent(hInterruptEvent);
|
||||||
rc = WaitForSingleObject(hInterruptEvent, ul_millis);
|
rc = WaitForSingleObject(hInterruptEvent, ul_millis);
|
||||||
if (rc == WAIT_OBJECT_0) {
|
if (rc == WAIT_OBJECT_0) {
|
||||||
/* Yield to make sure real Python signal
|
|
||||||
* handler called.
|
|
||||||
*/
|
|
||||||
Sleep(1);
|
|
||||||
Py_BLOCK_THREADS
|
Py_BLOCK_THREADS
|
||||||
errno = EINTR;
|
errno = EINTR;
|
||||||
PyErr_SetFromErrno(PyExc_IOError);
|
PyErr_SetFromErrno(PyExc_IOError);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue