Skip to content

Commit 2e5667e

Browse files
Stop the world when setting __class__
1 parent ac37a80 commit 2e5667e

File tree

3 files changed

+29
-22
lines changed

3 files changed

+29
-22
lines changed

Include/object.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ _Py_IsOwnedByCurrentThread(PyObject *ob)
247247
// bpo-39573: The Py_SET_TYPE() function must be used to set an object type.
248248
static inline PyTypeObject* Py_TYPE(PyObject *ob) {
249249
#ifdef Py_GIL_DISABLED
250-
return (PyTypeObject *)_Py_atomic_load_ptr_relaxed(&ob->ob_type);
250+
return (PyTypeObject *)&ob->ob_type;
251251
#else
252252
return ob->ob_type;
253253
#endif
@@ -278,11 +278,7 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
278278

279279

280280
static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
281-
#ifdef Py_GIL_DISABLED
282-
_Py_atomic_store_ptr(&ob->ob_type, type);
283-
#else
284281
ob->ob_type = type;
285-
#endif
286282
}
287283
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
288284
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)

Objects/dictobject.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ ASSERT_DICT_LOCKED(PyObject *op)
158158
if (!_PyInterpreterState_GET()->stoptheworld.world_stopped) { \
159159
ASSERT_DICT_LOCKED(op); \
160160
}
161+
#define ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(op) \
162+
if (!_PyInterpreterState_GET()->stoptheworld.world_stopped) { \
163+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op); \
164+
}
161165

162166
#define IS_DICT_SHARED(mp) _PyObject_GC_IS_SHARED(mp)
163167
#define SET_DICT_SHARED(mp) _PyObject_GC_SET_SHARED(mp)
@@ -7170,7 +7174,7 @@ PyObject_ClearManagedDict(PyObject *obj)
71707174
int
71717175
_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
71727176
{
7173-
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
7177+
ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(obj);
71747178
assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
71757179
assert(_PyObject_InlineValuesConsistencyCheck(obj));
71767180

Objects/typeobject.c

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6599,6 +6599,12 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
65996599
}
66006600

66016601
PyTypeObject *oldto = Py_TYPE(self);
6602+
#ifdef Py_GIL_DISABLED
6603+
PyInterpreterState *interp = PyInterpreterState_Get();
6604+
// The real Py_TYPE(self) (`oldto`) may have changed from
6605+
// underneath us in another thread, so we re-fetch it here.
6606+
_PyEval_StopTheWorld(interp);
6607+
#endif
66026608

66036609
/* In versions of CPython prior to 3.5, the code in
66046610
compatible_for_assignment was not set up to correctly check for memory
@@ -6656,7 +6662,7 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
66566662
PyErr_Format(PyExc_TypeError,
66576663
"__class__ assignment only supported for mutable types "
66586664
"or ModuleType subclasses");
6659-
return -1;
6665+
goto err;
66606666
}
66616667

66626668
if (compatible_for_assignment(oldto, newto, "__class__")) {
@@ -6665,47 +6671,48 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
66656671
if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
66666672
PyDictObject *dict = _PyObject_MaterializeManagedDict(self);
66676673
if (dict == NULL) {
6668-
return -1;
6674+
goto err;
66696675
}
66706676

6671-
bool error = false;
6672-
6673-
Py_BEGIN_CRITICAL_SECTION2(self, dict);
6674-
66756677
// If we raced after materialization and replaced the dict
66766678
// then the materialized dict should no longer have the
66776679
// inline values in which case detach is a nop.
6680+
// Note: we don't need to lock here because the world should be stopped.
6681+
66786682
assert(_PyObject_GetManagedDict(self) == dict ||
66796683
dict->ma_values != _PyObject_InlineValues(self));
66806684

66816685
if (_PyDict_DetachFromObject(dict, self) < 0) {
6682-
error = true;
6686+
goto err;
66836687
}
66846688

6685-
Py_END_CRITICAL_SECTION2();
6686-
if (error) {
6687-
return -1;
6688-
}
66896689
}
66906690
if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE) {
66916691
Py_INCREF(newto);
66926692
}
6693-
Py_BEGIN_CRITICAL_SECTION(self);
6694-
// The real Py_TYPE(self) (`oldto`) may have changed from
6695-
// underneath us in another thread, so we re-fetch it here.
6693+
66966694
oldto = Py_TYPE(self);
66976695
Py_SET_TYPE(self, newto);
6698-
Py_END_CRITICAL_SECTION();
6696+
66996697
if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE) {
67006698
Py_DECREF(oldto);
67016699
}
67026700

67036701
RARE_EVENT_INC(set_class);
6702+
6703+
#ifdef Py_GIL_DISABLED
6704+
_PyEval_StartTheWorld(interp);
6705+
#endif
67046706
return 0;
67056707
}
67066708
else {
6707-
return -1;
6709+
goto err;
67086710
}
6711+
err:
6712+
#ifdef Py_GIL_DISABLED
6713+
_PyEval_StartTheWorld(interp);
6714+
#endif
6715+
return -1;
67096716
}
67106717

67116718
static PyGetSetDef object_getsets[] = {

0 commit comments

Comments
 (0)