Skip to content

Commit cb14452

Browse files
committed
Add thread safety around inline dict matieralization
1 parent 0ff1709 commit cb14452

File tree

11 files changed

+254
-139
lines changed

11 files changed

+254
-139
lines changed

Include/internal/pycore_dict.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ extern "C" {
99
# error "this header requires Py_BUILD_CORE define"
1010
#endif
1111

12-
#include "pycore_freelist.h" // _PyFreeListState
13-
#include "pycore_identifier.h" // _Py_Identifier
14-
#include "pycore_object.h" // PyManagedDictPointer
12+
#include "pycore_freelist.h" // _PyFreeListState
13+
#include "pycore_identifier.h" // _Py_Identifier
14+
#include "pycore_object.h" // PyManagedDictPointer
15+
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_ACQUIRE
1516

1617
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
1718
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
@@ -248,7 +249,7 @@ _PyDict_NotifyEvent(PyInterpreterState *interp,
248249
return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK);
249250
}
250251

251-
extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj);
252+
extern PyObject *_PyObject_MaterializeManagedDict(PyObject *obj);
252253

253254
PyAPI_FUNC(PyObject *)_PyDict_FromItems(
254255
PyObject *const *keys, Py_ssize_t keys_offset,
@@ -276,19 +277,16 @@ _PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix)
276277
static inline size_t
277278
shared_keys_usable_size(PyDictKeysObject *keys)
278279
{
279-
#ifdef Py_GIL_DISABLED
280280
// dk_usable will decrease for each instance that is created and each
281281
// value that is added. dk_nentries will increase for each value that
282282
// is added. We want to always return the right value or larger.
283283
// We therefore increase dk_nentries first and we decrease dk_usable
284284
// second, and conversely here we read dk_usable first and dk_entries
285285
// second (to avoid the case where we read entries before the increment
286286
// and read usable after the decrement)
287-
return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) +
288-
_Py_atomic_load_ssize_acquire(&keys->dk_nentries));
289-
#else
290-
return (size_t)keys->dk_nentries + (size_t)keys->dk_usable;
291-
#endif
287+
Py_ssize_t dk_usable = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_usable);
288+
Py_ssize_t dk_nentries = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_nentries);
289+
return dk_nentries + dk_usable;
292290
}
293291

294292
static inline size_t

Include/internal/pycore_object.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern "C" {
1212
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
1313
#include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall()
1414
#include "pycore_interp.h" // PyInterpreterState.gc
15+
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_PTR_RELAXED
1516
#include "pycore_pystate.h" // _PyInterpreterState_GET()
1617

1718
/* Check if an object is consistent. For example, ensure that the reference
@@ -621,10 +622,10 @@ extern PyObject* _PyType_GetDocFromInternalDoc(const char *, const char *);
621622
extern PyObject* _PyType_GetTextSignatureFromInternalDoc(const char *, const char *, int);
622623

623624
void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp);
624-
extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
625-
PyObject *name, PyObject *value);
626-
PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
627-
PyObject *name);
625+
extern int _PyObject_TryStoreInstanceAttribute(PyObject *obj,
626+
PyObject *name, PyObject *value);
627+
extern int _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name,
628+
PyObject **attr);
628629

629630
#ifdef Py_GIL_DISABLED
630631
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-1)
@@ -645,6 +646,20 @@ _PyObject_ManagedDictPointer(PyObject *obj)
645646
return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET);
646647
}
647648

649+
static inline PyDictObject *
650+
_PyObject_GetManagedDict(PyObject *obj)
651+
{
652+
PyManagedDictPointer *dorv = _PyObject_ManagedDictPointer(obj);
653+
return (PyDictObject *)FT_ATOMIC_LOAD_PTR_RELAXED(dorv->dict);
654+
}
655+
656+
static inline void
657+
_PyObject_SetManagedDict(PyObject *obj, PyObject *dict)
658+
{
659+
PyManagedDictPointer *ptr = _PyObject_ManagedDictPointer(obj);
660+
FT_ATOMIC_STORE_PTR_RELEASE(ptr->dict, dict);
661+
}
662+
648663
static inline PyDictValues *
649664
_PyObject_InlineValues(PyObject *obj)
650665
{

Include/internal/pycore_pyatomic_ft_wrappers.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@ extern "C" {
2121

2222
#ifdef Py_GIL_DISABLED
2323
#define FT_ATOMIC_LOAD_SSIZE(value) _Py_atomic_load_ssize(&value)
24+
#define FT_ATOMIC_LOAD_SSIZE_ACQUIRE(value) \
25+
_Py_atomic_load_ssize_acquire(&value)
2426
#define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) \
2527
_Py_atomic_load_ssize_relaxed(&value)
28+
#define FT_ATOMIC_LOAD_PTR_RELAXED(value) \
29+
_Py_atomic_load_ptr_relaxed(&value)
30+
#define FT_ATOMIC_LOAD_UINT8_RELAXED(value) \
31+
_Py_atomic_load_uint8_relaxed(&value)
2632
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) \
2733
_Py_atomic_store_ptr_relaxed(&value, new_value)
2834
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) \
@@ -31,7 +37,10 @@ extern "C" {
3137
_Py_atomic_store_ssize_relaxed(&value, new_value)
3238
#else
3339
#define FT_ATOMIC_LOAD_SSIZE(value) value
40+
#define FT_ATOMIC_LOAD_SSIZE_ACQUIRE(value) value
3441
#define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) value
42+
#define FT_ATOMIC_LOAD_PTR_RELAXED(value) value
43+
#define FT_ATOMIC_LOAD_UINT8_RELAXED(value) value
3544
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value
3645
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value
3746
#define FT_ATOMIC_STORE_SSIZE_RELAXED(value, new_value) value = new_value

0 commit comments

Comments
 (0)