Skip to content

Conversation

corona10
Copy link
Member

@corona10 corona10 commented May 20, 2025

_PyObject_XSetRefDelayed(PyObject **ptr, PyObject *value)
{
PyObject *old = *ptr;
FT_ATOMIC_STORE_PTR_RELEASE(*ptr, Py_NewRef(value));
Copy link
Contributor

Choose a reason for hiding this comment

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

The default build implementation in pycore_pymem.h doesn't include the Py_NewRef().

My preference is to keep the Py_NewRef(value) in the callers, i.e.:

_PyObject_XSetRefDelayed(dictptr, Py_NewRef(value));

to match the semantics of Py_XSETREF.

@corona10
Copy link
Member Author

Interesting... CI passed in my local but not in the CI.. :(

@corona10
Copy link
Member Author

Ah okay..

test_aclose (test.test_asyncgen.TestUnawaitedWarnings.test_aclose) ... Assertion failed: (!_Py_IsImmortal(op)), function _Py_ExplicitMergeRefcount, file object.c, line 467.

@corona10 corona10 changed the title gh-133931: Introduce _PyObject_XSetRefDelayed to replace Py_XSETREF [WIP] gh-133931: Introduce _PyObject_XSetRefDelayed to replace Py_XSETREF May 21, 2025
@markshannon
Copy link
Member

Why do we need _PyObject_XSetRefDelayed? It looks like all uses of it are wrapped in critical sections where the old Py_XSETREF would work.

Also, what is "delayed"? Either choose a clearer name, or add an explanatory comment in the header file.

@corona10 corona10 changed the title [WIP] gh-133931: Introduce _PyObject_XSetRefDelayed to replace Py_XSETREF gh-133931: Introduce _PyObject_XSetRefDelayed to replace Py_XSETREF Jun 7, 2025
@corona10 corona10 marked this pull request as ready for review June 7, 2025 13:34
@corona10
Copy link
Member Author

corona10 commented Jun 7, 2025

Why do we need _PyObject_XSetRefDelayed? It looks like all uses of it are wrapped in critical sections where the old Py_XSETREF would work.

IIUC, the critical section is for the root object to prevent other threads from mutating the child object, and a delayed reference to prevent use-after-free from other threads that reference the child object.

@corona10 corona10 requested a review from colesbury June 7, 2025 13:42
@corona10 corona10 requested a review from kumaraditya303 June 11, 2025 13:46
@corona10
Copy link
Member Author

Gentle ping @kumaraditya303 @vstinner

@corona10 corona10 requested a review from vstinner June 15, 2025 06:32
}
Py_XSETREF(op->gi_name, Py_NewRef(value));
Py_BEGIN_CRITICAL_SECTION(self);
// To prevent use-after-free from other threads that reference the gi_name.
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add gh-133931: prefix to these comments.

#ifdef Py_GIL_DISABLED
// Same as `Py_XSETREF` but in free-threading, it stores the object atomically
// and queues the old object to be decrefed at a safe point using QSBR.
PyAPI_FUNC(void) _PyObject_XSetRefDelayed(PyObject **p_obj, PyObject *obj);
Copy link
Member

Choose a reason for hiding this comment

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

Why not adding this function to pycore_object.h header instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because _PyObject_XDecRefDelayed is also declared here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's fine to place _PyObject_XDecRefDelayed and _PyObject_XSetRefDelayed in pycore_object.h instead if you prefer. (I agree that they should probably be in the same header as each other).

}
Py_BEGIN_CRITICAL_SECTION(obj);
// To prevent use-after-free from other threads that reference the __dict__
// gh-133931: To prevent use-after-free from other threads that reference
Copy link
Member

@efimov-mikhail efimov-mikhail Jun 16, 2025

Choose a reason for hiding this comment

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

Is it correct issue reference for __dict__ too?

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.

Looks good - a few comments below

#ifdef Py_GIL_DISABLED
// Same as `Py_XSETREF` but in free-threading, it stores the object atomically
// and queues the old object to be decrefed at a safe point using QSBR.
PyAPI_FUNC(void) _PyObject_XSetRefDelayed(PyObject **p_obj, PyObject *obj);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's fine to place _PyObject_XDecRefDelayed and _PyObject_XSetRefDelayed in pycore_object.h instead if you prefer. (I agree that they should probably be in the same header as each other).

@corona10 corona10 requested a review from colesbury June 17, 2025 02:48
Copy link
Member

@efimov-mikhail efimov-mikhail left a comment

Choose a reason for hiding this comment

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

IMO, it's better to use "gh-133980" comment for __dict__ and "gh-133931" comment for gi_name and gi_qualname.
Moreover, we've moved _PyObject_XSetRefDelayed and so we have to make little changes in includes.

Copy link
Member

@efimov-mikhail efimov-mikhail left a comment

Choose a reason for hiding this comment

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

LGTM

@corona10 corona10 requested a review from vstinner June 17, 2025 14:25
Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

LGTM

@corona10 corona10 merged commit 52be7f4 into python:main Jun 17, 2025
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment