-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
gh-127266: avoid data races when updating type slots #131174
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
0e995ad
b173f17
d4ce112
44eb332
d132fab
658bcd5
ef2f07b
ce8536d
b97a4b4
ca00e74
6db4542
398ac14
75d6b71
895a86a
65e40f4
b68f1a1
9976b32
1b84486
2af9e49
f65e87c
395a6d3
e4f87e5
2a66555
2efac26
f7d2d36
f3fd35a
57c2a44
7c0ccf5
4fa77bb
caf6554
803d703
90ea541
0dc0faf
956e5d1
2bb710c
3a9bc96
d742a53
e7480c3
e2ea281
935bfca
1cff448
a81e9e3
f5df0c3
7db281c
da2a0ad
986f23a
c404ed4
55af4ba
0eb77da
16f15b2
0c328cc
64547e9
fff1bd2
5672352
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3490,6 +3490,9 @@ static int update_slot(PyTypeObject *, PyObject *); | |
static void fixup_slot_dispatchers(PyTypeObject *); | ||
static int type_new_set_names(PyTypeObject *); | ||
static int type_new_init_subclass(PyTypeObject *, PyObject *); | ||
#ifdef Py_GIL_DISABLED | ||
static bool has_slotdef(PyObject *); | ||
#endif | ||
|
||
/* | ||
* Helpers for __dict__ descriptor. We don't want to expose the dicts | ||
|
@@ -5943,6 +5946,23 @@ _Py_type_getattro(PyObject *tp, PyObject *name) | |
return _Py_type_getattro_impl(type, name, NULL); | ||
} | ||
|
||
#ifdef Py_GIL_DISABLED | ||
static int | ||
update_slot_world_stopped(PyTypeObject *type, PyObject *name) | ||
{ | ||
// Modification of type slots is protected by the global type | ||
// lock. However, type slots are read non-atomically without holding the | ||
// type lock. So, we need to stop-the-world while modifying slots, in | ||
// order to avoid data races. This is unfortunately quite expensive. | ||
int ret; | ||
PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
_PyEval_StopTheWorld(interp); | ||
ret = update_slot(type, name); | ||
_PyEval_StartTheWorld(interp); | ||
return ret; | ||
} | ||
#endif | ||
|
||
static int | ||
type_update_dict(PyTypeObject *type, PyDictObject *dict, PyObject *name, | ||
PyObject *value, PyObject **old_value) | ||
|
@@ -5972,9 +5992,15 @@ type_update_dict(PyTypeObject *type, PyDictObject *dict, PyObject *name, | |
return -1; | ||
} | ||
|
||
#if Py_GIL_DISABLED | ||
if (is_dunder_name(name) && has_slotdef(name)) { | ||
return update_slot_world_stopped(type, name); | ||
nascheme marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
#else | ||
if (is_dunder_name(name)) { | ||
return update_slot(type, name); | ||
} | ||
#endif | ||
|
||
return 0; | ||
} | ||
|
@@ -11002,6 +11028,21 @@ resolve_slotdups(PyTypeObject *type, PyObject *name) | |
#undef ptrs | ||
} | ||
|
||
#ifdef Py_GIL_DISABLED | ||
// Return true if "name" corresponds to at least one slot definition. This is | ||
// used to avoid calling update_slot() if is_dunder_name() is true but it's | ||
// not actually a slot. | ||
static bool | ||
has_slotdef(PyObject *name) | ||
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. |
||
{ | ||
for (pytype_slotdef *p = slotdefs; p->name_strobj; p++) { | ||
if (p->name_strobj == name) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
#endif | ||
|
||
/* Common code for update_slots_callback() and fixup_slot_dispatchers(). | ||
* | ||
|
@@ -11241,20 +11282,32 @@ fixup_slot_dispatchers(PyTypeObject *type) | |
END_TYPE_LOCK(); | ||
} | ||
|
||
// Called when __bases__ is re-assigned. | ||
static void | ||
update_all_slots(PyTypeObject* type) | ||
{ | ||
pytype_slotdef *p; | ||
|
||
ASSERT_TYPE_LOCK_HELD(); | ||
|
||
// Similar to update_slot_world_stopped(), this is required to | ||
// avoid races. We do it once here rather than once per-slot. | ||
#ifdef Py_GIL_DISABLED | ||
PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
_PyEval_StopTheWorld(interp); | ||
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.
|
||
#endif | ||
|
||
/* Clear the VALID_VERSION flag of 'type' and all its subclasses. */ | ||
type_modified_unlocked(type); | ||
|
||
for (p = slotdefs; p->name; p++) { | ||
/* update_slot returns int but can't actually fail */ | ||
update_slot(type, p->name_strobj); | ||
} | ||
|
||
#ifdef Py_GIL_DISABLED | ||
_PyEval_StartTheWorld(interp); | ||
#endif | ||
} | ||
|
||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.