GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-154527: Fix data-race in `defaultdict_repr` and `defaultdict_missing` by brijkapadia · Pull Request #154560 · python/cpython

Conversation

@brijkapadia

@brijkapadia brijkapadia commented Jul 23, 2026 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Adds a critical section around defaultdict_repr and defaultdict_missing.

  • Issue: defaultdict reads default_factory as a borrowed reference in defdict_repr / defdict_missing under free-threading #154527

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@brijkapadia
pythongh-154527: Fix data-race in defaultdict_repr and `defaultdict…
01165d4
…_missing`
@brijkapadia
brijkapadia requested a review from rhettinger as a code owner July 23, 2026 18:13
@bedevere-app bedevere-app Bot mentioned this pull request Jul 23, 2026
defaultdict reads default_factory as a borrowed reference in defdict_repr / defdict_missing under free-threading #154527
Open
@bedevere-app bedevere-app Bot added the awaiting review label Jul 23, 2026
@brijkapadia
Merge remote-tracking branch 'upstream' into defaultdict-race
e1e314e
nouraellm
nouraellm reviewed Aug 9, 2026
View reviewed changes
Comment thread Modules/_collectionsmodule.c
Comment on lines +2423 to +2432
static PyObject *
defdict_repr(PyObject *op)
{
PyObject *return_value;
Py_BEGIN_CRITICAL_SECTION(op);
return_value = defdict_repr_lock_held(op);
Py_END_CRITICAL_SECTION();
return return_value;
}

@nouraellm nouraellm Aug 9, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @brijkapadia I stumbled across this PR while looking at free-threading issues and thought I’d share a slightly different approach.

The current change (clinic @critical_section on __missing__ + wrapping the whole of defdict_repr) holds the per-object lock across the call to the factory and across PyObject_Repr.

That fixes the data race but it also means we’re executing arbitrary Python while still holding the lock so if the factory (or a custom __repr__) ends up touching the same defaultdict again or tries to take other locks then things can get uncomfortable.

The pattern that seems preferred in the other free-threading cleanups is to take a strong reference under the lock and then drop it immediately. For the repr path that would look like:

Suggested change
static PyObject *
defdict_repr(PyObject *op)
{
PyObject *return_value;
Py_BEGIN_CRITICAL_SECTION(op);
return_value = defdict_repr_lock_held(op);
Py_END_CRITICAL_SECTION();
return return_value;
}
static PyObject *
defdict_repr(PyObject *op)
{
defdictobject *dd = defdictobject_CAST(op);
PyObject *baserepr;
PyObject *defrepr;
PyObject *factory;
PyObject *result;
baserepr = PyDict_Type.tp_repr(op);
if (baserepr == NULL)
return NULL;
Py_BEGIN_CRITICAL_SECTION(dd);
factory = Py_XNewRef(dd->default_factory);
Py_END_CRITICAL_SECTION();
if (factory == NULL) {
defrepr = PyUnicode_FromString("None");
}
else {
int status = Py_ReprEnter(factory);
if (status != 0) {
if (status < 0) {
Py_DECREF(baserepr);
Py_DECREF(factory);
return NULL;
}
defrepr = PyUnicode_FromString("...");
}
else {
defrepr = PyObject_Repr(factory);
Py_ReprLeave(factory);
}
Py_DECREF(factory);
}
if (defrepr == NULL) {
Py_DECREF(baserepr);
return NULL;
}
result = PyUnicode_FromFormat("%s(%U, %U)",
_PyType_Name(Py_TYPE(dd)),
defrepr, baserepr);
Py_DECREF(defrepr);
Py_DECREF(baserepr);
return result;
}

That way the lock is only held for the tiny window needed to take the reference and everything after that is safe because we own the object.

Good catch btw and have a great day!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@github-actions

github-actions Bot commented Nov 11, 2026

Copy link
Copy Markdown

This PR is stale because it has been open for 90 days with no activity.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions github-actions Bot added the stale Stale PR or inactive for long period of time. label Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@rhettinger rhettinger Awaiting requested review from rhettinger rhettinger is a code owner
1 more reviewer
@nouraellm nouraellm nouraellm left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting review stale Stale PR or inactive for long period of time.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@brijkapadia @nouraellm
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.