Skip to content

Commit d6f77e6

Browse files
gh-116738: make entering of contextvars.Context thread safe (#143074)
1 parent 12d363b commit d6f77e6

File tree

2 files changed

+11
-5
lines changed

2 files changed

+11
-5
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix thread safety of :func:`contextvars.Context.run`.

Python/context.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,20 @@ _PyContext_Enter(PyThreadState *ts, PyObject *octx)
195195
{
196196
ENSURE_Context(octx, -1)
197197
PyContext *ctx = (PyContext *)octx;
198+
#ifdef Py_GIL_DISABLED
199+
int already_entered = _Py_atomic_exchange_int(&ctx->ctx_entered, 1);
200+
#else
201+
int already_entered = ctx->ctx_entered;
202+
ctx->ctx_entered = 1;
203+
#endif
198204

199-
if (ctx->ctx_entered) {
205+
if (already_entered) {
200206
_PyErr_Format(ts, PyExc_RuntimeError,
201207
"cannot enter context: %R is already entered", ctx);
202208
return -1;
203209
}
204210

205211
ctx->ctx_prev = (PyContext *)ts->context; /* borrow */
206-
ctx->ctx_entered = 1;
207-
208212
ts->context = Py_NewRef(ctx);
209213
context_switched(ts);
210214
return 0;
@@ -225,8 +229,9 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx)
225229
{
226230
ENSURE_Context(octx, -1)
227231
PyContext *ctx = (PyContext *)octx;
232+
int already_entered = FT_ATOMIC_LOAD_INT_RELAXED(ctx->ctx_entered);
228233

229-
if (!ctx->ctx_entered) {
234+
if (!already_entered) {
230235
PyErr_Format(PyExc_RuntimeError,
231236
"cannot exit context: %R has not been entered", ctx);
232237
return -1;
@@ -243,7 +248,7 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx)
243248
Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev);
244249

245250
ctx->ctx_prev = NULL;
246-
ctx->ctx_entered = 0;
251+
FT_ATOMIC_STORE_INT(ctx->ctx_entered, 0);
247252
context_switched(ts);
248253
return 0;
249254
}

0 commit comments

Comments
 (0)