Skip to content

Commit 395a6d3

Browse files
committed
Re-add the ASSERT_NEW_OR_STOPPED() asserts.
These seem like helpful asserts that could catch future bugs, e.g. if the typeobject.c code is refactored or significantly changed. So, I think it makes sense to allocate a ob_flags bit for this purpose. We can remove it later if desired since it's non-public. Move the ob_flags bit values into the object.h header file.
1 parent f65e87c commit 395a6d3

File tree

3 files changed

+42
-3
lines changed

3 files changed

+42
-3
lines changed

Include/object.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,12 @@ given type object has a specified feature.
620620
#define Py_TPFLAGS_HAVE_FINALIZE (1UL << 0)
621621
#define Py_TPFLAGS_HAVE_VERSION_TAG (1UL << 18)
622622

623+
// Flag values for ob_flags (16 bits available, if SIZEOF_VOID_P > 4).
624+
#define _Py_IMMORTAL_FLAGS (1 << 0)
625+
#define _Py_STATICALLY_ALLOCATED_FLAG (1 << 2)
626+
#if defined(Py_GIL_DISABLED) && defined(Py_DEBUG)
627+
#define _Py_TYPE_REVEALED_FLAG (1 << 3)
628+
#endif
623629

624630
#define Py_CONSTANT_NONE 0
625631
#define Py_CONSTANT_FALSE 1

Include/refcount.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ immortal. The latter should be the only instances that require
1919
cleanup during runtime finalization.
2020
*/
2121

