From e5618dce4caa5c658c76d0d03b0178474a28d356 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 15:49:53 +0200 Subject: [PATCH 1/4] plugins/python: use PyOS_*Fork stable API functions on 3.7+ To avoid calling functions made private from 3.13+. And probably fixing issues with C extensions. --- plugins/python/python_plugin.c | 20 +++++++++----------- plugins/python/uwsgi_python.h | 8 ++------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 6c5be6c7f..066bde668 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -461,7 +461,7 @@ void uwsgi_python_post_fork() { // reset python signal flags so child processes can trap signals // Necessary if uwsgi fork hooks not called to update interpreter state if (!up.call_uwsgi_fork_hooks && up.call_osafterfork) { -#ifdef HAS_NOT_PyOS_AfterFork_Child +#ifdef HAS_NOT_PYOS_FORK_STABLE_API PyOS_AfterFork(); #else PyOS_AfterFork_Child(); @@ -1368,11 +1368,10 @@ void uwsgi_python_pre_uwsgi_fork() { // Acquire the gil and import lock before forking in order to avoid // deadlocks in workers UWSGI_GET_GIL -#if defined UWSGI_PY312 - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyImport_AcquireLock(interp); -#else +#ifdef HAS_NOT_PYOS_FORK_STABLE_API _PyImport_AcquireLock(); +#else + PyOS_BeforeFork(); #endif } } @@ -1385,17 +1384,16 @@ void uwsgi_python_post_uwsgi_fork(int step) { if (uwsgi.has_threads) { if (step == 0) { // Release locks within master process -#if defined UWSGI_PY312 - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyImport_ReleaseLock(interp); -#else +#ifdef HAS_NOT_PYOS_FORK_STABLE_API _PyImport_ReleaseLock(); +#else + PyOS_AfterFork_Parent(); #endif UWSGI_RELEASE_GIL } else { // Ensure thread state and locks are cleaned up in child process -#ifdef HAS_NOT_PyOS_AfterFork_Child +#ifdef HAS_NOT_PYOS_FORK_STABLE_API PyOS_AfterFork(); #else PyOS_AfterFork_Child(); @@ -2164,7 +2162,7 @@ static int uwsgi_python_worker() { // ensure signals can be used again from python // Necessary if fork hooks have been not used to update interpreter state if (!up.call_osafterfork && !up.call_uwsgi_fork_hooks) -#ifdef HAS_NOT_PyOS_AfterFork_Child +#ifdef HAS_NOT_PYOS_FORK_STABLE_API PyOS_AfterFork(); #else PyOS_AfterFork_Child(); diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 961a46c49..538f3315b 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -41,12 +41,8 @@ #define HAS_NO_ERRORS_IN_PyFile_FromFd #endif -#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7 -#define HAS_NOT_PyOS_AfterFork_Child -#endif - -#if PY_MAJOR_VERSION < 3 -#define HAS_NOT_PyOS_AfterFork_Child +#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7) || PY_MAJOR_VERSION < 3 +#define HAS_NOT_PYOS_FORK_STABLE_API #endif #if PY_MAJOR_VERSION > 2 From 896ee575d0743e78237c7220007f4bbeaa8cedf8 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 16:00:35 +0200 Subject: [PATCH 2/4] core/uwsgi: set enable threads by default As it should have been since years :_) --- core/init.c | 2 +- core/uwsgi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/init.c b/core/init.c index d8ef00daa..62dfb0a8c 100644 --- a/core/init.c +++ b/core/init.c @@ -468,8 +468,8 @@ void sanitize_args() { uwsgi.cores = uwsgi.async; } + uwsgi.has_threads = 1; if (uwsgi.threads > 1) { - uwsgi.has_threads = 1; uwsgi.cores = uwsgi.threads; } diff --git a/core/uwsgi.c b/core/uwsgi.c index 9bcfe15ab..befaf9621 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -178,7 +178,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"freebind", no_argument, 0, "put socket in freebind mode", uwsgi_opt_true, &uwsgi.freebind, 0}, #endif {"map-socket", required_argument, 0, "map sockets to specific workers", uwsgi_opt_add_string_list, &uwsgi.map_socket, 0}, - {"enable-threads", no_argument, 'T', "enable threads", uwsgi_opt_true, &uwsgi.has_threads, 0}, + {"enable-threads", no_argument, 'T', "enable threads (stub option this is true by default)", uwsgi_opt_true, &uwsgi.has_threads, 0}, {"no-threads-wait", no_argument, 0, "do not wait for threads cancellation on quit/reload", uwsgi_opt_true, &uwsgi.no_threads_wait, 0}, {"auto-procname", no_argument, 0, "automatically set processes name to something meaningful", uwsgi_opt_true, &uwsgi.auto_procname, 0}, From 699dc20f8204ee18812951600b0221156d217530 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 16:32:31 +0200 Subject: [PATCH 3/4] plugins/python: handle cframe removal from CPython thread state Use current_frame instead --- plugins/python/python_plugin.c | 16 ++++++++++++++++ plugins/python/uwsgi_python.h | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 066bde668..a3969d571 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -1662,7 +1662,11 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 up.current_c_recursion_remaining[wsgi_req->async_id] = tstate->c_recursion_remaining; up.current_py_recursion_remaining[wsgi_req->async_id] = tstate->py_recursion_remaining; +#ifdef UWSGI_PY313 + up.current_frame[wsgi_req->async_id] = tstate->current_frame; +#else up.current_frame[wsgi_req->async_id] = tstate->cframe; +#endif #elif defined UWSGI_PY311 up.current_recursion_remaining[wsgi_req->async_id] = tstate->recursion_remaining; up.current_frame[wsgi_req->async_id] = tstate->cframe; @@ -1675,7 +1679,11 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 up.current_main_c_recursion_remaining = tstate->c_recursion_remaining; up.current_main_py_recursion_remaining = tstate->py_recursion_remaining; +#ifdef UWSGI_PY313 + up.current_main_frame = tstate->current_frame; +#else up.current_main_frame = tstate->cframe; +#endif #elif defined UWSGI_PY311 up.current_main_recursion_remaining = tstate->recursion_remaining; up.current_main_frame = tstate->cframe; @@ -1915,7 +1923,11 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 tstate->c_recursion_remaining = up.current_c_recursion_remaining[wsgi_req->async_id]; tstate->py_recursion_remaining = up.current_py_recursion_remaining[wsgi_req->async_id]; +#ifdef UWSGI_PY313 + tstate->current_frame = up.current_frame[wsgi_req->async_id]; +#else tstate->cframe = up.current_frame[wsgi_req->async_id]; +#endif #elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_recursion_remaining[wsgi_req->async_id]; tstate->cframe = up.current_frame[wsgi_req->async_id]; @@ -1928,7 +1940,11 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 tstate->c_recursion_remaining = up.current_main_c_recursion_remaining; tstate->py_recursion_remaining = up.current_main_py_recursion_remaining; +#ifdef UWSGI_PY313 + tstate->current_frame = up.current_main_frame; +#else tstate->cframe = up.current_main_frame; +#endif #elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_main_recursion_remaining; tstate->cframe = up.current_main_frame; diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 538f3315b..de83ac454 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -25,6 +25,10 @@ # define UWSGI_PY312 #endif +#if (PY_VERSION_HEX >= 0x030d0000) +# define UWSGI_PY313 +#endif + #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 7 #define HAS_NOT_PyMemoryView_FromBuffer #endif @@ -185,11 +189,19 @@ struct uwsgi_python { #ifdef UWSGI_PY312 int *current_c_recursion_remaining; int *current_py_recursion_remaining; +#ifdef UWSGI_PY313 + struct _PyInterpreterFrame **current_frame; +#else _PyCFrame **current_frame; +#endif int current_main_c_recursion_remaining; int current_main_py_recursion_remaining; +#ifdef UWSGI_PY313 + struct _PyInterpreterFrame *current_main_frame; +#else _PyCFrame *current_main_frame; +#endif #elif defined UWSGI_PY311 int *current_recursion_remaining; _PyCFrame **current_frame; From d6211dc1d3b00ec3a93d317644a9982d77a75b58 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 16:13:10 +0200 Subject: [PATCH 4/4] ci: test on Python 3.13 That is still in beta but removed private C APIs we were using. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88df146b6..2d4731914 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] test-suite: [python, deadlocks] steps: - name: Add deadnakes ppa