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 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}, diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 6c5be6c7f..a3969d571 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(); @@ -1664,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; @@ -1677,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; @@ -1917,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]; @@ -1930,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; @@ -2164,7 +2178,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..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 @@ -41,12 +45,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 @@ -189,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;