Bug report
Bug description:
First of all, I'm not a threading expert and my understanding of the memory-ordering model may be wrong. So, if I'm wrong I will be happy to fix my knowledge lacoons.
I saw some inconsistency (from my sight) of loading and writing of managed dict pointer.
Non-atomic loads:
PyObject_VisitManagedDict
| Py_VISIT(_PyObject_ManagedDictPointer(obj)->dict); |
PyObject_ClearManagedDict
| Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict); |
_PyObject_GetDictPtr
| return (PyObject**)&_PyObject_ManagedDictPointer(obj)->dict; |
Non-atomic stores:
_PyObject_InitInlineValues
| for (size_ti=0; i<size; i++) { |
| values->values[i] =NULL; |
| } |
| _PyObject_ManagedDictPointer(obj)->dict=NULL; |
| } |
IIUC mixing of non-atomic loads/stores with atomic ones may lead to data races.
memory_order_acquire loads:
_PyObject_GetManagedDict
| _PyObject_GetManagedDict(PyObject*obj) |
| { |
| PyManagedDictPointer*dorv=_PyObject_ManagedDictPointer(obj); |
| return (PyDictObject*)FT_ATOMIC_LOAD_PTR_ACQUIRE(dorv->dict); |
| } |
memory_order_release stores:
_PyObject_MaterializeManagedDict_LockHeld
| FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict, |
| dict); |
| returndict; |
store_instance_attr_lock_held
| FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict, |
| (PyDictObject*)dict); |
| return0; |
ensure_managed_dict
| FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict, |
| (PyDictObject*)dict); |
memory_order_seq_cst stores:
set_dict_inline_values
| FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, new_dict); |
try_set_dict_inline_only_or_other_dict
| FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, |
| (PyDictObject*)Py_XNewRef(new_dict)); |
replace_dict_probably_inline_materialized
| FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, |
| (PyDictObject*)Py_XNewRef(new_dict)); |
| return0; |
IIUC mixing acquire/release with seq_cst may break total order of seq_cst operations.
Mixing with memory_order_seq_cst stores
_PyObject_SetManagedDict
| PyDictObject*dict=_PyObject_GetManagedDict(obj); |
| if (dict==NULL) { |
| set_dict_inline_values(obj, (PyDictObject*)new_dict); |
| return0; |
| } |
| if (_PyDict_DetachFromObject(dict, obj) ==0) { |
| _PyObject_ManagedDictPointer(obj)->dict= (PyDictObject*)Py_XNewRef(new_dict); |
| Py_DECREF(dict); |
| return0; |
| } |
_PyObject_SetManagedDict uses non-atomic load but stores with seq_cst mode so it is OK (IIUC), but following store without fence may lead to data race.
Are my findings valid or am I completely wrong?
cc @colesbury@kumaraditya303
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
Bug report
Bug description:
First of all, I'm not a threading expert and my understanding of the memory-ordering model may be wrong. So, if I'm wrong I will be happy to fix my knowledge lacoons.
I saw some inconsistency (from my sight) of loading and writing of managed dict pointer.
Non-atomic loads:
PyObject_VisitManagedDictcpython/Objects/dictobject.c
Line 7202 in 9ad0c7b
PyObject_ClearManagedDictcpython/Objects/dictobject.c
Line 7462 in 9ad0c7b
_PyObject_GetDictPtrcpython/Objects/object.c
Line 1541 in 9ad0c7b
Non-atomic stores:
_PyObject_InitInlineValuescpython/Objects/dictobject.c
Lines 6787 to 6791 in 9ad0c7b
IIUC mixing of non-atomic loads/stores with atomic ones may lead to data races.
memory_order_acquireloads:_PyObject_GetManagedDictcpython/Include/internal/pycore_object.h
Lines 932 to 936 in 9ad0c7b
memory_order_releasestores:_PyObject_MaterializeManagedDict_LockHeldcpython/Objects/dictobject.c
Lines 6827 to 6829 in 9ad0c7b
store_instance_attr_lock_heldcpython/Objects/dictobject.c
Lines 6925 to 6927 in 9ad0c7b
ensure_managed_dictcpython/Objects/dictobject.c
Lines 7494 to 7495 in 9ad0c7b
memory_order_seq_cststores:set_dict_inline_valuescpython/Objects/dictobject.c
Line 7225 in 9ad0c7b
try_set_dict_inline_only_or_other_dictcpython/Objects/dictobject.c
Lines 7252 to 7253 in 9ad0c7b
replace_dict_probably_inline_materializedcpython/Objects/dictobject.c
Lines 7287 to 7289 in 9ad0c7b
IIUC mixing acquire/release with seq_cst may break total order of seq_cst operations.
Mixing with
memory_order_seq_cststores_PyObject_SetManagedDictcpython/Objects/dictobject.c
Lines 7356 to 7365 in 9ad0c7b
_PyObject_SetManagedDictuses non-atomic load but stores withseq_cstmode so it is OK (IIUC), but following store without fence may lead to data race.Are my findings valid or am I completely wrong?
cc @colesbury@kumaraditya303
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
PyObject_GenericSetDict#133988PyObject_GenericSetDict(GH-133988) #134354