-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
gh-120198: Stop the world when setting __class__ on free-threaded build #120672
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
Changes from 1 commit
2e5667e
b482f87
d2a7095
8a562ce
a88d238
8a25bfb
13486cb
a409ab2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6599,6 +6599,12 @@ object_set_class(PyObject *self, PyObject *value, void *closure) | |
} | ||
|
||
PyTypeObject *oldto = Py_TYPE(self); | ||
#ifdef Py_GIL_DISABLED | ||
PyInterpreterState *interp = PyInterpreterState_Get(); | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// The real Py_TYPE(self) (`oldto`) may have changed from | ||
// underneath us in another thread, so we re-fetch it here. | ||
_PyEval_StopTheWorld(interp); | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#endif | ||
|
||
/* In versions of CPython prior to 3.5, the code in | ||
compatible_for_assignment was not set up to correctly check for memory | ||
|
@@ -6656,7 +6662,7 @@ object_set_class(PyObject *self, PyObject *value, void *closure) | |
PyErr_Format(PyExc_TypeError, | ||
"__class__ assignment only supported for mutable types " | ||
"or ModuleType subclasses"); | ||
return -1; | ||
goto err; | ||
} | ||
|
||
if (compatible_for_assignment(oldto, newto, "__class__")) { | ||
|
@@ -6665,47 +6671,48 @@ object_set_class(PyObject *self, PyObject *value, void *closure) | |
if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) { | ||
PyDictObject *dict = _PyObject_MaterializeManagedDict(self); | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (dict == NULL) { | ||
return -1; | ||
goto err; | ||
} | ||
|
||
bool error = false; | ||
|
||
Py_BEGIN_CRITICAL_SECTION2(self, dict); | ||
|
||
// If we raced after materialization and replaced the dict | ||
// then the materialized dict should no longer have the | ||
// inline values in which case detach is a nop. | ||
// Note: we don't need to lock here because the world should be stopped. | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
assert(_PyObject_GetManagedDict(self) == dict || | ||
dict->ma_values != _PyObject_InlineValues(self)); | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (_PyDict_DetachFromObject(dict, self) < 0) { | ||
error = true; | ||
goto err; | ||
} | ||
|
||
Py_END_CRITICAL_SECTION2(); | ||
if (error) { | ||
return -1; | ||
} | ||
} | ||
if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE) { | ||
Py_INCREF(newto); | ||
} | ||
Py_BEGIN_CRITICAL_SECTION(self); | ||
// The real Py_TYPE(self) (`oldto`) may have changed from | ||
// underneath us in another thread, so we re-fetch it here. | ||
|
||
oldto = Py_TYPE(self); | ||
Py_SET_TYPE(self, newto); | ||
Py_END_CRITICAL_SECTION(); | ||
|
||
if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE) { | ||
Py_DECREF(oldto); | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
RARE_EVENT_INC(set_class); | ||
|
||
#ifdef Py_GIL_DISABLED | ||
_PyEval_StartTheWorld(interp); | ||
#endif | ||
return 0; | ||
} | ||
else { | ||
return -1; | ||
goto err; | ||
} | ||
err: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To have a more regular There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think @vstinner's suggestion is fine. Or you can refactor the parts that should be in a stop-the-world call into it's own function, like we often do for locks. Another advantage of moving the body to a separate function is that it makes it more clear what data crosses the stop-the-world boundary -- some data loaded before the stop-the-world call may not be valid after it. |
||
#ifdef Py_GIL_DISABLED | ||
_PyEval_StartTheWorld(interp); | ||
#endif | ||
return -1; | ||
} | ||
|
||
static PyGetSetDef object_getsets[] = { | ||
|
Uh oh!
There was an error while loading. Please reload this page.