python / cpython

The Python programming language
https://www.python.org
Other
62.87k stars 30.11k forks source link

input() is blocking exceptions at some phase during execution #112585

Open wh-timme opened 10 months ago

wh-timme commented 10 months ago

Bug report

Bug description:

The following loop will soon stuck at some value, due to some phase of built-in input() itself is blocking exceptions and will not pass the exception to upper scope until an ENTER is hit on keyboard.

import os, signal, threading

cnt = [0]

def interrupter(cnt):
    local_cnt = 0
    while 1:
        if local_cnt != cnt[0]:
            local_cnt = cnt[0]
            os.kill(os.getpid(), signal.SIGINT)

threading.Thread(target=interrupter, args=(cnt,), daemon=True).start()

while 1:
    cnt[0] += 1
    try:
        input(">")
    except KeyboardInterrupt:
        print(cnt[0])

CPython versions tested on:

3.8

Operating systems tested on:

macOS

TuringKi commented 9 months ago

I have come up with a solution, but I am not sure if it is robust enough:

diff --git a/Parser/myreadline.c b/Parser/myreadline.c
index 1825665..3d6abe5 100644
--- a/Parser/myreadline.c
+++ b/Parser/myreadline.c
@@ -12,6 +12,7 @@
 #include "Python.h"
 #include "pycore_fileutils.h"     // _Py_BEGIN_SUPPRESS_IPH
 #include "pycore_pystate.h"   // _PyThreadState_GET()
+#include <sys/types.h>
 #ifdef MS_WINDOWS
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
@@ -331,8 +332,9 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
             return NULL;
         }
         p = pr;
-        int err = my_fgets(tstate, p + n, (int)incr, sys_stdin);
-        if (err == 1) {
+        int err = _Py_read(sys_stdin->_fileno, p + n, incr);
+        //int err = my_fgets(tstate, p + n, (int)incr, sys_stdin);
+        if (err == -1) {
             // Interrupt
             PyMem_RawFree(p);
             return NULL;
@@ -393,8 +395,8 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
     }

     _PyOS_ReadlineTState = tstate;
-    Py_BEGIN_ALLOW_THREADS
-    PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
+    //Py_BEGIN_ALLOW_THREADS
+    //PyThread_acquire_lock(_PyOS_ReadlineLock, 1);

     /* This is needed to handle the unlikely case that the
      * interpreter is in interactive mode *and* stdin/out are not
@@ -418,9 +420,9 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
     else {
         rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout, prompt);
     }
-    Py_END_ALLOW_THREADS
+    //Py_END_ALLOW_THREADS

-    PyThread_release_lock(_PyOS_ReadlineLock);
+    //PyThread_release_lock(_PyOS_ReadlineLock);

     _PyOS_ReadlineTState = NULL;