Skip to content

Commit 975e392

Browse files
committed
Fix bug 581232 - [Windows] Can not interrupt time.sleep()
time.sleep() will now be interrupted on the main thread when Ctrl+C is pressed. Other threads are never interrupted.
1 parent 0756a5e commit 975e392

File tree

1 file changed

+46
-3
lines changed

1 file changed

+46
-3
lines changed

Modules/timemodule.c

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,24 @@ extern int ftime(struct timeb *);
2828
#include <i86.h>
2929
#else
3030
#ifdef MS_WINDOWS
31+
#define WIN32_LEAN_AND_MEAN
3132
#include <windows.h>
33+
#include "pythread.h"
34+
35+
/* helper to allow us to interrupt sleep() on Windows*/
36+
static HANDLE hInterruptEvent = NULL;
37+
static BOOL WINAPI PyCtrlHandler(DWORD dwCtrlType)
38+
{
39+
SetEvent(hInterruptEvent);
40+
/* allow other default handlers to be called.
41+
Default Python handler will setup the
42+
KeyboardInterrupt exception.
43+
*/
44+
return FALSE;
45+
}
46+
static long main_thread;
47+
48+
3249
#if defined(__BORLANDC__)
3350
/* These overrides not needed for Win32 */
3451
#define timezone _timezone
@@ -680,7 +697,15 @@ inittime(void)
680697
Py_BuildValue("(zz)", _tzname[0], _tzname[1]));
681698
#endif /* __CYGWIN__ */
682699
#endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
683-
700+
#ifdef MS_WINDOWS
701+
/* Helper to allow interrupts for Windows.
702+
If Ctrl+C event delivered while not sleeping
703+
it will be ignored.
704+
*/
705+
main_thread = PyThread_get_thread_ident();
706+
hInterruptEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
707+
SetConsoleCtrlHandler( PyCtrlHandler, TRUE);
708+
#endif /* MS_WINDOWS */
684709
PyStructSequence_InitType(&StructTimeType, &struct_time_type_desc);
685710
Py_INCREF(&StructTimeType);
686711
PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType);
@@ -775,9 +800,27 @@ floatsleep(double secs)
775800
PyErr_SetString(PyExc_OverflowError, "sleep length is too large");
776801
return -1;
777802
}
778-
/* XXX Can't interrupt this sleep */
779803
Py_BEGIN_ALLOW_THREADS
780-
Sleep((unsigned long)millisecs);
804+
/* allow sleep(0) to maintain win32 semantics, and as decreed by
805+
Guido, only the main thread can be interrupted. */
806+
if ((unsigned long)millisecs==0 || main_thread != PyThread_get_thread_ident())
807+
Sleep((unsigned long)millisecs);
808+
else {
809+
DWORD rc;
810+
ResetEvent(hInterruptEvent);
811+
rc = WaitForSingleObject(hInterruptEvent, (unsigned long)millisecs);
812+
if (rc==WAIT_OBJECT_0) {
813+
/* yield to make sure real Python signal handler called */
814+
Sleep(1);
815+
Py_BLOCK_THREADS
816+
/* PyErr_SetFromErrno() does the "right thing" wrt signals
817+
if errno=EINTR
818+
*/
819+
errno = EINTR;
820+
PyErr_SetFromErrno(PyExc_IOError);
821+
return -1;
822+
}
823+
}
781824
Py_END_ALLOW_THREADS
782825
}
783826
#elif defined(PYOS_OS2)

0 commit comments

Comments
 (0)