mirror of https://github.com/pyodide/pyodide.git
149 lines
5.7 KiB
Diff
149 lines
5.7 KiB
Diff
From 320cc07c0a30436759cde8f4c926e76ed8dc918a Mon Sep 17 00:00:00 2001
|
|
From: Hood <hood@mit.edu>
|
|
Date: Fri, 3 Sep 2021 18:08:26 -0700
|
|
Subject: [PATCH] Patch in keyboard interrupt handling
|
|
|
|
---
|
|
Include/cpython/ceval.h | 1 +
|
|
Include/cpython/pystate.h | 3 +++
|
|
Modules/signalmodule.c | 3 +++
|
|
Python/ceval.c | 13 ++++++++++++-
|
|
Python/pystate.c | 2 ++
|
|
5 files changed, 21 insertions(+), 1 deletion(-)
|
|
|
|
This patch adds a callback called pyodide_callback with signature
|
|
`int callback(void)` to the main loop in `ceval.c`. This function gets called once
|
|
per opcode except after opcodes `SETUP_FINALLY`, `SETUP_WITH`, `BEFORE_ASYNC_WITH`,
|
|
and `YIELD_FROM`. The main loop normally prevents signal handling, etc from happening
|
|
after these instructions, so I figured this should apply to my callback too.
|
|
(There is an extra layer of protection here though because in the callback we use
|
|
`PyErr_SetInterrupt` which simulates a SIGINT signal and triggers the KeyboardInterrupt
|
|
via the standard mechanism.)
|
|
|
|
Note that we call the callback outside of the normal
|
|
`if (_Py_atomic_load_relaxed(eval_breaker))`
|
|
block where most of the "periodic things" happen. This is because normally when a
|
|
"periodic thing" is queued, the `eval_breaker` flag is set signalling the need for
|
|
handling. The whole point of this patch though is that we need to be able to set an
|
|
interrupt from a remote thread and the `eval_breaker` flag lives on the WASM heap.
|
|
Unless the whole WASM heap is made into a `SharedArrayBuffer` and shared between
|
|
webworkers, we have no way to set the `eval_breaker` flag. We still want to skip
|
|
the callback after the sensitive opcodes `SETUP_FINALLY`, `SETUP_WITH`,
|
|
`BEFORE_ASYNC_WITH`, and `YIELD_FROM`, so we hoisted the check for that condition
|
|
out of the `eval_breaker` conditional.
|
|
|
|
We also patched the `threadstate` struct to include a `pyodide_callback` field, patch
|
|
`new_threadstate` to initialize the `pyodide_callback` field to `NULL`, and add a new
|
|
API `PyPyodide_SetPyodideCallback` to set `pyodide_callback`.
|
|
|
|
Lastly, we patched PyErr_CheckSignals to check our custom source of keyboard interrupts
|
|
so that C extensions that use PyErr_CheckSignals to allow keyboard interrupts will be
|
|
interruptable in pyodide too.
|
|
|
|
diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h
|
|
index e1922a6..a4acd9b 100644
|
|
--- a/Include/cpython/ceval.h
|
|
+++ b/Include/cpython/ceval.h
|
|
@@ -10,6 +10,7 @@ PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *);
|
|
PyAPI_DATA(int) _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
|
|
PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *);
|
|
PyAPI_FUNC(int) _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
|
|
+PyAPI_FUNC(void) PyPyodide_SetPyodideCallback(PyPyodide_callback);
|
|
PyAPI_FUNC(int) _PyEval_GetCoroutineOriginTrackingDepth(void);
|
|
PyAPI_FUNC(int) _PyEval_SetAsyncGenFirstiter(PyObject *);
|
|
PyAPI_FUNC(PyObject *) _PyEval_GetAsyncGenFirstiter(void);
|
|
diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h
|
|
index f292da1..3577ac8 100644
|
|
--- a/Include/cpython/pystate.h
|
|
+++ b/Include/cpython/pystate.h
|
|
@@ -17,6 +17,7 @@ PyAPI_FUNC(PyObject *) _PyInterpreterState_GetMainModule(PyInterpreterState *);
|
|
|
|
/* Py_tracefunc return -1 when raising an exception, or 0 for success. */
|
|
typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *, int, PyObject *);
|
|
+typedef int (*PyPyodide_callback)(void);
|
|
|
|
/* The following values are used for 'what' for tracefunc functions
|
|
*
|
|
@@ -75,6 +76,8 @@ struct _ts {
|
|
PyObject *c_profileobj;
|
|
PyObject *c_traceobj;
|
|
|
|
+ PyPyodide_callback pyodide_callback;
|
|
+
|
|
/* The exception currently being raised */
|
|
PyObject *curexc_type;
|
|
PyObject *curexc_value;
|
|
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
|
|
index de564c2..bfe2fd1 100644
|
|
--- a/Modules/signalmodule.c
|
|
+++ b/Modules/signalmodule.c
|
|
@@ -1683,6 +1683,9 @@ PyErr_CheckSignals(void)
|
|
int
|
|
_PyErr_CheckSignalsTstate(PyThreadState *tstate)
|
|
{
|
|
+ if (tstate->pyodide_callback != NULL && tstate->pyodide_callback() == -1)
|
|
+ return -1;
|
|
+
|
|
if (!_Py_atomic_load(&is_tripped)) {
|
|
return 0;
|
|
}
|
|
diff --git a/Python/ceval.c b/Python/ceval.c
|
|
index 91e879e..f8d424d 100644
|
|
--- a/Python/ceval.c
|
|
+++ b/Python/ceval.c
|
|
@@ -1374,7 +1374,6 @@ main_loop:
|
|
async I/O handler); see Py_AddPendingCall() and
|
|
Py_MakePendingCalls() above. */
|
|
|
|
- if (_Py_atomic_load_relaxed(eval_breaker)) {
|
|
opcode = _Py_OPCODE(*next_instr);
|
|
if (opcode == SETUP_FINALLY ||
|
|
opcode == SETUP_WITH ||
|
|
@@ -1399,11 +1398,18 @@ main_loop:
|
|
goto fast_next_opcode;
|
|
}
|
|
|
|
+ if (_Py_atomic_load_relaxed(eval_breaker)) {
|
|
if (eval_frame_handle_pending(tstate) != 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
+ if(tstate->pyodide_callback != NULL){
|
|
+ if(tstate->pyodide_callback() != 0){
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+
|
|
fast_next_opcode:
|
|
f->f_lasti = INSTR_OFFSET();
|
|
|
|
@@ -4816,6 +4822,11 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
|
|
}
|
|
}
|
|
|
|
+void
|
|
+PyPyodide_SetPyodideCallback(PyPyodide_callback pyodide_callback){
|
|
+ PyThreadState *tstate = _PyThreadState_GET();
|
|
+ tstate->pyodide_callback = pyodide_callback;
|
|
+}
|
|
|
|
void
|
|
_PyEval_SetCoroutineOriginTrackingDepth(PyThreadState *tstate, int new_depth)
|
|
diff --git a/Python/pystate.c b/Python/pystate.c
|
|
index 9beefa8..408b7ce 100644
|
|
--- a/Python/pystate.c
|
|
+++ b/Python/pystate.c
|
|
@@ -602,6 +602,8 @@ new_threadstate(PyInterpreterState *interp, int init)
|
|
tstate->c_profileobj = NULL;
|
|
tstate->c_traceobj = NULL;
|
|
|
|
+ tstate->pyodide_callback = NULL;
|
|
+
|
|
tstate->trash_delete_nesting = 0;
|
|
tstate->trash_delete_later = NULL;
|
|
tstate->on_delete = NULL;
|
|
--
|
|
2.17.1
|
|
|