Commit f7f593f4 authored by Jeremy Hylton's avatar Jeremy Hylton

final patches from Neil Schemenauer for garbage collection

parent afa84cf0
......@@ -203,7 +203,6 @@ extern DL_IMPORT(void) _PyObject_Del Py_PROTO((PyObject *));
( (type *) PyObject_InitVar( \
(PyVarObject *) PyObject_MALLOC( _PyObject_VAR_SIZE((typeobj),(n)) ),\
(typeobj), (n)) )
#define PyObject_DEL(op) PyObject_FREE(op)
/* This example code implements an object constructor with a custom
allocator, where PyObject_New is inlined, and shows the important
......@@ -234,11 +233,67 @@ extern DL_IMPORT(void) _PyObject_Del Py_PROTO((PyObject *));
the 1st step is performed automatically for you, so in a C++ class
constructor you would start directly with PyObject_Init/InitVar. */
/*
* Garbage Collection Support
* ==========================
*/
/* To make a new object participate in garbage collection use
PyObject_{New, VarNew, Del} to manage the memory. Set the type flag
Py_TPFLAGS_GC and define the type method tp_recurse. You should also
add the method tp_clear if your object is mutable. Include
PyGC_INFO_SIZE in the calculation of tp_basicsize. Call
PyObject_GC_Init after the pointers followed by tp_recurse become
valid (usually just before returning the object from the allocation
method. Call PyObject_GC_Fini before those pointers become invalid
(usually at the top of the deallocation method). */
#ifndef WITH_CYCLE_GC
#define PyGC_INFO_SIZE 0
#endif
#define PyGC_HEAD_SIZE 0
#define PyObject_GC_Init(op)
#define PyObject_GC_Fini(op)
#define PyObject_AS_GC(op) (op)
#define PyObject_FROM_GC(op) (op)
#define PyObject_DEL(op) PyObject_FREE(op)
#else
/* Add the object into the container set */
extern DL_IMPORT(void) _PyGC_Insert Py_PROTO((PyObject *));
/* Remove the object from the container set */
extern DL_IMPORT(void) _PyGC_Remove Py_PROTO((PyObject *));
#define PyObject_GC_Init(op) _PyGC_Insert((PyObject *)op)
#define PyObject_GC_Fini(op) _PyGC_Remove((PyObject *)op)
/* Structure *prefixed* to container objects participating in GC */
typedef struct _gc_head {
struct _gc_head *gc_next;
struct _gc_head *gc_prev;
int gc_refs;
} PyGC_Head;
#define PyGC_HEAD_SIZE sizeof(PyGC_Head)
/* Test if a type has a GC head */
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_GC)
/* Test if an object has a GC head */
#define PyObject_IS_GC(o) PyType_IS_GC((o)->ob_type)
/* Get an object's GC head */
#define PyObject_AS_GC(o) ((PyGC_Head *)(o)-1)
/* Get the object given the PyGC_Head */
#define PyObject_FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
#define PyObject_DEL(op) PyObject_FREE( PyObject_IS_GC(op) ? \
(ANY *)PyObject_AS_GC(op) : \
(ANY *)(op) )
#endif /* WITH_CYCLE_GC */
#ifdef __cplusplus
}
......
test_gc
list 0x831a754
dict 0x831a754
list 0x831a754
tuple 0x831a734
class 0x831a794
<test_gc.A instance at 831a754>
a <test_gc.A instance at 831a754>
b <test_gc.B instance at 831a95c>
dict 0x831a9bc
func 0x831d9e4
import gc
def test_list():
l = []
l.append(l)
print 'list 0x%x' % id(l)
gc.collect()
del l
assert gc.collect() == 1
def test_dict():
d = {}
d[1] = d
print 'dict 0x%x' % id(d)
gc.collect()
del d
assert gc.collect() == 1
def test_tuple():
l = []
t = (l,)
l.append(t)
print 'list 0x%x' % id(l)
print 'tuple 0x%x' % id(t)
gc.collect()
del t
del l
assert gc.collect() == 2
def test_class():
class A:
pass
A.a = A
print 'class 0x%x' % id(A)
gc.collect()
del A
assert gc.collect() > 0
def test_instance():
class A:
pass
a = A()
a.a = a
print repr(a)
gc.collect()
del a
assert gc.collect() > 0
def test_method():
class A:
def __init__(self):
self.init = self.__init__
a = A()
gc.collect()
del a
assert gc.collect() > 0
def test_finalizer():
class A:
def __del__(self): pass
class B:
pass
a = A()
a.a = a
id_a = id(a)
b = B()
b.b = b
print 'a', repr(a)
print 'b', repr(b)
gc.collect()
gc.garbage[:] = []
del a
del b
assert gc.collect() > 0
assert id(gc.garbage[0]) == id_a
def test_function():
d = {}
exec("def f(): pass\n") in d
print 'dict 0x%x' % id(d)
print 'func 0x%x' % id(d['f'])
gc.collect()
del d
assert gc.collect() == 2
def test_all():
debug = gc.get_debug()
gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_STATS)
test_list()
test_dict()
test_tuple()
test_class()
test_instance()
test_method()
test_finalizer()
test_function()
gc.set_debug(debug)
test_all()
......@@ -10,3 +10,6 @@
# support threads.
@USE_THREAD_MODULE@thread threadmodule.c
# Garbage collection enabled with --with-cycle-gc
@USE_GC_MODULE@gc gcmodule.c
......@@ -2892,7 +2892,7 @@ Instance_New(PyObject *cls, PyObject *args) {
Py_DECREF(inst);
goto err;
}
PyObject_GC_Init(inst);
return (PyObject *)inst;
}
Py_DECREF(__getinitargs__);
......
This diff is collapsed.
......@@ -56,6 +56,7 @@ new_instance(unused, args)
Py_INCREF(dict);
inst->in_class = (PyClassObject *)klass;
inst->in_dict = dict;
PyObject_GC_Init(inst);
return (PyObject *)inst;
}
......
......@@ -132,6 +132,7 @@ PyClass_New(bases, dict, name)
Py_XINCREF(op->cl_getattr);
Py_XINCREF(op->cl_setattr);
Py_XINCREF(op->cl_delattr);
PyObject_GC_Init(op);
return (PyObject *) op;
}
......@@ -141,6 +142,7 @@ static void
class_dealloc(op)
PyClassObject *op;
{
PyObject_GC_Fini(op);
Py_DECREF(op->cl_bases);
Py_DECREF(op->cl_dict);
Py_XDECREF(op->cl_name);
......@@ -428,7 +430,7 @@ PyTypeObject PyClass_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"class",
sizeof(PyClassObject) + PyGC_INFO_SIZE,
sizeof(PyClassObject) + PyGC_HEAD_SIZE,
0,
(destructor)class_dealloc, /*tp_dealloc*/
0, /*tp_print*/
......@@ -490,6 +492,7 @@ PyInstance_New(class, arg, kw)
if (inst == NULL)
return NULL;
inst->in_dict = PyDict_New();
PyObject_GC_Init(inst);
if (inst->in_dict == NULL) {
PyObject_DEL(inst);
return NULL;
......@@ -539,11 +542,12 @@ instance_dealloc(inst)
PyObject *error_type, *error_value, *error_traceback;
PyObject *del;
static PyObject *delstr;
extern long _Py_RefTotal;
PyObject_GC_Fini(inst);
/* Call the __del__ method if it exists. First temporarily
revive the object and save the current exception, if any. */
#ifdef Py_TRACE_REFS
/* much too complicated if Py_TRACE_REFS defined */
extern long _Py_RefTotal;
inst->ob_type = &PyInstance_Type;
_Py_NewReference((PyObject *)inst);
_Py_RefTotal--; /* compensate for increment in NEWREF */
......@@ -591,6 +595,7 @@ instance_dealloc(inst)
#ifdef COUNT_ALLOCS
inst->ob_type->tp_free--;
#endif
PyObject_GC_Init((PyObject *)inst);
return; /* __del__ added a reference; don't delete now */
}
#ifdef Py_TRACE_REFS
......@@ -598,7 +603,9 @@ instance_dealloc(inst)
inst->ob_type->tp_free--; /* compensate for increment in UNREF */
#endif
_Py_ForgetReference((PyObject *)inst);
#ifndef WITH_CYCLE_GC
inst->ob_type = NULL;
#endif
#endif /* Py_TRACE_REFS */
Py_DECREF(inst->in_class);
Py_XDECREF(inst->in_dict);
......@@ -1510,7 +1517,7 @@ PyTypeObject PyInstance_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"instance",
sizeof(PyInstanceObject) + PyGC_INFO_SIZE,
sizeof(PyInstanceObject) + PyGC_HEAD_SIZE,
0,
(destructor)instance_dealloc, /*tp_dealloc*/
0, /*tp_print*/
......@@ -1568,6 +1575,7 @@ PyMethod_New(func, self, class)
im->im_self = self;
Py_INCREF(class);
im->im_class = class;
PyObject_GC_Init(im);
return (PyObject *)im;
}
......@@ -1643,6 +1651,7 @@ static void
instancemethod_dealloc(im)
register PyMethodObject *im;
{
PyObject_GC_Fini(im);
Py_DECREF(im->im_func);
Py_XDECREF(im->im_self);
Py_DECREF(im->im_class);
......@@ -1745,7 +1754,7 @@ PyTypeObject PyMethod_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"instance method",
sizeof(PyMethodObject) + PyGC_INFO_SIZE,
sizeof(PyMethodObject) + PyGC_HEAD_SIZE,
0,
(destructor)instancemethod_dealloc, /*tp_dealloc*/
0, /*tp_print*/
......
......@@ -129,6 +129,7 @@ PyDict_New()
mp->ma_table = NULL;
mp->ma_fill = 0;
mp->ma_used = 0;
PyObject_GC_Init(mp);
return (PyObject *)mp;
}
......@@ -481,6 +482,7 @@ dict_dealloc(mp)
register int i;
register dictentry *ep;
Py_TRASHCAN_SAFE_BEGIN(mp)
PyObject_GC_Fini(mp);
for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
if (ep->me_key != NULL) {
Py_DECREF(ep->me_key);
......@@ -1087,7 +1089,7 @@ PyTypeObject PyDict_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"dictionary",
sizeof(dictobject) + PyGC_INFO_SIZE,
sizeof(dictobject) + PyGC_HEAD_SIZE,
0,
(destructor)dict_dealloc, /*tp_dealloc*/
(printfunc)dict_print, /*tp_print*/
......
......@@ -63,6 +63,7 @@ PyFunction_New(code, globals)
Py_INCREF(doc);
op->func_doc = doc;
}
PyObject_GC_Init(op);
return (PyObject *)op;
}
......@@ -186,6 +187,7 @@ static void
func_dealloc(op)
PyFunctionObject *op;
{
PyObject_GC_Fini(op);
Py_DECREF(op->func_code);
Py_DECREF(op->func_globals);
Py_DECREF(op->func_name);
......@@ -277,7 +279,7 @@ PyTypeObject PyFunction_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"function",
sizeof(PyFunctionObject) + PyGC_INFO_SIZE,
sizeof(PyFunctionObject) + PyGC_HEAD_SIZE,
0,
(destructor)func_dealloc, /*tp_dealloc*/
0, /*tp_print*/
......
......@@ -72,10 +72,11 @@ PyList_New(size)
}
/* PyObject_NewVar is inlined */
op = (PyListObject *) PyObject_MALLOC(sizeof(PyListObject)
+ PyGC_INFO_SIZE);
+ PyGC_HEAD_SIZE);
if (op == NULL) {
return PyErr_NoMemory();
}
op = (PyListObject *) PyObject_FROM_GC(op);
if (size <= 0) {
op->ob_item = NULL;
}
......@@ -89,6 +90,7 @@ PyList_New(size)
PyObject_INIT_VAR(op, &PyList_Type, size);
for (i = 0; i < size; i++)
op->ob_item[i] = NULL;
PyObject_GC_Init(op);
return (PyObject *) op;
}
......@@ -216,6 +218,7 @@ list_dealloc(op)
{
int i;
Py_TRASHCAN_SAFE_BEGIN(op)
PyObject_GC_Fini(op);
if (op->ob_item != NULL) {
/* Do it backwards, for Christian Tismer.
There's a simple test case where somehow this reduces
......@@ -1498,7 +1501,7 @@ PyTypeObject PyList_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"list",
sizeof(PyListObject) + PyGC_INFO_SIZE,
sizeof(PyListObject) + PyGC_HEAD_SIZE,
0,
(destructor)list_dealloc, /*tp_dealloc*/
(printfunc)list_print, /*tp_print*/
......@@ -1577,7 +1580,7 @@ static PyTypeObject immutable_list_type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"list (immutable, during sort)",
sizeof(PyListObject) + PyGC_INFO_SIZE,
sizeof(PyListObject) + PyGC_HEAD_SIZE,
0,
0, /*tp_dealloc*/ /* Cannot happen */
(printfunc)list_print, /*tp_print*/
......
......@@ -124,6 +124,10 @@ PyObject_Init(op, tp)
"NULL object passed to PyObject_Init");
return op;
}
#ifdef WITH_CYCLE_GC
if (PyType_IS_GC(tp))
op = (PyObject *) PyObject_FROM_GC(op);
#endif
/* Any changes should be reflected in PyObject_INIT (objimpl.h) */
op->ob_type = tp;
_Py_NewReference(op);
......@@ -141,6 +145,10 @@ PyObject_InitVar(op, tp, size)
"NULL object passed to PyObject_InitVar");
return op;
}
#ifdef WITH_CYCLE_GC
if (PyType_IS_GC(tp))
op = (PyVarObject *) PyObject_FROM_GC(op);
#endif
/* Any changes should be reflected in PyObject_INIT_VAR */
op->ob_size = size;
op->ob_type = tp;
......@@ -156,6 +164,10 @@ _PyObject_New(tp)
op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp));
if (op == NULL)
return PyErr_NoMemory();
#ifdef WITH_CYCLE_GC
if (PyType_IS_GC(tp))
op = (PyObject *) PyObject_FROM_GC(op);
#endif
return PyObject_INIT(op, tp);
}
......@@ -168,6 +180,10 @@ _PyObject_NewVar(tp, size)
op = (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size));
if (op == NULL)
return (PyVarObject *)PyErr_NoMemory();
#ifdef WITH_CYCLE_GC
if (PyType_IS_GC(tp))
op = (PyVarObject *) PyObject_FROM_GC(op);
#endif
return PyObject_INIT_VAR(op, tp, size);
}
......@@ -175,9 +191,23 @@ void
_PyObject_Del(op)
PyObject *op;
{
PyObject_FREE(op);
#ifdef WITH_CYCLE_GC
if (PyType_IS_GC(op->ob_type)) {
PyGC_Head *g = PyObject_AS_GC(op);
PyObject_FREE(g);
} else
#endif
{
PyObject_FREE(op);
}
}
#ifndef WITH_CYCLE_GC
/* extension modules might need these */
void _PyGC_Insert(PyObject *op) { }
void _PyGC_Remove(PyObject *op) { }
#endif
int
PyObject_Print(op, fp, flags)
PyObject *op;
......@@ -917,8 +947,10 @@ _Py_Dealloc(op)
{
destructor dealloc = op->ob_type->tp_dealloc;
_Py_ForgetReference(op);
#ifndef WITH_CYCLE_GC
if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL-1)
op->ob_type = NULL;
#endif
(*dealloc)(op);
}
......
......@@ -94,7 +94,7 @@ PyTuple_New(size)
/* Check for overflow */
if (nbytes / sizeof(PyObject *) != (size_t)size ||
(nbytes += sizeof(PyTupleObject) - sizeof(PyObject *)
+ PyGC_INFO_SIZE)
+ PyGC_HEAD_SIZE)
<= 0)
{
return PyErr_NoMemory();
......@@ -103,7 +103,7 @@ PyTuple_New(size)
op = (PyTupleObject *) PyObject_MALLOC(nbytes);
if (op == NULL)
return PyErr_NoMemory();
op = (PyTupleObject *) PyObject_FROM_GC(op);
PyObject_INIT_VAR(op, &PyTuple_Type, size);
}
for (i = 0; i < size; i++)
......@@ -115,6 +115,7 @@ PyTuple_New(size)
Py_INCREF(op); /* extra INCREF so that this is never freed */
}
#endif
PyObject_GC_Init(op);
return (PyObject *) op;
}
......@@ -181,6 +182,7 @@ tupledealloc(op)
register int i;
register int len = op->ob_size;
Py_TRASHCAN_SAFE_BEGIN(op)
PyObject_GC_Fini(op);
if (len > 0) {
i = len;
while (--i >= 0)
......@@ -453,7 +455,7 @@ PyTypeObject PyTuple_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"tuple",
sizeof(PyTupleObject) - sizeof(PyObject *) + PyGC_INFO_SIZE,
sizeof(PyTupleObject) - sizeof(PyObject *) + PyGC_HEAD_SIZE,
sizeof(PyObject *),
(destructor)tupledealloc, /*tp_dealloc*/
(printfunc)tupleprint, /*tp_print*/
......@@ -557,12 +559,27 @@ _PyTuple_Resize(pv, newsize, last_is_sticky)
} else
#endif
{
#ifdef WITH_CYCLE_GC
PyGC_Head *g = PyObject_AS_GC((PyObject *)v);
PyObject_GC_Fini((PyObject *)v);
sv = (PyTupleObject *)
PyObject_REALLOC((char *)g, sizeof(PyTupleObject)
+ PyGC_HEAD_SIZE
+ newsize * sizeof(PyObject *));
if (g == NULL) {
sv = NULL;
} else {
sv = (PyTupleObject *)PyObject_FROM_GC(g);
}
#else
sv = (PyTupleObject *)
PyObject_REALLOC((char *)v, sizeof(PyTupleObject)
+ PyGC_INFO_SIZE
+ PyGC_HEAD_SIZE
+ newsize * sizeof(PyObject *));
#endif
*pv = (PyObject *) sv;
if (sv == NULL) {
PyObject_GC_Init((PyObject *)v);
PyObject_DEL(v);
PyErr_NoMemory();
return -1;
......@@ -578,6 +595,7 @@ _PyTuple_Resize(pv, newsize, last_is_sticky)
sv->ob_item[i - sizediff] = NULL;
}
}
PyObject_GC_Init(sv);
sv->ob_size = newsize;
return 0;
}
......
......@@ -43,6 +43,9 @@ extern void initbinascii();
#endif
extern void initcmath();
extern void initerrno();
#ifdef WITH_CYCLE_GC
extern void initgc();
#endif
#ifndef MS_WIN64
extern void initimageop();
#endif
......@@ -89,6 +92,9 @@ struct _inittab _PyImport_Inittab[] = {
#endif
{"cmath", initcmath},
{"errno", initerrno},
#ifdef WITH_CYCLE_GC
{"gc", initgc},
#endif
#ifndef MS_WIN64
{"imageop", initimageop},
#endif
......
......@@ -451,6 +451,9 @@ typedef long intptr_t;
/* Define if you want to use the GNU readline library */
/* #define WITH_READLINE 1 */
/* Define if you want cycle garbage collection */
/* #define WITH_CYCLE_GC 1 */
/* Define if you have clock. */
/* #define HAVE_CLOCK */
......
......@@ -659,6 +659,20 @@ SOURCE=..\Objects\funcobject.c
# End Source File
# Begin Source File
SOURCE=..\Modules\gcmodule.c
!IF "$(CFG)" == "python16 - Win32 Release"
!ELSEIF "$(CFG)" == "python16 - Win32 Debug"
!ELSEIF "$(CFG)" == "python16 - Win32 Alpha Debug"
!ELSEIF "$(CFG)" == "python16 - Win32 Alpha Release"
!ENDIF
# End Source File
# Begin Source File
SOURCE=..\Python\getargs.c
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment