Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3562ae2

Browse files
[3.8]bpo-37879: Suppress subtype_dealloc decref when base type is a C heap type (GH-15323,GH-16004) (GH-15966)
The instance destructor for a type is responsible for preparingan instance for deallocation by decrementing the reference countsof its referents.If an instance belongs to a heap type, the type object of an instancehas its reference count decremented while for static types, whichare permanently allocated, the type object is unaffected by theinstance destructor.Previously, the default instance destructor searched the classhierarchy for an inherited instance destructor and, if present,would invoke it.Then, if the instance type is a heap type, it would decrement thereference count of that heap type. However, this could result in thepremature destruction of a type because the inherited instancedestructor should have already decremented the reference countof the type object.This change avoids the premature destruction of the type objectby suppressing the decrement of its reference count when aninherited, non-default instance destructor has been invoked.Finally, an assertion on the Py_SIZE of a type was deleted. Heaptypes have a non zero size, making this into an incorrect assertion.#15323.(cherry picked from commitff023ed)Fixup:#16004.(cherry picked from commit5e9caee)Co-authored-by: Eddie Elizondo <eduardo.elizondorueda@gmail.com>
1 parenteb19c45 commit3562ae2

File tree

4 files changed

+319
-10
lines changed

4 files changed

+319
-10
lines changed

‎Lib/test/test_capi.py‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,92 @@ def __del__(self):
383383
delL
384384
self.assertEqual(PyList.num,0)
385385

386+
deftest_subclass_of_heap_gc_ctype_with_tpdealloc_decrefs_once(self):
387+
classHeapGcCTypeSubclass(_testcapi.HeapGcCType):
388+
def__init__(self):
389+
self.value2=20
390+
super().__init__()
391+
392+
subclass_instance=HeapGcCTypeSubclass()
393+
type_refcnt=sys.getrefcount(HeapGcCTypeSubclass)
394+
395+
# Test that subclass instance was fully created
396+
self.assertEqual(subclass_instance.value,10)
397+
self.assertEqual(subclass_instance.value2,20)
398+
399+
# Test that the type reference count is only decremented once
400+
delsubclass_instance
401+
self.assertEqual(type_refcnt-1,sys.getrefcount(HeapGcCTypeSubclass))
402+
403+
deftest_subclass_of_heap_gc_ctype_with_del_modifying_dunder_class_only_decrefs_once(self):
404+
classA(_testcapi.HeapGcCType):
405+
def__init__(self):
406+
self.value2=20
407+
super().__init__()
408+
409+
classB(A):
410+
def__init__(self):
411+
super().__init__()
412+
413+
def__del__(self):
414+
self.__class__=A
415+
A.refcnt_in_del=sys.getrefcount(A)
416+
B.refcnt_in_del=sys.getrefcount(B)
417+
418+
subclass_instance=B()
419+
type_refcnt=sys.getrefcount(B)
420+
new_type_refcnt=sys.getrefcount(A)
421+
422+
# Test that subclass instance was fully created
423+
self.assertEqual(subclass_instance.value,10)
424+
self.assertEqual(subclass_instance.value2,20)
425+
426+
delsubclass_instance
427+
428+
# Test that setting __class__ modified the reference counts of the types
429+
self.assertEqual(type_refcnt-1,B.refcnt_in_del)
430+
self.assertEqual(new_type_refcnt+1,A.refcnt_in_del)
431+
432+
# Test that the original type already has decreased its refcnt
433+
self.assertEqual(type_refcnt-1,sys.getrefcount(B))
434+
435+
# Test that subtype_dealloc decref the newly assigned __class__ only once
436+
self.assertEqual(new_type_refcnt,sys.getrefcount(A))
437+
438+
deftest_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self):
439+
subclass_instance=_testcapi.HeapCTypeSubclass()
440+
type_refcnt=sys.getrefcount(_testcapi.HeapCTypeSubclass)
441+
442+
# Test that subclass instance was fully created
443+
self.assertEqual(subclass_instance.value,10)
444+
self.assertEqual(subclass_instance.value2,20)
445+
446+
# Test that the type reference count is only decremented once
447+
delsubclass_instance
448+
self.assertEqual(type_refcnt-1,sys.getrefcount(_testcapi.HeapCTypeSubclass))
449+
450+
deftest_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_once(self):
451+
subclass_instance=_testcapi.HeapCTypeSubclassWithFinalizer()
452+
type_refcnt=sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer)
453+
new_type_refcnt=sys.getrefcount(_testcapi.HeapCTypeSubclass)
454+
455+
# Test that subclass instance was fully created
456+
self.assertEqual(subclass_instance.value,10)
457+
self.assertEqual(subclass_instance.value2,20)
458+
459+
# The tp_finalize slot will set __class__ to HeapCTypeSubclass
460+
delsubclass_instance
461+
462+
# Test that setting __class__ modified the reference counts of the types
463+
self.assertEqual(type_refcnt-1,_testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del)
464+
self.assertEqual(new_type_refcnt+1,_testcapi.HeapCTypeSubclass.refcnt_in_del)
465+
466+
# Test that the original type already has decreased its refcnt
467+
self.assertEqual(type_refcnt-1,sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer))
468+
469+
# Test that subtype_dealloc decref the newly assigned __class__ only once
470+
self.assertEqual(new_type_refcnt,sys.getrefcount(_testcapi.HeapCTypeSubclass))
471+
386472

