Skip to content

Commit 5ec0a29

Browse files
committed
Make instance attributes stored in inline "dict" thread safe
1 parent 0edde64 commit 5ec0a29

12 files changed

Lines changed: 363 additions & 151 deletions

File tree

Include/cpython/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ PyAPI_FUNC(int) _PyTrash_cond(PyObject *op, destructor dealloc);
490490
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
491491

492492
PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
493+
PyAPI_FUNC(void) _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict);
493494
PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
494495

495496
#define TYPE_MAX_WATCHERS 8

Include/internal/pycore_dict.h

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#ifndef Py_INTERNAL_DICT_H
32
#define Py_INTERNAL_DICT_H
43
#ifdef __cplusplus
@@ -9,9 +8,10 @@ extern "C" {
98
# error "this header requires Py_BUILD_CORE define"
109
#endif
1110

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

1616
// Unsafe flavor of PyDict_GetItemWithError(): no error checking
1717
extern PyObject* _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
@@ -249,7 +249,7 @@ _PyDict_NotifyEvent(PyInterpreterState *interp,
249249
return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK);
250250
}
251251

252-
extern PyDictObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj);
252+
extern PyDictObject *_PyObject_MaterializeManagedDict(PyObject *obj);
253253

254254
PyAPI_FUNC(PyObject *)_PyDict_FromItems(
255255
PyObject *const *keys, Py_ssize_t keys_offset,
@@ -277,19 +277,16 @@ _PyDictValues_AddToInsertionOrder(PyDictValues *values, Py_ssize_t ix)
277277
static inline size_t
278278
shared_keys_usable_size(PyDictKeysObject *keys)
279279
{
280-
#ifdef Py_GIL_DISABLED
281280
// dk_usable will decrease for each instance that is created and each
282281
// value that is added. dk_nentries will increase for each value that
283282
// is added. We want to always return the right value or larger.
284283
// We therefore increase dk_nentries first and we decrease dk_usable
285284
// second, and conversely here we read dk_usable first and dk_entries
286285
// second (to avoid the case where we read entries before the increment
287286
// and read usable after the decrement)
288-
return (size_t)(_Py_atomic_load_ssize_acquire(&keys->dk_usable) +
289-
_Py_atomic_load_ssize_acquire(&keys->dk_nentries));
290-
#else
291-
return (size_t)keys->dk_nentries + (size_t)keys->dk_usable;
292-
#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;
293290
}
294291

295292
static inline size_t

Include/internal/pycore_object.h

Lines changed: 12 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
@@ -611,10 +612,10 @@ extern PyObject* _PyType_GetDocFromInternalDoc(const char *, const char *);
611612
extern PyObject* _PyType_GetTextSignatureFromInternalDoc(const char *, const char *, int);
612613

613614
void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp);
614-
extern int _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
615-
PyObject *name, PyObject *value);
616-
PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
617-
PyObject *name);
615+
extern int _PyObject_TryStoreInstanceAttribute(PyObject *obj,
616+
PyObject *name, PyObject *value);
617+
extern int _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name,
618+
PyObject **attr);
618619

619620
#ifdef Py_GIL_DISABLED
620621
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-1)
@@ -635,6 +636,13 @@ _PyObject_ManagedDictPointer(PyObject *obj)
635636
return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET);
636637
}
637638

639+
static inline PyDictObject *
640+
_PyObject_GetManagedDict(PyObject *obj)
641+
{
642+
PyManagedDictPointer *dorv = _PyObject_ManagedDictPointer(obj);
643+
return (PyDictObject *)FT_ATOMIC_LOAD_PTR_RELAXED(dorv->dict);
644+
}
645+
638646
static inline PyDictValues *
639647
_PyObject_InlineValues(PyObject *obj)
640648
{

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)