From 087d0fa5b97796560c0d8ceab4f0360fd54baf4f Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 3 Apr 2022 13:58:52 -0700 Subject: bpo-47176: Interrupt handling for wasm32-emscripten builds without pthreads (GH-32209) Co-authored-by: Christian Heimes Co-authored-by: Brett Cannon --- Include/internal/pycore_emscripten_signal.h | 25 ++++++++++ Makefile.pre.in | 4 +- .../2022-04-02-14-32-21.bpo-47176.kTygYI.rst | 6 +++ Modules/signalmodule.c | 2 + Python/ceval.c | 2 + Python/emscripten_signal.c | 56 ++++++++++++++++++++++ configure | 17 +++++++ configure.ac | 13 +++++ 8 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 Include/internal/pycore_emscripten_signal.h create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-04-02-14-32-21.bpo-47176.kTygYI.rst create mode 100644 Python/emscripten_signal.c diff --git a/Include/internal/pycore_emscripten_signal.h b/Include/internal/pycore_emscripten_signal.h new file mode 100644 index 0000000..8b3287d --- /dev/null +++ b/Include/internal/pycore_emscripten_signal.h @@ -0,0 +1,25 @@ +#ifndef Py_EMSCRIPTEN_SIGNAL_H +#define Py_EMSCRIPTEN_SIGNAL_H + +#if defined(__EMSCRIPTEN__) + +void +_Py_CheckEmscriptenSignals(void); + +void +_Py_CheckEmscriptenSignalsPeriodically(void); + +#define _Py_CHECK_EMSCRIPTEN_SIGNALS() _Py_CheckEmscriptenSignals() + +#define _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY() _Py_CheckEmscriptenSignalsPeriodically() + +extern int Py_EMSCRIPTEN_SIGNAL_HANDLING; + +#else + +#define _Py_CHECK_EMSCRIPTEN_SIGNALS() +#define _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY() + +#endif // defined(__EMSCRIPTEN__) + +#endif // ndef Py_EMSCRIPTEN_SIGNAL_H diff --git a/Makefile.pre.in b/Makefile.pre.in index 5318a41..f94ba93 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -429,7 +429,8 @@ PYTHON_OBJS= \ Python/$(DYNLOADFILE) \ $(LIBOBJS) \ $(MACHDEP_OBJS) \ - $(DTRACE_OBJS) + $(DTRACE_OBJS) \ + @PLATFORM_OBJS@ ########################################################################## @@ -1608,6 +1609,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_unicodeobject.h \ $(srcdir)/Include/internal/pycore_warnings.h \ $(DTRACE_HEADERS) \ + @PLATFORM_HEADERS@ \ \ $(srcdir)/Python/stdlib_module_names.h diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-04-02-14-32-21.bpo-47176.kTygYI.rst b/Misc/NEWS.d/next/Core and Builtins/2022-04-02-14-32-21.bpo-47176.kTygYI.rst new file mode 100644 index 0000000..03fe54a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-02-14-32-21.bpo-47176.kTygYI.rst @@ -0,0 +1,6 @@ +Emscripten builds cannot handle signals in the usual way due to platform +limitations. Python can now handle signals. To use, set +Module.Py_EmscriptenSignalBuffer to be a single byte SharedArrayBuffer and +set Py_EMSCRIPTEN_SIGNAL_HANDLING to 1. Writing a number into the +SharedArrayBuffer will cause the corresponding signal to be raised into the +Python thread. diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 9566263..1ee5c66 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -13,6 +13,7 @@ #include "pycore_pyerrors.h" // _PyErr_SetString() #include "pycore_pylifecycle.h" // NSIG #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS #ifndef MS_WINDOWS # include "posixmodule.h" @@ -1797,6 +1798,7 @@ PyErr_CheckSignals(void) int _PyErr_CheckSignalsTstate(PyThreadState *tstate) { + _Py_CHECK_EMSCRIPTEN_SIGNALS(); if (!_Py_atomic_load(&is_tripped)) { return 0; } diff --git a/Python/ceval.c b/Python/ceval.c index 43080f8..68d2920 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -21,6 +21,7 @@ #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_sysmodule.h" // _PySys_Audit() #include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS #include "code.h" #include "pycore_dict.h" @@ -1292,6 +1293,7 @@ eval_frame_handle_pending(PyThreadState *tstate) } #define CHECK_EVAL_BREAKER() \ + _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); \ if (_Py_atomic_load_relaxed(eval_breaker)) { \ goto handle_eval_breaker; \ } diff --git a/Python/emscripten_signal.c b/Python/emscripten_signal.c new file mode 100644 index 0000000..d617ddf --- /dev/null +++ b/Python/emscripten_signal.c @@ -0,0 +1,56 @@ +// To enable signal handling, the embedder should: +// 1. set Module.Py_EmscriptenSignalBuffer = some_shared_array_buffer; +// 2. set the Py_EMSCRIPTEN_SIGNAL_HANDLING flag to 1 as follows: +// Module.HEAP8[Module._Py_EMSCRIPTEN_SIGNAL_HANDLING] = 1 +// +// The address &Py_EMSCRIPTEN_SIGNAL_HANDLING is exported as +// Module._Py_EMSCRIPTEN_SIGNAL_HANDLING. +#include +#include "Python.h" + +EM_JS(int, _Py_CheckEmscriptenSignals_Helper, (void), { + if (!Module.Py_EmscriptenSignalBuffer) { + return 0; + } + try { + let result = Module.Py_EmscriptenSignalBuffer[0]; + Module.Py_EmscriptenSignalBuffer[0] = 0; + return result; + } catch(e) { +#if !defined(NDEBUG) + console.warn("Error occurred while trying to read signal buffer:", e); +#endif + return 0; + } +}); + +EMSCRIPTEN_KEEPALIVE int Py_EMSCRIPTEN_SIGNAL_HANDLING = 0; + +void +_Py_CheckEmscriptenSignals(void) +{ + if (!Py_EMSCRIPTEN_SIGNAL_HANDLING) { + return; + } + int signal = _Py_CheckEmscriptenSignals_Helper(); + if (signal) { + PyErr_SetInterruptEx(signal); + } +} + + +#define PY_EMSCRIPTEN_SIGNAL_INTERVAL 50 +static int emscripten_signal_clock = PY_EMSCRIPTEN_SIGNAL_INTERVAL; + +void +_Py_CheckEmscriptenSignalsPeriodically(void) +{ + if (!Py_EMSCRIPTEN_SIGNAL_HANDLING) { + return; + } + emscripten_signal_clock--; + if (emscripten_signal_clock == 0) { + emscripten_signal_clock = PY_EMSCRIPTEN_SIGNAL_INTERVAL; + _Py_CheckEmscriptenSignals(); + } +} diff --git a/configure b/configure index a5062d7..bb1aa75 100755 --- a/configure +++ b/configure @@ -817,6 +817,8 @@ TRUE MACHDEP_OBJS DYNLOADFILE DLINCLDIR +PLATFORM_OBJS +PLATFORM_HEADERS DTRACE_OBJS DTRACE_HEADERS DFLAGS @@ -13993,6 +13995,21 @@ $as_echo "$ac_cv_dtrace_link" >&6; } fi fi +PLATFORM_HEADERS= +PLATFORM_OBJS= + +case $ac_sys_system in #( + Emscripten) : + + as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o' + as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h' + ;; #( + *) : + ;; +esac + + + # -I${DLINCLDIR} is added to the compile rule for importdl.o DLINCLDIR=. diff --git a/configure.ac b/configure.ac index 84bc9b3..9f0a50e 100644 --- a/configure.ac +++ b/configure.ac @@ -4187,6 +4187,19 @@ then fi fi +dnl Platform-specific C and header files. +PLATFORM_HEADERS= +PLATFORM_OBJS= + +AS_CASE([$ac_sys_system], + [Emscripten], [ + AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o']) + AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h']) + ], +) +AC_SUBST([PLATFORM_HEADERS]) +AC_SUBST([PLATFORM_OBJS]) + # -I${DLINCLDIR} is added to the compile rule for importdl.o AC_SUBST(DLINCLDIR) DLINCLDIR=. -- cgit v0.12