Skip to content

Commit f4f8b93

Browse files
authored
gh-159739: Preserve ContextVar cache updates from finalizers (#160197)
Resetting a ContextVar can finalize the replaced value, whose finalizer may set the variable again. Updating the lookup cache after releasing the old context root overwrites that newer cached value, so get() can return a stale value or use a freed object. Update the cache before the old root can be released. Backport to 3.15 and 3.14.
1 parent 3e7393b commit f4f8b93

3 files changed

Lines changed: 16 additions & 2 deletions

File tree

‎Lib/test/test_context.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,19 @@ def fun():
336336
contextvars.copy_context().run(fun)
337337
self.assertEqual(c.get(), [])
338338

339+
@isolated_context
340+
def test_reset_with_reentrant_finalizer(self):
341+
var = contextvars.ContextVar('var')
342+
var.set('original')
343+
344+
class Reenter:
345+
def __del__(self):
346+
var.set('finalizer')
347+
348+
token = var.set(Reenter())
349+
var.reset(token)
350+
self.assertEqual(var.get(), 'finalizer')
351+
339352
def test_context_copy_1(self):
340353
ctx1 = contextvars.Context()
341354
c = contextvars.ContextVar('c', default=42)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :class:`contextvars.ContextVar` returning a stale value after a reentrant finalizer changes it during :meth:`~contextvars.ContextVar.reset`.

‎Python/context.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -872,13 +872,13 @@ contextvar_set(PyContextVar *var, PyObject *val)
872872
return -1;
873873
}
874874

875-
context_set_vars(ctx, new_vars);
876-
877875
#ifndef Py_GIL_DISABLED
878876
var->var_cached = val; /* borrow */
879877
var->var_cached_tsid = ts->id;
880878
var->var_cached_tsver = ts->context_ver;
881879
#endif
880+
/* Releasing the old variables may run a finalizer that updates the cache. */
881+
context_set_vars(ctx, new_vars);
882882
return 0;
883883
}
884884

0 commit comments

Comments
 (0)