22-
#define _Py_STATICALLY_ALLOCATED_FLAG 4
23-
#define _Py_IMMORTAL_FLAGS 1
24-
2522
#if SIZEOF_VOID_P > 4
2623
/*
2724
In 64+ bit systems, any object whose 32 bit reference count is >= 2**31

Objects/typeobject.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ types_world_is_stopped(void)
9292
return interp->stoptheworld.world_stopped;
9393
}
9494

95+
// Checks that either the type has not yet been revealed (exposed) to
96+
// other threads or the world is stopped and we can safely update it
97+
// without races. The _Py_TYPE_REVEALED_FLAG flag is set by type_new() and
98+
// PyType_FromMetaclass() to indicate that a newly initialized type might
99+
// be revealed.
100+
#if SIZEOF_VOID_P > 4
101+
#define ASSERT_NEW_OR_STOPPED(tp) \
102+
assert((((PyObject *)(tp))->ob_flags & _Py_TYPE_REVEALED_FLAG) == 0 || \
103+
types_world_is_stopped())
104+
#else
105+
#define ASSERT_NEW_OR_STOPPED(tp)
106+
#endif
107+
95108
static void
96109
types_stop_world(void)
97110
{
@@ -159,6 +172,7 @@ types_mutex_unlock(void)
159172
#define types_stop_world()
160173
#define types_start_world()
161174
#define ASSERT_TYPE_LOCK_HELD()
175+
#define ASSERT_NEW_OR_STOPPED(tp)
162176

163177
#endif
164178

@@ -428,13 +442,15 @@ type_set_flags(PyTypeObject *tp, unsigned long flags)
428442
// be active).
429443
ASSERT_TYPE_LOCK_HELD();
430444
}
445+
ASSERT_NEW_OR_STOPPED(tp);
431446
tp->tp_flags = flags;
432447
}
433448

434449
static void
435450
type_set_flags_with_mask(PyTypeObject *tp, unsigned long mask, unsigned long flags)
436451
{
437452
ASSERT_TYPE_LOCK_HELD();
453+
ASSERT_NEW_OR_STOPPED(tp);
438454
unsigned long new_flags = (tp->tp_flags & ~mask) | flags;
439455
type_set_flags(tp, new_flags);
440456
}
@@ -570,6 +586,7 @@ static inline void
570586
set_tp_bases(PyTypeObject *self, PyObject *bases, int initial)
571587
{
572588
assert(PyTuple_CheckExact(bases));
589+
ASSERT_NEW_OR_STOPPED(self);
573590
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
574591
// XXX tp_bases can probably be statically allocated for each
575592
// static builtin type.
@@ -627,6 +644,7 @@ _PyType_GetMRO(PyTypeObject *self)
627644
static inline void
628645
set_tp_mro(PyTypeObject *self, PyObject *mro, int initial)
629646
{
647+
ASSERT_NEW_OR_STOPPED(self);
630648
if (mro != NULL) {
631649
assert(PyTuple_CheckExact(mro));
632650
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
@@ -1705,6 +1723,7 @@ static int
17051723
mro_hierarchy(PyTypeObject *type, PyObject *temp)
17061724
{
17071725
ASSERT_TYPE_LOCK_HELD();
1726+
ASSERT_NEW_OR_STOPPED(type);
17081727

17091728
PyObject *old_mro;
17101729
int res = mro_internal(type, &old_mro);
@@ -3551,6 +3570,7 @@ static int
35513570
mro_internal_unlocked(PyTypeObject *type, int initial, PyObject **p_old_mro)
35523571
{
35533572
ASSERT_TYPE_LOCK_HELD();
3573+
ASSERT_NEW_OR_STOPPED(type);
35543574

35553575
PyObject *new_mro, *old_mro;
35563576
int reent;
@@ -3600,6 +3620,7 @@ static int
36003620
mro_internal(PyTypeObject *type, PyObject **p_old_mro)
36013621
{
36023622
int res;
3623+
ASSERT_NEW_OR_STOPPED(type);
36033624
res = mro_internal_unlocked(type, 0, p_old_mro);
36043625
return res;
36053626
}
@@ -4679,6 +4700,10 @@ type_new_impl(type_new_ctx *ctx)
46794700
}
46804701

46814702
assert(_PyType_CheckConsistency(type));
4703+
#if defined(Py_GIL_DISABLED) && defined(Py_DEBUG) && SIZEOF_VOID_P > 4
4704+
// After this point, other threads can potentally use this type.
4705+
((PyObject*)type)->ob_flags |= _Py_TYPE_REVEALED_FLAG;
4706+
#endif
46824707

46834708
return (PyObject *)type;
46844709

@@ -5392,6 +5417,10 @@ PyType_FromMetaclass(
53925417
}
53935418

53945419
assert(_PyType_CheckConsistency(type));
5420+
#if defined(Py_GIL_DISABLED) && defined(Py_DEBUG) && SIZEOF_VOID_P > 4
5421+
// After this point, other threads can potentally use this type.
5422+
((PyObject*)type)->ob_flags |= _Py_TYPE_REVEALED_FLAG;
5423+
#endif
53955424

53965425
finally:
53975426
if (PyErr_Occurred()) {
@@ -8649,6 +8678,7 @@ static int
86498678
type_ready_mro(PyTypeObject *type, int initial)
86508679
{
86518680
ASSERT_TYPE_LOCK_HELD();
8681+
ASSERT_NEW_OR_STOPPED(type);
86528682

86538683
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
86548684
if (!initial) {
@@ -8724,6 +8754,7 @@ static int
87248754
type_ready_inherit(PyTypeObject *type)
87258755
{
87268756
ASSERT_TYPE_LOCK_HELD();
8757+
ASSERT_NEW_OR_STOPPED(type);
87278758

87288759
/* Inherit special flags from dominant base */
87298760
PyTypeObject *base = type->tp_base;
@@ -8921,6 +8952,7 @@ static int
89218952
type_ready(PyTypeObject *type, int initial)
89228953
{
89238954
ASSERT_TYPE_LOCK_HELD();
8955+
ASSERT_NEW_OR_STOPPED(type);
89248956

89258957
_PyObject_ASSERT((PyObject *)type, !is_readying(type));
89268958
start_readying(type);
@@ -11253,6 +11285,7 @@ static pytype_slotdef *
1125311285
update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1125411286
{
1125511287
ASSERT_TYPE_LOCK_HELD();
11288+
ASSERT_NEW_OR_STOPPED(type);
1125611289

1125711290
PyObject *descr;
1125811291
PyWrapperDescrObject *d;
@@ -11369,6 +11402,7 @@ static int
1136911402
update_slots_callback(PyTypeObject *type, void *data)
1137011403
{
1137111404
ASSERT_TYPE_LOCK_HELD();
11405+
ASSERT_NEW_OR_STOPPED(type);
1137211406

1137311407
pytype_slotdef **pp = (pytype_slotdef **)data;
1137411408
for (; *pp; pp++) {
@@ -11387,6 +11421,7 @@ update_slot(PyTypeObject *type, PyObject *name)
1138711421
int offset;
1138811422

1138911423
ASSERT_TYPE_LOCK_HELD();
11424+
ASSERT_NEW_OR_STOPPED(type);
1139011425
assert(types_world_is_stopped());
1139111426
assert(PyUnicode_CheckExact(name));
1139211427
assert(PyUnicode_CHECK_INTERNED(name));
@@ -11441,6 +11476,7 @@ update_all_slots(PyTypeObject* type)
1144111476
pytype_slotdef *p;
1144211477

1144311478
ASSERT_TYPE_LOCK_HELD();
11479+
ASSERT_NEW_OR_STOPPED(type);
1144411480

1144511481
/* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
1144611482
type_modified_unlocked(type);

0 commit comments

Comments
 (0)