Skip to content

gh-124470: Fix crash when reading from object instance dictionary while replacing it #122489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 21, 2024

Conversation

DinoV
Copy link
Contributor

@DinoV DinoV commented Jul 31, 2024

Currently when we have one thread reading an attribute from an object and another thread replacing the dictionary we can crash. This is because the reader thread is actually using a borrowed reference to the object and the writer will decref the dictionary and de-allocate it when replacing the dictionary.

This fixes this by changing the decref of the previous dictionary to be delayed via QSBR. We get rid of the GC_SET_SHARED_INLINE flag which is not being used anywhere and instead we simply queue the decref via _PyObject_XDecRefDelayed.

When we process these objects during GC we need to be careful because we don't want to run object finalizers during the destruction of the objects. Instead we do the same thing we do w/ merged dec refs and queue the objects to be decref'd once the world has resumed.

@DinoV DinoV force-pushed the nogil_dict_incref branch 3 times, most recently from c26d357 to 5a1b6fd Compare July 31, 2024 17:50
@DinoV DinoV force-pushed the nogil_dict_incref branch 4 times, most recently from 16ca39f to 0988d1a Compare August 13, 2024 21:50
@DinoV DinoV force-pushed the nogil_dict_incref branch 2 times, most recently from 0169a30 to bd1c1a7 Compare September 24, 2024 20:44
@DinoV DinoV changed the title Incref dict when it's usage is borrowed gh-124470: Incref dict when it's usage is borrowed Sep 24, 2024
@DinoV DinoV force-pushed the nogil_dict_incref branch 4 times, most recently from 0146a13 to 2d05896 Compare September 25, 2024 22:58
@DinoV DinoV requested a review from colesbury September 25, 2024 22:59
@DinoV DinoV marked this pull request as ready for review September 25, 2024 23:33
Copy link
Contributor

@colesbury colesbury left a comment

Choose a reason for hiding this comment

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

The overall approach makes sense to me. A few comments below.

}

FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
(PyDictObject *)Py_XNewRef(new_dict));
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: align (PyDictObject *)Py_XNewRef(new_dict)

@DinoV DinoV force-pushed the nogil_dict_incref branch 4 times, most recently from 1a199bf to dc3d5ae Compare September 26, 2024 23:16
@DinoV DinoV changed the title gh-124470: Incref dict when it's usage is borrowed gh-124470: Fix crash when reading from object instance dictionary while replacing it Sep 27, 2024
Copy link
Contributor

@colesbury colesbury left a comment

Choose a reason for hiding this comment

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

LGTM. A few minor comments below

@@ -285,6 +285,7 @@ PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int);
// zero. Otherwise, the thread gives up ownership and merges the reference
// count fields.
PyAPI_FUNC(void) _Py_MergeZeroLocalRefcount(PyObject *);

Copy link
Contributor

Choose a reason for hiding this comment

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

Stray whitespace change

}

static int
set_or_clear_managed_dict(PyObject *obj, PyObject *new_dict, bool clear)
{
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
assert(_PyObject_InlineValuesConsistencyCheck(obj));
Copy link
Contributor

Choose a reason for hiding this comment

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

Do these consistency checks need to be within some sort of lock?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The type flag one is fine, I'll add some ifdefs and lock for the inline check.

Comment on lines 7149 to 7113
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
(PyDictObject *)Py_XNewRef(new_dict));
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe align the wrapped line

@colesbury colesbury added the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Sep 30, 2024
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @colesbury for commit dc3d5ae 🤖

If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Sep 30, 2024
@DinoV DinoV force-pushed the nogil_dict_incref branch 2 times, most recently from 117a19e to 367e1f4 Compare November 20, 2024 21:57
@DinoV DinoV force-pushed the nogil_dict_incref branch from 367e1f4 to c9aee55 Compare November 20, 2024 22:04
@DinoV DinoV added the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Nov 21, 2024
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @DinoV for commit c9aee55 🤖

If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Nov 21, 2024
@DinoV DinoV merged commit bf542f8 into python:main Nov 21, 2024
54 of 55 checks passed
ebonnal pushed a commit to ebonnal/cpython that referenced this pull request Jan 12, 2025
…ry while replacing it (python#122489)

Delay free a dictionary when replacing it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants