Skip to content

Commit 46e3d83

Browse files
committed
Use _PyType_HasFeature() to check tp_flags.
In the free-threaded build, an atomic load is needed to safely read `tp_flags`, avoiding data races. Replace bit tests on `tp->tp_flags` with calls to `_PyType_HasFeature()`. When multiple bits are being returned as a result of the test, use `PyType_GetFlags()`.
1 parent d7bb7c7 commit 46e3d83

File tree

15 files changed

+198
-196
lines changed

15 files changed

+198
-196
lines changed

Include/internal/pycore_object.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ static inline PyObject **
810810
_PyObject_GET_WEAKREFS_LISTPTR(PyObject *op)
811811
{
812812
if (PyType_Check(op) &&
813-
((PyTypeObject *)op)->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
813+
_PyType_HasFeature((PyTypeObject *)op, _Py_TPFLAGS_STATIC_BUILTIN)) {
814814
PyInterpreterState *interp = _PyInterpreterState_GET();
815815
managed_static_type_state *state = _PyStaticType_GetState(
816816
interp, (PyTypeObject *)op);
@@ -837,7 +837,7 @@ static inline PyWeakReference **
837837
_PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(PyObject *op)
838838
{
839839
assert(!PyType_Check(op) ||
840-
((PyTypeObject *)op)->tp_flags & Py_TPFLAGS_HEAPTYPE);
840+
_PyType_HasFeature((PyTypeObject *)op, Py_TPFLAGS_HEAPTYPE));
841841
Py_ssize_t offset = Py_TYPE(op)->tp_weaklistoffset;
842842
return (PyWeakReference **)((char *)op + offset);
843843
}
@@ -935,7 +935,7 @@ typedef union {
935935
static inline PyManagedDictPointer *
936936
_PyObject_ManagedDictPointer(PyObject *obj)
937937
{
938-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
938+
assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT));
939939
return (PyManagedDictPointer *)((char *)obj + MANAGED_DICT_OFFSET);
940940
}
941941

@@ -951,8 +951,8 @@ _PyObject_InlineValues(PyObject *obj)
951951
{
952952
PyTypeObject *tp = Py_TYPE(obj);
953953
assert(tp->tp_basicsize > 0 && (size_t)tp->tp_basicsize % sizeof(PyObject *) == 0);
954-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
955-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
954+
assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES));
955+
assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT));
956956
return (PyDictValues *)((char *)obj + tp->tp_basicsize);
957957
}
958958

Include/internal/pycore_typeobject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
#include "pycore_moduleobject.h" // PyModuleObject
1212
#include "pycore_lock.h" // PyMutex
13+
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_ULONG_RELAXED
1314

1415

1516
/* state */
@@ -207,7 +208,7 @@ static inline void *
207208
_PyType_GetModuleState(PyTypeObject *type)
208209
{
209210
assert(PyType_Check(type));
210-
assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
211+
assert((FT_ATOMIC_LOAD_ULONG_RELAXED(type->tp_flags) & Py_TPFLAGS_HEAPTYPE));
211212
PyHeapTypeObject *et = (PyHeapTypeObject *)type;
212213
assert(et->ht_module);
213214
PyModuleObject *mod = (PyModuleObject *)(et->ht_module);

Modules/_testinternalcapi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1911,7 +1911,7 @@ get_tlbc_id(PyObject *Py_UNUSED(module), PyObject *obj)
19111911
static PyObject *
19121912
has_inline_values(PyObject *self, PyObject *obj)
19131913
{
1914-
if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
1914+
if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) &&
19151915
_PyObject_InlineValues(obj)->valid) {
19161916
Py_RETURN_TRUE;
19171917
}

Objects/dictobject.c

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6718,9 +6718,9 @@ _PyDict_NewKeysForClass(PyHeapTypeObject *cls)
67186718
void
67196719
_PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp)
67206720
{
6721-
assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE);
6722-
assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES);
6723-
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
6721+
assert(_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE));
6722+
assert(_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES));
6723+
assert(_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT));
67246724
PyDictKeysObject *keys = CACHED_KEYS(tp);
67256725
assert(keys != NULL);
67266726
OBJECT_STAT_INC(inline_values);
@@ -6840,7 +6840,7 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
68406840
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
68416841
assert(keys != NULL);
68426842
assert(values != NULL);
6843-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
6843+
assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES));
68446844
Py_ssize_t ix = DKIX_EMPTY;
68456845
PyDictObject *dict = _PyObject_GetManagedDict(obj);
68466846
assert(dict == NULL || ((PyDictObject *)dict)->ma_values == values);
@@ -7005,7 +7005,7 @@ int
70057005
_PyObject_ManagedDictValidityCheck(PyObject *obj)
70067006
{
70077007
PyTypeObject *tp = Py_TYPE(obj);
7008-
CHECK(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
7008+
CHECK(_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT));
70097009
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
70107010
if (_PyManagedDictPointer_IsValues(*managed_dict)) {
70117011
PyDictValues *values = _PyManagedDictPointer_GetValues(*managed_dict);
@@ -7117,7 +7117,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
71177117
return 1;
71187118
}
71197119
PyDictObject *dict;
7120-
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
7120+
if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) {
71217121
PyDictValues *values = _PyObject_InlineValues(obj);
71227122
if (FT_ATOMIC_LOAD_UINT8(values->valid)) {
71237123
PyDictKeysObject *keys = CACHED_KEYS(tp);
@@ -7130,7 +7130,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
71307130
}
71317131
dict = _PyObject_GetManagedDict(obj);
71327132
}
7133-
else if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
7133+
else if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
71347134
dict = _PyObject_GetManagedDict(obj);
71357135
}
71367136
else {
@@ -7147,10 +7147,10 @@ int
71477147
PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
71487148
{
71497149
PyTypeObject *tp = Py_TYPE(obj);
7150-
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
7150+
if(!_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
71517151
return 0;
71527152
}
7153-
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
7153+
if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) {
71547154
PyDictValues *values = _PyObject_InlineValues(obj);
71557155
if (values->valid) {
71567156
for (Py_ssize_t i = 0; i < values->capacity; i++) {
@@ -7265,15 +7265,15 @@ decref_maybe_delay(PyObject *obj, bool delay)
72657265
int
72667266
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
72677267
{
7268-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
7268+
assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT));
72697269
#ifndef NDEBUG
72707270
Py_BEGIN_CRITICAL_SECTION(obj);
72717271
assert(_PyObject_InlineValuesConsistencyCheck(obj));
72727272
Py_END_CRITICAL_SECTION();
72737273
#endif
72747274
int err = 0;
72757275
PyTypeObject *tp = Py_TYPE(obj);
7276-
if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
7276+
if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) {
72777277
#ifdef Py_GIL_DISABLED
72787278
PyDictObject *prev_dict;
72797279
if (!try_set_dict_inline_only_or_other_dict(obj, new_dict, &prev_dict)) {
@@ -7359,7 +7359,7 @@ detach_dict_from_object(PyDictObject *mp, PyObject *obj)
73597359
ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(mp);
73607360
assert(mp->ma_values->embedded == 1);
73617361
assert(mp->ma_values->valid == 1);
7362-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
7362+
assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES));
73637363

73647364
PyDictValues *values = copy_values(mp->ma_values);
73657365

@@ -7382,7 +7382,7 @@ PyObject_ClearManagedDict(PyObject *obj)
73827382
{
73837383
// This is called when the object is being freed or cleared
73847384
// by the GC and therefore known to have no references.
7385-
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
7385+
if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES)) {
73867386
PyDictObject *dict = _PyObject_GetManagedDict(obj);
73877387
if (dict == NULL) {
73887388
// We have no materialized dictionary and inline values
@@ -7436,7 +7436,7 @@ ensure_managed_dict(PyObject *obj)
74367436
PyDictObject *dict = _PyObject_GetManagedDict(obj);
74377437
if (dict == NULL) {
74387438
PyTypeObject *tp = Py_TYPE(obj);
7439-
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
7439+
if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES) &&
74407440
FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(obj)->valid)) {
74417441
dict = _PyObject_MaterializeManagedDict(obj);
74427442
}
@@ -7702,10 +7702,10 @@ _PyDict_SendEvent(int watcher_bits,
77027702
static int
77037703
_PyObject_InlineValuesConsistencyCheck(PyObject *obj)
77047704
{
7705-
if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) == 0) {
7705+
if (!_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES)) {
77067706
return 1;
77077707
}
7708-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
7708+
assert(_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT));
77097709
PyDictObject *dict = _PyObject_GetManagedDict(obj);
77107710
if (dict == NULL) {
77117711
return 1;

Objects/object.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,7 +1484,7 @@ PyObject **
14841484
_PyObject_ComputedDictPointer(PyObject *obj)
14851485
{
14861486
PyTypeObject *tp = Py_TYPE(obj);
1487-
assert((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0);
1487+
assert(!_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT));
14881488

14891489
Py_ssize_t dictoffset = tp->tp_dictoffset;
14901490
if (dictoffset == 0) {
@@ -1518,11 +1518,11 @@ _PyObject_ComputedDictPointer(PyObject *obj)
15181518
PyObject **
15191519
_PyObject_GetDictPtr(PyObject *obj)
15201520
{
1521-
if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
1521+
if (!_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT)) {
15221522
return _PyObject_ComputedDictPointer(obj);
15231523
}
15241524
PyDictObject *dict = _PyObject_GetManagedDict(obj);
1525-
if (dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
1525+
if (dict == NULL && _PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES)) {
15261526
dict = _PyObject_MaterializeManagedDict(obj);
15271527
if (dict == NULL) {
15281528
PyErr_Clear();
@@ -1598,7 +1598,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
15981598
}
15991599
}
16001600
PyObject *dict, *attr;
1601-
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
1601+
if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES) &&
16021602
_PyObject_TryGetInstanceAttribute(obj, name, &attr)) {
16031603
if (attr != NULL) {
16041604
*method = attr;
@@ -1607,7 +1607,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
16071607
}
16081608
dict = NULL;
16091609
}
1610-
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
1610+
else if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
16111611
dict = (PyObject *)_PyObject_GetManagedDict(obj);
16121612
}
16131613
else {
@@ -1700,7 +1700,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
17001700
}
17011701
}
17021702
if (dict == NULL) {
1703-
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) {
1703+
if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) {
17041704
if (PyUnicode_CheckExact(name) &&
17051705
_PyObject_TryGetInstanceAttribute(obj, name, &res)) {
17061706
if (res != NULL) {
@@ -1715,7 +1715,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
17151715
}
17161716
}
17171717
}
1718-
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
1718+
else if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
17191719
dict = (PyObject *)_PyObject_GetManagedDict(obj);
17201720
}
17211721
else {
@@ -1816,12 +1816,12 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
18161816
if (dict == NULL) {
18171817
PyObject **dictptr;
18181818

1819-
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) {
1819+
if (_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES)) {
18201820
res = _PyObject_StoreInstanceAttribute(obj, name, value);
18211821
goto error_check;
18221822
}
18231823

1824-
if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
1824+
if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
18251825
PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
18261826
dictptr = (PyObject **)&managed_dict->dict;
18271827
}