387473
classTestPendingCalls(unittest.TestCase):
388474

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix subtype_dealloc to suppress the type decref when the base type is a C
2+
heap type

‎Modules/_testcapimodule.c‎

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
# error "_testcapi must test the public Python C API, not CPython internal C API"
3333
#endif
3434

35+
staticstructPyModuleDef_testcapimodule;
36+
3537
staticPyObject*TestError;/* set to exception object in init */
3638

3739
/* Raise TestError with test_name + ": " + msg, and return NULL. */
@@ -5955,6 +5957,190 @@ static PyTypeObject MethodDescriptor2_Type = {
59555957
.tp_flags=Py_TPFLAGS_DEFAULT |Py_TPFLAGS_BASETYPE |_Py_TPFLAGS_HAVE_VECTORCALL,
59565958
};
59575959

5960+
PyDoc_STRVAR(heapgctype__doc__,
5961+
"A heap type with GC, and with overridden dealloc.\n\n"
5962+
"The 'value' attribute is set to 10 in __init__.");
5963+
5964+
typedefstruct {
5965+
PyObject_HEAD
5966+
intvalue;
5967+
}HeapCTypeObject;
5968+
5969+
staticstructPyMemberDefheapctype_members[]= {
5970+
{"value",T_INT, offsetof(HeapCTypeObject,value)},
5971+
{NULL}/* Sentinel */
5972+
};
5973+
5974+
staticint
5975+
heapctype_init(PyObject*self,PyObject*args,PyObject*kwargs)
5976+
{
5977+
((HeapCTypeObject*)self)->value=10;
5978+
return0;
5979+
}
5980+
5981+
staticvoid
5982+
heapgcctype_dealloc(HeapCTypeObject*self)
5983+
{
5984+
PyTypeObject*tp=Py_TYPE(self);
5985+
PyObject_GC_UnTrack(self);
5986+
PyObject_GC_Del(self);
5987+
Py_DECREF(tp);
5988+
}
5989+
5990+
staticPyType_SlotHeapGcCType_slots[]= {
5991+
{Py_tp_init,heapctype_init},
5992+
{Py_tp_members,heapctype_members},
5993+
{Py_tp_dealloc,heapgcctype_dealloc},
5994+
{Py_tp_doc, (char*)heapgctype__doc__},
5995+
{0,0},
5996+
};
5997+
5998+
staticPyType_SpecHeapGcCType_spec= {
5999+
"_testcapi.HeapGcCType",
6000+
sizeof(HeapCTypeObject),
6001+
0,
6002+
Py_TPFLAGS_DEFAULT |Py_TPFLAGS_BASETYPE |Py_TPFLAGS_HAVE_GC,
6003+
HeapGcCType_slots
6004+
};
6005+
6006+
PyDoc_STRVAR(heapctype__doc__,
6007+
"A heap type without GC, but with overridden dealloc.\n\n"
6008+
"The 'value' attribute is set to 10 in __init__.");
6009+
6010+
staticvoid
6011+
heapctype_dealloc(HeapCTypeObject*self)
6012+
{
6013+
PyTypeObject*tp=Py_TYPE(self);
6014+
PyObject_Del(self);
6015+
Py_DECREF(tp);
6016+
}
6017+
6018+
staticPyType_SlotHeapCType_slots[]= {
6019+
{Py_tp_init,heapctype_init},
6020+
{Py_tp_members,heapctype_members},
6021+
{Py_tp_dealloc,heapctype_dealloc},
6022+
{Py_tp_doc, (char*)heapctype__doc__},
6023+
{0,0},
6024+
};
6025+
6026+
staticPyType_SpecHeapCType_spec= {
6027+
"_testcapi.HeapCType",
6028+
sizeof(HeapCTypeObject),
6029+
0,
6030+
Py_TPFLAGS_DEFAULT |Py_TPFLAGS_BASETYPE,
6031+
HeapCType_slots
6032+
};
6033+
6034+
PyDoc_STRVAR(heapctypesubclass__doc__,
6035+
"Subclass of HeapCType, without GC.\n\n"
6036+
"__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
6037+
6038+
typedefstruct {
6039+
HeapCTypeObjectbase;
6040+
intvalue2;
6041+
}HeapCTypeSubclassObject;
6042+
6043+
staticint
6044+
heapctypesubclass_init(PyObject*self,PyObject*args,PyObject*kwargs)
6045+
{
6046+
/* Call __init__ of the superclass */
6047+
if (heapctype_init(self,args,kwargs)<0) {
6048+
return-1;
6049+
}
6050+
/* Initialize additional element */
6051+
((HeapCTypeSubclassObject*)self)->value2=20;
6052+
return0;
6053+
}
6054+
6055+
staticstructPyMemberDefheapctypesubclass_members[]= {
6056+
{"value2",T_INT, offsetof(HeapCTypeSubclassObject,value2)},
6057+
{NULL}/* Sentinel */
6058+
};
6059+
6060+
staticPyType_SlotHeapCTypeSubclass_slots[]= {
6061+
{Py_tp_init,heapctypesubclass_init},
6062+
{Py_tp_members,heapctypesubclass_members},
6063+
{Py_tp_doc, (char*)heapctypesubclass__doc__},
6064+
{0,0},
6065+
};
6066+
6067+
staticPyType_SpecHeapCTypeSubclass_spec= {
6068+
"_testcapi.HeapCTypeSubclass",
6069+
sizeof(HeapCTypeSubclassObject),
6070+
0,
6071+
Py_TPFLAGS_DEFAULT |Py_TPFLAGS_BASETYPE,
6072+
HeapCTypeSubclass_slots
6073+
};
6074+
6075+
PyDoc_STRVAR(heapctypesubclasswithfinalizer__doc__,
6076+
"Subclass of HeapCType with a finalizer that reassigns __class__.\n\n"
6077+
"__class__ is set to plain HeapCTypeSubclass during finalization.\n"
6078+
"__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
6079+
6080+
staticint
6081+
heapctypesubclasswithfinalizer_init(PyObject*self,PyObject*args,PyObject*kwargs)
6082+
{
6083+
PyTypeObject*base= (PyTypeObject*)PyType_GetSlot(Py_TYPE(self),Py_tp_base);
6084+
initprocbase_init=PyType_GetSlot(base,Py_tp_init);
6085+
base_init(self,args,kwargs);
6086+
return0;
6087+
}
6088+
6089+
staticvoid
6090+
heapctypesubclasswithfinalizer_finalize(PyObject*self)
6091+
{
6092+
PyObject*error_type,*error_value,*error_traceback,*m;
6093+
PyObject*oldtype=NULL,*newtype=NULL;
6094+
6095+
/* Save the current exception, if any. */
6096+
PyErr_Fetch(&error_type,&error_value,&error_traceback);
6097+
6098+
m=PyState_FindModule(&_testcapimodule);
6099+
if (m==NULL) {
6100+
gotocleanup_finalize;
6101+
}
6102+
oldtype=PyObject_GetAttrString(m,"HeapCTypeSubclassWithFinalizer");
6103+
newtype=PyObject_GetAttrString(m,"HeapCTypeSubclass");
6104+
if (oldtype==NULL||newtype==NULL) {
6105+
gotocleanup_finalize;
6106+
}
6107+
6108+
if (PyObject_SetAttrString(self,"__class__",newtype)<0) {
6109+
gotocleanup_finalize;
6110+
}
6111+
if (PyObject_SetAttrString(
6112+
oldtype,"refcnt_in_del",PyLong_FromSsize_t(Py_REFCNT(oldtype)))<0) {
6113+
gotocleanup_finalize;
6114+
}
6115+
if (PyObject_SetAttrString(
6116+
newtype,"refcnt_in_del",PyLong_FromSsize_t(Py_REFCNT(newtype)))<0) {
6117+
gotocleanup_finalize;
6118+
}
6119+
6120+
cleanup_finalize:
6121+
Py_XDECREF(oldtype);
6122+
Py_XDECREF(newtype);
6123+
6124+
/* Restore the saved exception. */
6125+
PyErr_Restore(error_type,error_value,error_traceback);
6126+
}
6127+
6128+
staticPyType_SlotHeapCTypeSubclassWithFinalizer_slots[]= {
6129+
{Py_tp_init,heapctypesubclasswithfinalizer_init},
6130+
{Py_tp_members,heapctypesubclass_members},
6131+
{Py_tp_finalize,heapctypesubclasswithfinalizer_finalize},
6132+
{Py_tp_doc, (char*)heapctypesubclasswithfinalizer__doc__},
6133+
{0,0},
6134+
};
6135+
6136+
staticPyType_SpecHeapCTypeSubclassWithFinalizer_spec= {
6137+
"_testcapi.HeapCTypeSubclassWithFinalizer",
6138+
sizeof(HeapCTypeSubclassObject),
6139+
0,
6140+
Py_TPFLAGS_DEFAULT |Py_TPFLAGS_HAVE_FINALIZE,
6141+
HeapCTypeSubclassWithFinalizer_slots
6142+
};
6143+
59586144

59596145
staticstructPyModuleDef_testcapimodule= {
59606146
PyModuleDef_HEAD_INIT,
@@ -6089,5 +6275,40 @@ PyInit__testcapi(void)
60896275
TestError=PyErr_NewException("_testcapi.error",NULL,NULL);
60906276
Py_INCREF(TestError);
60916277
PyModule_AddObject(m,"error",TestError);
6278+
6279+
PyObject*HeapGcCType=PyType_FromSpec(&HeapGcCType_spec);
6280+
if (HeapGcCType==NULL) {
6281+
returnNULL;
6282+
}
6283+
PyModule_AddObject(m,"HeapGcCType",HeapGcCType);
6284+
6285+
PyObject*HeapCType=PyType_FromSpec(&HeapCType_spec);
6286+
if (HeapCType==NULL) {
6287+
returnNULL;
6288+
}
6289+
PyObject*subclass_bases=PyTuple_Pack(1,HeapCType);
6290+
if (subclass_bases==NULL) {
6291+
returnNULL;
6292+
}
6293+
PyObject*HeapCTypeSubclass=PyType_FromSpecWithBases(&HeapCTypeSubclass_spec,subclass_bases);
6294+
if (HeapCTypeSubclass==NULL) {
6295+
returnNULL;
6296+
}
6297+
Py_DECREF(subclass_bases);
6298+
PyModule_AddObject(m,"HeapCTypeSubclass",HeapCTypeSubclass);
6299+
6300+
PyObject*subclass_with_finalizer_bases=PyTuple_Pack(1,HeapCTypeSubclass);
6301+
if (subclass_with_finalizer_bases==NULL) {
6302+
returnNULL;
6303+
}
6304+
PyObject*HeapCTypeSubclassWithFinalizer=PyType_FromSpecWithBases(
6305+
&HeapCTypeSubclassWithFinalizer_spec,subclass_with_finalizer_bases);
6306+
if (HeapCTypeSubclassWithFinalizer==NULL) {
6307+
returnNULL;
6308+
}
6309+
Py_DECREF(subclass_with_finalizer_bases);
6310+
PyModule_AddObject(m,"HeapCTypeSubclassWithFinalizer",HeapCTypeSubclassWithFinalizer);
6311+
6312+
PyState_AddModule(m,&_testcapimodule);
60926313
returnm;
60936314
}

‎Objects/typeobject.c‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,11 +1157,9 @@ subtype_dealloc(PyObject *self)
11571157
/* Test whether the type has GC exactly once */
11581158

11591159
if (!PyType_IS_GC(type)) {
1160-
/* It's really rare to find a dynamic type that doesn't have
1161-
GC; it can only happen when deriving from 'object' and not
1162-
adding any slots or instance variables. This allows
1163-
certain simplifications: there's no need to call
1164-
clear_slots(), or DECREF the dict, or clear weakrefs. */
1160+
/* A non GC dynamic type allows certain simplifications:
1161+
there's no need to call clear_slots(), or DECREF the dict,
1162+
or clear weakrefs. */
11651163

11661164
/* Maybe call finalizer; exit early if resurrected */
11671165
if (type->tp_finalize) {
@@ -1177,7 +1175,6 @@ subtype_dealloc(PyObject *self)
11771175
/* Find the nearest base with a different tp_dealloc */
11781176
base=type;
11791177
while ((basedealloc=base->tp_dealloc)==subtype_dealloc) {
1180-
assert(Py_SIZE(base)==0);
11811178
base=base->tp_base;
11821179
assert(base);
11831180
}
@@ -1189,8 +1186,10 @@ subtype_dealloc(PyObject *self)
11891186
assert(basedealloc);
11901187
basedealloc(self);
11911188

1192-
/* Can't reference self beyond this point */
1193-
Py_DECREF(type);
1189+
/* Only decref if the base type is not already a heap allocated type.
1190+
Otherwise, basedealloc should have decref'd it already */
1191+
if (type->tp_flags&Py_TPFLAGS_HEAPTYPE&& !(base->tp_flags&Py_TPFLAGS_HEAPTYPE))
1192+
Py_DECREF(type);
11941193

11951194
/* Done */
11961195
return;
@@ -1289,8 +1288,9 @@ subtype_dealloc(PyObject *self)
12891288

12901289
/* Can't reference self beyond this point. It's possible tp_del switched
12911290
our type from a HEAPTYPE to a non-HEAPTYPE, so be careful about
1292-
reference counting. */
1293-
if (type->tp_flags&Py_TPFLAGS_HEAPTYPE)
1291+
reference counting. Only decref if the base type is not already a heap
1292+
allocated type. Otherwise, basedealloc should have decref'd it already */
1293+
if (type->tp_flags&Py_TPFLAGS_HEAPTYPE&& !(base->tp_flags&Py_TPFLAGS_HEAPTYPE))
12941294
Py_DECREF(type);
12951295

12961296
endlabel:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2026 Movatter.jp