Skip to content

Commit edd5de7

Browse files
author
gvanrossum
committed
Add Garbage Collection support to new-style classes (not yet to their
instances). Also added GC support to various auxiliary types: super, property, descriptors, wrappers, dictproxy. (Only type objects have a tp_clear field; the other types are.) One change was necessary to the GC infrastructure. We have statically allocated type objects that don't have a GC header (and can't easily be given one) and heap-allocated type objects that do have a GC header. Giving these different metatypes would be really ugly: I tried, and I had to modify pickle.py, cPickle.c, copy.py, add a new invent a new name for the new metatype and make it a built-in, change affected tests... In short, a mess. So instead, we add a new type slot tp_is_gc, which is a simple Boolean function that determines whether a particular instance has GC headers or not. This slot is only relevant for types that have the (new) GC flag bit set. If the tp_is_gc slot is NULL (by far the most common case), all instances of the type are deemed to have GC headers. This slot is called by the PyObject_IS_GC() macro (which is only used twice, both times in gcmodule.c). I also changed the extern declarations for a bunch of GC-related functions (_PyObject_GC_Del etc.): these always exist but objimpl.h only declared them when WITH_CYCLE_GC was defined, but I needed to be able to reference them without #ifdefs. (When WITH_CYCLE_GC is not defined, they do the same as their non-GC counterparts anyway.) git-svn-id: http://svn.python.org/projects/python/trunk@23484 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 5bc8011 commit edd5de7

5 files changed

Lines changed: 237 additions & 39 deletions

File tree

Include/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ typedef struct _typeobject {
285285
allocfunc tp_alloc;
286286
newfunc tp_new;
287287
destructor tp_free; /* Low-level free-memory routine */
288+
inquiry tp_is_gc; /* For PyObject_IS_GC */
288289
PyObject *tp_bases;
289290
PyObject *tp_mro; /* method resolution order */
290291
PyObject *tp_defined;

Include/objimpl.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,28 +217,33 @@ extern DL_IMPORT(void) _PyObject_Del(PyObject *);
217217
/*
218218
* Garbage Collection Support
219219
* ==========================
220+
*
221+
* Some of the functions and macros below are always defined; when
222+
* WITH_CYCLE_GC is undefined, they simply don't do anything different
223+
* than their non-GC counterparts.
220224
*/
221225

222226
/* Test if a type has a GC head */
223227
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
224228

225229
/* Test if an object has a GC head */
226-
#define PyObject_IS_GC(o) PyType_IS_GC((o)->ob_type)
230+
#define PyObject_IS_GC(o) (PyType_IS_GC((o)->ob_type) && \
231+
((o)->ob_type->tp_is_gc == NULL || (o)->ob_type->tp_is_gc(o)))
227232

228233
extern DL_IMPORT(PyObject *) _PyObject_GC_Malloc(PyTypeObject *, int);
229234
extern DL_IMPORT(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, int);
230235

231236
#define PyObject_GC_Resize(type, op, n) \
232237
( (type *) _PyObject_GC_Resize((PyVarObject *)(op), (n)) )
233238

234-
#ifdef WITH_CYCLE_GC
235-
236239
extern DL_IMPORT(PyObject *) _PyObject_GC_New(PyTypeObject *);
237240
extern DL_IMPORT(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, int);
238241
extern DL_IMPORT(void) _PyObject_GC_Del(PyObject *);
239242
extern DL_IMPORT(void) _PyObject_GC_Track(PyObject *);
240243
extern DL_IMPORT(void) _PyObject_GC_UnTrack(PyObject *);
241244

