From 68f8ec6a2d280eaec6e2cafeae72a2dc20292b59 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sun, 21 Dec 2025 15:12:56 +0530 Subject: [PATCH 1/3] make entering of context thread safe --- Python/context.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Python/context.c b/Python/context.c index 606ce4b1c8f60a..a91e41ade57590 100644 --- a/Python/context.c +++ b/Python/context.c @@ -195,16 +195,20 @@ _PyContext_Enter(PyThreadState *ts, PyObject *octx) { ENSURE_Context(octx, -1) PyContext *ctx = (PyContext *)octx; +#ifdef Py_GIL_DISABLED + int already_entered = _Py_atomic_exchange_int(&ctx->ctx_entered, 1); +#else + int already_entered = ctx->ctx_entered; + ctx->ctx_entered = 1; +#endif - if (ctx->ctx_entered) { + if (already_entered) { _PyErr_Format(ts, PyExc_RuntimeError, "cannot enter context: %R is already entered", ctx); return -1; } ctx->ctx_prev = (PyContext *)ts->context; /* borrow */ - ctx->ctx_entered = 1; - ts->context = Py_NewRef(ctx); context_switched(ts); return 0; @@ -225,8 +229,14 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx) { ENSURE_Context(octx, -1) PyContext *ctx = (PyContext *)octx; +#ifdef Py_GIL_DISABLED + int already_entered = _Py_atomic_exchange_int(&ctx->ctx_entered, 0); +#else + int already_entered = ctx->ctx_entered; + ctx->ctx_entered = 0; +#endif - if (!ctx->ctx_entered) { + if (!already_entered) { PyErr_Format(PyExc_RuntimeError, "cannot exit context: %R has not been entered", ctx); return -1; @@ -243,7 +253,6 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx) Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev); ctx->ctx_prev = NULL; - ctx->ctx_entered = 0; context_switched(ts); return 0; } From 7fb7931d923a92ba5b36ba3a2b9e6dbc623d3f5e Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:22:11 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-12-22-16-22-02.gh-issue-116738.caQuq_.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-22-16-22-02.gh-issue-116738.caQuq_.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-22-16-22-02.gh-issue-116738.caQuq_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-22-16-22-02.gh-issue-116738.caQuq_.rst new file mode 100644 index 00000000000000..9fa2039ca81d35 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-22-16-22-02.gh-issue-116738.caQuq_.rst @@ -0,0 +1 @@ +Fix thread safety of :func:`contextvars.Context.run`. From cc959daa04337b5433c22563d8bb95aa6023c17d Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 22 Dec 2025 21:58:15 +0530 Subject: [PATCH 3/3] code review: make exiting thread safe --- Python/context.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Python/context.c b/Python/context.c index a91e41ade57590..62b582f271ffe5 100644 --- a/Python/context.c +++ b/Python/context.c @@ -229,12 +229,7 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx) { ENSURE_Context(octx, -1) PyContext *ctx = (PyContext *)octx; -#ifdef Py_GIL_DISABLED - int already_entered = _Py_atomic_exchange_int(&ctx->ctx_entered, 0); -#else - int already_entered = ctx->ctx_entered; - ctx->ctx_entered = 0; -#endif + int already_entered = FT_ATOMIC_LOAD_INT_RELAXED(ctx->ctx_entered); if (!already_entered) { PyErr_Format(PyExc_RuntimeError, @@ -253,6 +248,7 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx) Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev); ctx->ctx_prev = NULL; + FT_ATOMIC_STORE_INT(ctx->ctx_entered, 0); context_switched(ts); return 0; }