Objects/structseq.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ static int
111111
structseq_traverse(PyObject *op, visitproc visit, void *arg)
112112
{
113113
PyStructSequence *obj = (PyStructSequence *)op;
114-
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_HEAPTYPE) {
114+
if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_HEAPTYPE)) {
115115
Py_VISIT(Py_TYPE(obj));
116116
}
117117
Py_ssize_t i, size;
@@ -577,6 +577,8 @@ initialize_static_fields(PyTypeObject *type, PyStructSequence_Desc *desc,
577577
type->tp_base = &PyTuple_Type;
578578
type->tp_methods = structseq_methods;
579579
type->tp_new = structseq_new;
580+
// Note that it is thread-safe to not use an atomic operation to set flags
581+
// here since PyType_Ready() will be called later.
580582
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | tp_flags;
581583
type->tp_traverse = structseq_traverse;
582584
type->tp_members = tp_members;
@@ -613,7 +615,7 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp,
613615
Py_ssize_t n_members = count_members(desc, &n_unnamed_members);
614616
PyMemberDef *members = NULL;
615617

616-
if ((type->tp_flags & Py_TPFLAGS_READY) == 0) {
618+
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) {
617619
assert(type->tp_name == NULL);
618620
assert(type->tp_members == NULL);
619621
assert(type->tp_base == NULL);
@@ -632,7 +634,7 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp,
632634
assert(type->tp_name != NULL);
633635
assert(type->tp_members != NULL);
634636
assert(type->tp_base == &PyTuple_Type);
635-
assert((type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
637+
assert(_PyType_HasFeature(type, _Py_TPFLAGS_STATIC_BUILTIN));
636638
assert(_Py_IsImmortal(type));
637639
}
638640
#endif
@@ -710,7 +712,7 @@ _PyStructSequence_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type)
710712
// Ensure that the type is initialized
711713
assert(type->tp_name != NULL);
712714
assert(type->tp_base == &PyTuple_Type);
713-
assert((type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
715+
assert(_PyType_HasFeature(type, _Py_TPFLAGS_STATIC_BUILTIN));
714716
assert(_Py_IsImmortal(type));
715717

716718
// Cannot delete a type if it still has subclasses

0 commit comments

Comments
 (0)