245+
#ifdef WITH_CYCLE_GC
246+
242247
/* GC information is stored BEFORE the object structure */
243248
typedef struct _gc_head {
244249
struct _gc_head *gc_next; /* not NULL if object is tracked */

Lib/test/test_gc.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ def expect(actual, expected, name):
77
raise TestFailed, "test_%s: actual %d, expected %d" % (
88
name, actual, expected)
99

10-
def expect_not(actual, expected, name):
11-
if actual == expected:
12-
raise TestFailed, "test_%s: actual %d unexpected" % (name, actual)
10+
def expect_nonzero(actual, name):
11+
if actual == 0:
12+
raise TestFailed, "test_%s: unexpected zero" % name
1313

1414
def run_test(name, thunk):
1515
if verbose:
@@ -48,7 +48,21 @@ class A:
4848
A.a = A
4949
gc.collect()
5050
del A
51-
expect_not(gc.collect(), 0, "class")
51+
expect_nonzero(gc.collect(), "class")
52+
53+
def test_staticclass():
54+
class A(object):
55+
__dynamic__ = 0
56+
gc.collect()
57+
del A
58+
expect_nonzero(gc.collect(), "staticclass")
59+
60+
def test_dynamicclass():
61+
class A(object):
62+
__dynamic__ = 1
63+
gc.collect()
64+
del A
65+
expect_nonzero(gc.collect(), "dynamicclass")
5266

5367
def test_instance():
5468
class A:
@@ -57,7 +71,7 @@ class A:
5771
a.a = a
5872
gc.collect()
5973
del a
60-
expect_not(gc.collect(), 0, "instance")
74+
expect_nonzero(gc.collect(), "instance")
6175

6276
def test_method():
6377
# Tricky: self.__init__ is a bound method, it references the instance.
@@ -67,7 +81,7 @@ def __init__(self):
6781
a = A()
6882
gc.collect()
6983
del a
70-
expect_not(gc.collect(), 0, "method")
84+
expect_nonzero(gc.collect(), "method")
7185

7286
def test_finalizer():
7387
# A() is uncollectable if it is part of a cycle, make sure it shows up
@@ -84,7 +98,7 @@ class B:
8498
gc.collect()
8599
del a
86100
del b
87-
expect_not(gc.collect(), 0, "finalizer")
101+
expect_nonzero(gc.collect(), "finalizer")
88102
for obj in gc.garbage:
89103
if id(obj) == id_a:
90104
del obj.a
@@ -153,6 +167,8 @@ def test_all():
153167
run_test("dicts", test_dict)
154168
run_test("tuples", test_tuple)
155169
run_test("classes", test_class)
170+
run_test("static classes", test_staticclass)
171+
run_test("dynamic classes", test_dynamicclass)
156172
run_test("instances", test_instance)
157173
run_test("methods", test_method)
158174
run_test("functions", test_function)

Objects/descrobject.c

Lines changed: 94 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ typedef struct {
3838
static void
3939
descr_dealloc(PyDescrObject *descr)
4040
{
41+
_PyObject_GC_UNTRACK(descr);
4142
Py_XDECREF(descr->d_type);
4243
Py_XDECREF(descr->d_name);
43-
PyObject_DEL(descr);
44+
PyObject_GC_Del(descr);
4445
}
4546

4647
static char *
@@ -352,6 +353,20 @@ static PyGetSetDef wrapper_getset[] = {
352353
{0}
353354
};
354355

356+
static int
357+
descr_traverse(PyObject *self, visitproc visit, void *arg)
358+
{
359+
PyDescrObject *descr = (PyDescrObject *)self;
360+
int err;
361+
362+
if (descr->d_type) {
363+
err = visit((PyObject *)(descr->d_type), arg);
364+
if (err)
365+
return err;
366+
}
367+
return 0;
368+
}
369+
355370
static PyTypeObject PyMethodDescr_Type = {
356371
PyObject_HEAD_INIT(&PyType_Type)
357372
0,
@@ -373,9 +388,9 @@ static PyTypeObject PyMethodDescr_Type = {
373388
PyObject_GenericGetAttr, /* tp_getattro */
374389
0, /* tp_setattro */
375390
0, /* tp_as_buffer */
376-
Py_TPFLAGS_DEFAULT, /* tp_flags */
391+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
377392
0, /* tp_doc */
378-
0, /* tp_traverse */
393+
descr_traverse, /* tp_traverse */
379394
0, /* tp_clear */
380395
0, /* tp_richcompare */
381396
0, /* tp_weaklistoffset */
@@ -411,9 +426,9 @@ static PyTypeObject PyMemberDescr_Type = {
411426
PyObject_GenericGetAttr, /* tp_getattro */
412427
0, /* tp_setattro */
413428
0, /* tp_as_buffer */
414-
Py_TPFLAGS_DEFAULT, /* tp_flags */
429+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
415430
0, /* tp_doc */
416-
0, /* tp_traverse */
431+
descr_traverse, /* tp_traverse */
417432
0, /* tp_clear */
418433
0, /* tp_richcompare */
419434
0, /* tp_weaklistoffset */
@@ -449,9 +464,9 @@ static PyTypeObject PyGetSetDescr_Type = {
449464
PyObject_GenericGetAttr, /* tp_getattro */
450465
0, /* tp_setattro */
451466
0, /* tp_as_buffer */
452-
Py_TPFLAGS_DEFAULT, /* tp_flags */
467+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
453468
0, /* tp_doc */
454-
0, /* tp_traverse */
469+
descr_traverse, /* tp_traverse */
455470
0, /* tp_clear */
456471
0, /* tp_richcompare */
457472
0, /* tp_weaklistoffset */
@@ -487,9 +502,9 @@ static PyTypeObject PyWrapperDescr_Type = {
487502
PyObject_GenericGetAttr, /* tp_getattro */
488503
0, /* tp_setattro */
489504
0, /* tp_as_buffer */
490-
Py_TPFLAGS_DEFAULT, /* tp_flags */
505+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
491506
0, /* tp_doc */
492-
0, /* tp_traverse */
507+
descr_traverse, /* tp_traverse */
493508
0, /* tp_clear */
494509
0, /* tp_richcompare */
495510
0, /* tp_weaklistoffset */
@@ -679,8 +694,9 @@ static PyMethodDef proxy_methods[] = {
679694
static void
680695
proxy_dealloc(proxyobject *pp)
681696
{
697+
_PyObject_GC_UNTRACK(pp);
682698
Py_DECREF(pp->dict);
683-
PyObject_DEL(pp);
699+
PyObject_GC_Del(pp);
684700
}
685701

686702
static PyObject *
@@ -695,6 +711,20 @@ proxy_str(proxyobject *pp)
695711
return PyObject_Str(pp->dict);
696712
}
697713

714+
static int
715+
proxy_traverse(PyObject *self, visitproc visit, void *arg)
716+
{
717+
proxyobject *pp = (proxyobject *)self;
718+
int err;
719+
720+
if (pp->dict) {
721+
err = visit(pp->dict, arg);
722+
if (err)
723+
return err;
724+
}
725+
return 0;
726+
}
727+
698728
PyTypeObject proxytype = {
699729
PyObject_HEAD_INIT(&PyType_Type)
700730
0, /* ob_size */
@@ -717,9 +747,9 @@ PyTypeObject proxytype = {
717747
PyObject_GenericGetAttr, /* tp_getattro */
718748
0, /* tp_setattro */
719749
0, /* tp_as_buffer */
720-
Py_TPFLAGS_DEFAULT, /* tp_flags */
750+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
721751
0, /* tp_doc */
722-
0, /* tp_traverse */
752+
proxy_traverse, /* tp_traverse */
723753
0, /* tp_clear */
724754
0, /* tp_richcompare */
725755
0, /* tp_weaklistoffset */
@@ -739,10 +769,11 @@ PyDictProxy_New(PyObject *dict)
739769
{
740770
proxyobject *pp;
741771

742-
pp = PyObject_NEW(proxyobject, &proxytype);
772+
pp = PyObject_GC_New(proxyobject, &proxytype);
743773
if (pp != NULL) {
744774
Py_INCREF(dict);
745775
pp->dict = dict;
776+
_PyObject_GC_TRACK(pp);
746777
}
747778
return (PyObject *)pp;
748779
}
@@ -762,9 +793,10 @@ typedef struct {
762793
static void
763794
wrapper_dealloc(wrapperobject *wp)
764795
{
796+
_PyObject_GC_UNTRACK(wp);
765797
Py_XDECREF(wp->descr);
766798
Py_XDECREF(wp->self);
767-
PyObject_DEL(wp);
799+
PyObject_GC_Del(wp);
768800
}
769801

770802
static PyMethodDef wrapper_methods[] = {
@@ -808,6 +840,25 @@ wrapper_call(wrapperobject *wp, PyObject *args, PyObject *kwds)
808840
return (*wrapper)(self, args, wp->descr->d_wrapped);
809841
}
810842

843+
static int
844+
wrapper_traverse(PyObject *self, visitproc visit, void *arg)
845+
{
846+
wrapperobject *wp = (wrapperobject *)self;
847+
int err;
848+
849+
if (wp->descr) {
850+
err = visit((PyObject *)(wp->descr), arg);
851+
if (err)
852+
return err;
853+
}
854+
if (wp->self) {
855+
err = visit(wp->self, arg);
856+
if (err)
857+
return err;
858+
}
859+
return 0;
860+
}
861+
811862
PyTypeObject wrappertype = {
812863
PyObject_HEAD_INIT(&PyType_Type)
813864
0, /* ob_size */
@@ -830,9 +881,9 @@ PyTypeObject wrappertype = {
830881
PyObject_GenericGetAttr, /* tp_getattro */
831882
0, /* tp_setattro */
832883
0, /* tp_as_buffer */
833-
Py_TPFLAGS_DEFAULT, /* tp_flags */
884+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
834885
0, /* tp_doc */
835-
0, /* tp_traverse */
886+
wrapper_traverse, /* tp_traverse */
836887
0, /* tp_clear */
837888
0, /* tp_richcompare */
838889
0, /* tp_weaklistoffset */
@@ -857,12 +908,13 @@ PyWrapper_New(PyObject *d, PyObject *self)
857908
descr = (PyWrapperDescrObject *)d;
858909
assert(PyObject_IsInstance(self, (PyObject *)(descr->d_type)));
859910

860-
wp = PyObject_NEW(wrapperobject, &wrappertype);
911+
wp = PyObject_GC_New(wrapperobject, &wrappertype);
861912
if (wp != NULL) {
862913
Py_INCREF(descr);
863914
wp->descr = descr;
864915
Py_INCREF(self);
865916
wp->self = self;
917+
_PyObject_GC_TRACK(wp);
866918
}
867919
return (PyObject *)wp;
868920
}
@@ -919,6 +971,7 @@ property_dealloc(PyObject *self)
919971
{
920972
propertyobject *gs = (propertyobject *)self;
921973

974+
_PyObject_GC_UNTRACK(self);
922975
Py_XDECREF(gs->prop_get);
923976
Py_XDECREF(gs->prop_set);
924977
Py_XDECREF(gs->prop_del);
@@ -1012,6 +1065,26 @@ static char property_doc[] =
10121065
" def delx(self): del self.__x\n"
10131066
" x = property(getx, setx, delx, \"I'm the 'x' property.\")";
10141067

1068+
static int
1069+
property_traverse(PyObject *self, visitproc visit, void *arg)
1070+
{
1071+
propertyobject *pp = (propertyobject *)self;
1072+
int err;
1073+
1074+
#define VISIT(SLOT) \
1075+
if (pp->SLOT) { \
1076+
err = visit((PyObject *)(pp->SLOT), arg); \
1077+
if (err) \
1078+
return err; \
1079+
}
1080+
1081+
VISIT(prop_get);
1082+
VISIT(prop_set);
1083+
VISIT(prop_del);
1084+
1085+
return 0;
1086+
}
1087+
10151088
PyTypeObject PyProperty_Type = {
10161089
PyObject_HEAD_INIT(&PyType_Type)
10171090
0, /* ob_size */
@@ -1034,9 +1107,10 @@ PyTypeObject PyProperty_Type = {
10341107
PyObject_GenericGetAttr, /* tp_getattro */
10351108
0, /* tp_setattro */
10361109
0, /* tp_as_buffer */
1037-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1110+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
1111+
Py_TPFLAGS_BASETYPE, /* tp_flags */
10381112
property_doc, /* tp_doc */
1039-
0, /* tp_traverse */
1113+
property_traverse, /* tp_traverse */
10401114
0, /* tp_clear */
10411115
0, /* tp_richcompare */
10421116
0, /* tp_weaklistoffset */
@@ -1053,5 +1127,5 @@ PyTypeObject PyProperty_Type = {
10531127
property_init, /* tp_init */
10541128
PyType_GenericAlloc, /* tp_alloc */
10551129
PyType_GenericNew, /* tp_new */
1056-
_PyObject_Del, /* tp_free */
1130+
_PyObject_GC_Del, /* tp_free */
10571131
};

0 commit comments

Comments
 (0)