final patches from Neil Schemenauer for garbage collection
This commit is contained in:
parent
4e542a3d99
commit
c5007aa5c3
|
@ -203,7 +203,6 @@ extern DL_IMPORT(void) _PyObject_Del Py_PROTO((PyObject *));
|
||||||
( (type *) PyObject_InitVar( \
|
( (type *) PyObject_InitVar( \
|
||||||
(PyVarObject *) PyObject_MALLOC( _PyObject_VAR_SIZE((typeobj),(n)) ),\
|
(PyVarObject *) PyObject_MALLOC( _PyObject_VAR_SIZE((typeobj),(n)) ),\
|
||||||
(typeobj), (n)) )
|
(typeobj), (n)) )
|
||||||
#define PyObject_DEL(op) PyObject_FREE(op)
|
|
||||||
|
|
||||||
/* This example code implements an object constructor with a custom
|
/* This example code implements an object constructor with a custom
|
||||||
allocator, where PyObject_New is inlined, and shows the important
|
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
|
the 1st step is performed automatically for you, so in a C++ class
|
||||||
constructor you would start directly with PyObject_Init/InitVar. */
|
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
|
#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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
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
|
|
@ -0,0 +1,100 @@
|
||||||
|
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.
|
# support threads.
|
||||||
|
|
||||||
@USE_THREAD_MODULE@thread threadmodule.c
|
@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);
|
Py_DECREF(inst);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
PyObject_GC_Init(inst);
|
||||||
return (PyObject *)inst;
|
return (PyObject *)inst;
|
||||||
}
|
}
|
||||||
Py_DECREF(__getinitargs__);
|
Py_DECREF(__getinitargs__);
|
||||||
|
|
|
@ -0,0 +1,679 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Reference Cycle Garbage Collection
|
||||||
|
==================================
|
||||||
|
|
||||||
|
Neil Schemenauer <nascheme@enme.ucalgary.ca>
|
||||||
|
|
||||||
|
Based on a post on the python-dev list. Ideas from Guido van Rossum,
|
||||||
|
Eric Tiedemann, and various others.
|
||||||
|
|
||||||
|
http://www.enme.calgary.ca/~nascheme/python/gc.html
|
||||||
|
http://www.python.org/pipermail/python-dev/2000-March/003869.html
|
||||||
|
http://www.python.org/pipermail/python-dev/2000-March/004010.html
|
||||||
|
http://www.python.org/pipermail/python-dev/2000-March/004022.html
|
||||||
|
|
||||||
|
For a highlevel view of the collection process, read the collect
|
||||||
|
function.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
use a different interface for set_debug() (keywords)?
|
||||||
|
tune parameters
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
#ifdef WITH_CYCLE_GC
|
||||||
|
|
||||||
|
/* magic gc_refs value */
|
||||||
|
#define GC_MOVED -1
|
||||||
|
|
||||||
|
/*** Global GC state ***/
|
||||||
|
|
||||||
|
/* linked lists of container objects */
|
||||||
|
static PyGC_Head generation0 = {&generation0, &generation0, 0};
|
||||||
|
static PyGC_Head generation1 = {&generation1, &generation1, 0};
|
||||||
|
static PyGC_Head generation2 = {&generation2, &generation2, 0};
|
||||||
|
static int generation = 0; /* current generation being collected */
|
||||||
|
|
||||||
|
/* collection frequencies, XXX tune these */
|
||||||
|
static int threshold0 = 100; /* net new containers before collection */
|
||||||
|
static int threshold1 = 10; /* generation0 collections before collecting 1 */
|
||||||
|
static int threshold2 = 10; /* generation1 collections before collecting 2 */
|
||||||
|
|
||||||
|
/* net new objects allocated since last collection */
|
||||||
|
static int allocated;
|
||||||
|
|
||||||
|
/* set for debugging information */
|
||||||
|
#define DEBUG_STATS (1<<0) /* print collection statistics */
|
||||||
|
#define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */
|
||||||
|
#define DEBUG_UNCOLLECTABLE (1<<2) /* print uncollectable objects */
|
||||||
|
#define DEBUG_INSTANCES (1<<3) /* print instances */
|
||||||
|
#define DEBUG_OBJECTS (1<<4) /* print other objects */
|
||||||
|
#define DEBUG_LEAK DEBUG_COLLECTABLE | \
|
||||||
|
DEBUG_UNCOLLECTABLE | \
|
||||||
|
DEBUG_INSTANCES | \
|
||||||
|
DEBUG_OBJECTS
|
||||||
|
static int debug;
|
||||||
|
|
||||||
|
/* list of uncollectable objects */
|
||||||
|
static PyObject *garbage;
|
||||||
|
|
||||||
|
|
||||||
|
/*** list functions ***/
|
||||||
|
|
||||||
|
static void
|
||||||
|
gc_list_init(PyGC_Head *list)
|
||||||
|
{
|
||||||
|
list->gc_prev = list;
|
||||||
|
list->gc_next = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gc_list_append(PyGC_Head *node, PyGC_Head *list)
|
||||||
|
{
|
||||||
|
node->gc_next = list;
|
||||||
|
node->gc_prev = list->gc_prev;
|
||||||
|
node->gc_prev->gc_next = node;
|
||||||
|
list->gc_prev = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gc_list_remove(PyGC_Head *node)
|
||||||
|
{
|
||||||
|
node->gc_prev->gc_next = node->gc_next;
|
||||||
|
node->gc_next->gc_prev = node->gc_prev;
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
node->gc_prev = NULL;
|
||||||
|
node->gc_next = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gc_list_move(PyGC_Head *from, PyGC_Head *to)
|
||||||
|
{
|
||||||
|
if (from->gc_next == from) {
|
||||||
|
/* empty from list */
|
||||||
|
gc_list_init(to);
|
||||||
|
} else {
|
||||||
|
to->gc_next = from->gc_next;
|
||||||
|
to->gc_next->gc_prev = to;
|
||||||
|
to->gc_prev = from->gc_prev;
|
||||||
|
to->gc_prev->gc_next = to;
|
||||||
|
}
|
||||||
|
gc_list_init(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append a list onto another list, from becomes an empty list */
|
||||||
|
static void
|
||||||
|
gc_list_merge(PyGC_Head *from, PyGC_Head *to)
|
||||||
|
{
|
||||||
|
PyGC_Head *tail;
|
||||||
|
if (from->gc_next != from) {
|
||||||
|
tail = to->gc_prev;
|
||||||
|
tail->gc_next = from->gc_next;
|
||||||
|
tail->gc_next->gc_prev = tail;
|
||||||
|
to->gc_prev = from->gc_prev;
|
||||||
|
to->gc_prev->gc_next = to;
|
||||||
|
}
|
||||||
|
gc_list_init(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
gc_list_size(PyGC_Head *list)
|
||||||
|
{
|
||||||
|
PyGC_Head *gc;
|
||||||
|
long n = 0;
|
||||||
|
for (gc = list->gc_next; gc != list; gc = gc->gc_next) {
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** end of list stuff ***/
|
||||||
|
|
||||||
|
|
||||||
|
/* Set all gc_refs = ob_refcnt */
|
||||||
|
static void
|
||||||
|
update_refs(PyGC_Head *containers)
|
||||||
|
{
|
||||||
|
PyGC_Head *gc = containers->gc_next;
|
||||||
|
for (; gc != containers; gc=gc->gc_next) {
|
||||||
|
gc->gc_refs = PyObject_FROM_GC(gc)->ob_refcnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
visit_decref(PyObject *op, void *data)
|
||||||
|
{
|
||||||
|
if (op && PyObject_IS_GC(op)) {
|
||||||
|
PyObject_AS_GC(op)->gc_refs--;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtract internal references from gc_refs */
|
||||||
|
static void
|
||||||
|
subtract_refs(PyGC_Head *containers)
|
||||||
|
{
|
||||||
|
traverseproc traverse;
|
||||||
|
PyGC_Head *gc = containers->gc_next;
|
||||||
|
for (; gc != containers; gc=gc->gc_next) {
|
||||||
|
traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse;
|
||||||
|
(void) traverse(PyObject_FROM_GC(gc),
|
||||||
|
(visitproc)visit_decref,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append objects with gc_refs > 0 to roots list */
|
||||||
|
static void
|
||||||
|
move_roots(PyGC_Head *containers, PyGC_Head *roots)
|
||||||
|
{
|
||||||
|
PyGC_Head *next;
|
||||||
|
PyGC_Head *gc = containers->gc_next;
|
||||||
|
while (gc != containers) {
|
||||||
|
next = gc->gc_next;
|
||||||
|
if (gc->gc_refs > 0) {
|
||||||
|
gc_list_remove(gc);
|
||||||
|
gc_list_append(gc, roots);
|
||||||
|
gc->gc_refs = GC_MOVED;
|
||||||
|
}
|
||||||
|
gc = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
visit_reachable(PyObject *op, PyGC_Head *roots)
|
||||||
|
{
|
||||||
|
if (PyObject_IS_GC(op)) {
|
||||||
|
PyGC_Head *gc = PyObject_AS_GC(op);
|
||||||
|
if (gc && gc->gc_refs != GC_MOVED) {
|
||||||
|
gc_list_remove(gc);
|
||||||
|
gc_list_append(gc, roots);
|
||||||
|
gc->gc_refs = GC_MOVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move objects referenced from reachable to reachable set. */
|
||||||
|
static void
|
||||||
|
move_root_reachable(PyGC_Head *reachable)
|
||||||
|
{
|
||||||
|
traverseproc traverse;
|
||||||
|
PyGC_Head *gc = reachable->gc_next;
|
||||||
|
for (; gc != reachable; gc=gc->gc_next) {
|
||||||
|
/* careful, reachable list is growing here */
|
||||||
|
PyObject *op = PyObject_FROM_GC(gc);
|
||||||
|
traverse = op->ob_type->tp_traverse;
|
||||||
|
(void) traverse(op,
|
||||||
|
(visitproc)visit_reachable,
|
||||||
|
(void *)reachable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move all objects with finalizers (instances with __del__) */
|
||||||
|
static void
|
||||||
|
move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
|
||||||
|
{
|
||||||
|
PyGC_Head *next;
|
||||||
|
PyGC_Head *gc = unreachable->gc_next;
|
||||||
|
static PyObject *delstr;
|
||||||
|
if (delstr == NULL) {
|
||||||
|
delstr = PyString_InternFromString("__del__");
|
||||||
|
}
|
||||||
|
for (; gc != unreachable; gc=next) {
|
||||||
|
PyObject *op = PyObject_FROM_GC(gc);
|
||||||
|
next = gc->gc_next;
|
||||||
|
if (PyInstance_Check(op) && PyObject_HasAttr(op, delstr)) {
|
||||||
|
gc_list_remove(gc);
|
||||||
|
gc_list_append(gc, finalizers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* called by tp_traverse */
|
||||||
|
static int
|
||||||
|
visit_finalizer_reachable(PyObject *op, PyGC_Head *finalizers)
|
||||||
|
{
|
||||||
|
if (PyObject_IS_GC(op)) {
|
||||||
|
PyGC_Head *gc = PyObject_AS_GC(op);
|
||||||
|
if (gc && gc->gc_refs != GC_MOVED) {
|
||||||
|
gc_list_remove(gc);
|
||||||
|
gc_list_append(gc, finalizers);
|
||||||
|
gc->gc_refs = GC_MOVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move objects referenced from roots to roots */
|
||||||
|
static void
|
||||||
|
move_finalizer_reachable(PyGC_Head *finalizers)
|
||||||
|
{
|
||||||
|
traverseproc traverse;
|
||||||
|
PyGC_Head *gc = finalizers->gc_next;
|
||||||
|
for (; gc != finalizers; gc=gc->gc_next) {
|
||||||
|
/* careful, finalizers list is growing here */
|
||||||
|
traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse;
|
||||||
|
(void) traverse(PyObject_FROM_GC(gc),
|
||||||
|
(visitproc)visit_finalizer_reachable,
|
||||||
|
(void *)finalizers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
debug_instance(PyObject *output, char *msg, PyInstanceObject *inst)
|
||||||
|
{
|
||||||
|
char buf[200];
|
||||||
|
char *cname;
|
||||||
|
/* be careful not to create new dictionaries */
|
||||||
|
PyObject *classname = inst->in_class->cl_name;
|
||||||
|
if (classname != NULL && PyString_Check(classname))
|
||||||
|
cname = PyString_AsString(classname);
|
||||||
|
else
|
||||||
|
cname = "?";
|
||||||
|
sprintf(buf, "gc: %s<%.100s instance at %lx>\n",
|
||||||
|
msg, cname, (long)inst);
|
||||||
|
PyFile_WriteString(buf, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
debug_cycle(PyObject *output, char *msg, PyObject *op)
|
||||||
|
{
|
||||||
|
if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) {
|
||||||
|
debug_instance(output, msg, (PyInstanceObject *)op);
|
||||||
|
} else if (debug & DEBUG_OBJECTS) {
|
||||||
|
char buf[200];
|
||||||
|
sprintf(buf, "gc: %s<%s 0x%x>\n",
|
||||||
|
msg,
|
||||||
|
op->ob_type->tp_name,
|
||||||
|
(long)op);
|
||||||
|
PyFile_WriteString(buf, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle uncollectable garbage (cycles with finalizers). */
|
||||||
|
static void
|
||||||
|
handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
|
||||||
|
{
|
||||||
|
PyGC_Head *gc;
|
||||||
|
if (garbage == NULL) {
|
||||||
|
garbage = PyList_New(0);
|
||||||
|
}
|
||||||
|
for (gc = finalizers->gc_next; gc != finalizers;
|
||||||
|
gc = finalizers->gc_next) {
|
||||||
|
PyObject *op = PyObject_FROM_GC(gc);
|
||||||
|
/* Add all instances to a Python accessible list of garbage */
|
||||||
|
if (PyInstance_Check(op)) {
|
||||||
|
PyList_Append(garbage, op);
|
||||||
|
}
|
||||||
|
/* We assume that all objects in finalizers are reachable from
|
||||||
|
* instances. Once we add the instances to the garbage list
|
||||||
|
* everything is reachable from Python again. */
|
||||||
|
gc_list_remove(gc);
|
||||||
|
gc_list_append(gc, old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Break reference cycles by clearing the containers involved. This is
|
||||||
|
* tricky business as the lists can be changing and we don't know which
|
||||||
|
* objects may be freed. It is possible I screwed something up here. */
|
||||||
|
static void
|
||||||
|
delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
|
||||||
|
{
|
||||||
|
inquiry clear;
|
||||||
|
|
||||||
|
while (unreachable->gc_next != unreachable) {
|
||||||
|
PyGC_Head *gc = unreachable->gc_next;
|
||||||
|
PyObject *op = PyObject_FROM_GC(gc);
|
||||||
|
/*
|
||||||
|
PyList_Append(garbage, op);
|
||||||
|
*/
|
||||||
|
if ((clear = op->ob_type->tp_clear) != NULL) {
|
||||||
|
Py_INCREF(op);
|
||||||
|
clear((PyObject *)op);
|
||||||
|
Py_DECREF(op);
|
||||||
|
}
|
||||||
|
/* only try to call tp_clear once for each object */
|
||||||
|
if (unreachable->gc_next == gc) {
|
||||||
|
/* still alive, move it, it may die later */
|
||||||
|
gc_list_remove(gc);
|
||||||
|
gc_list_append(gc, old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the main function. Read this to understand how the
|
||||||
|
* collection process works. */
|
||||||
|
static long
|
||||||
|
collect(PyGC_Head *young, PyGC_Head *old)
|
||||||
|
{
|
||||||
|
long n = 0;
|
||||||
|
long m = 0;
|
||||||
|
PyGC_Head reachable;
|
||||||
|
PyGC_Head unreachable;
|
||||||
|
PyGC_Head finalizers;
|
||||||
|
PyGC_Head *gc;
|
||||||
|
PyObject *output = NULL;
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
output = PySys_GetObject("stderr");
|
||||||
|
}
|
||||||
|
if (debug & DEBUG_STATS) {
|
||||||
|
char buf[100];
|
||||||
|
sprintf(buf, "gc: collecting generation %d...\n", generation);
|
||||||
|
PyFile_WriteString(buf,output);
|
||||||
|
sprintf(buf, "gc: objects in each generation: %d %d %d\n",
|
||||||
|
gc_list_size(&generation0),
|
||||||
|
gc_list_size(&generation1),
|
||||||
|
gc_list_size(&generation2));
|
||||||
|
PyFile_WriteString(buf,output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Using ob_refcnt and gc_refs, calculate which objects in the
|
||||||
|
* container set are reachable from outside the set (ie. have a
|
||||||
|
* refcount greater than 0 when all the references within the
|
||||||
|
* set are taken into account */
|
||||||
|
update_refs(young);
|
||||||
|
subtract_refs(young);
|
||||||
|
|
||||||
|
/* Move everything reachable from outside the set into the
|
||||||
|
* reachable set (ie. gc_refs > 0). Next, move everything
|
||||||
|
* reachable from objects in the reachable set. */
|
||||||
|
gc_list_init(&reachable);
|
||||||
|
move_roots(young, &reachable);
|
||||||
|
move_root_reachable(&reachable);
|
||||||
|
|
||||||
|
/* move unreachable objects to a temporary list, new objects can be
|
||||||
|
* allocated after this point */
|
||||||
|
gc_list_init(&unreachable);
|
||||||
|
gc_list_move(young, &unreachable);
|
||||||
|
|
||||||
|
/* move reachable objects to next generation */
|
||||||
|
gc_list_merge(&reachable, old);
|
||||||
|
|
||||||
|
/* Move objects reachable from finalizers, we can't safely delete
|
||||||
|
* them. Python programmers should take care not to create such
|
||||||
|
* things. For Python finalizers means instance objects with
|
||||||
|
* __del__ methods. */
|
||||||
|
gc_list_init(&finalizers);
|
||||||
|
move_finalizers(&unreachable, &finalizers);
|
||||||
|
move_finalizer_reachable(&finalizers);
|
||||||
|
|
||||||
|
/* Collect statistics on collectable objects found and print
|
||||||
|
* debugging information. */
|
||||||
|
for (gc = unreachable.gc_next; gc != &unreachable;
|
||||||
|
gc = gc->gc_next) {
|
||||||
|
m++;
|
||||||
|
if (output != NULL && (debug & DEBUG_COLLECTABLE)) {
|
||||||
|
debug_cycle(output, "collectable ", PyObject_FROM_GC(gc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* call tp_clear on objects in the collectable set. This will cause
|
||||||
|
* the reference cycles to be broken. It may also cause some objects in
|
||||||
|
* finalizers to be freed */
|
||||||
|
delete_garbage(&unreachable, old);
|
||||||
|
|
||||||
|
/* Collect statistics on uncollectable objects found and print
|
||||||
|
* debugging information. */
|
||||||
|
for (gc = finalizers.gc_next; gc != &finalizers;
|
||||||
|
gc = gc->gc_next) {
|
||||||
|
n++;
|
||||||
|
if (output != NULL && (debug & DEBUG_UNCOLLECTABLE)) {
|
||||||
|
debug_cycle(output, "uncollectable ", PyObject_FROM_GC(gc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (output != NULL && (debug & DEBUG_STATS)) {
|
||||||
|
if (m == 0 && n == 0) {
|
||||||
|
PyFile_WriteString("gc: done.\n", output);
|
||||||
|
} else {
|
||||||
|
char buf[200];
|
||||||
|
sprintf(buf,
|
||||||
|
"gc: done, %d unreachable, %d uncollectable.\n",
|
||||||
|
n+m, n);
|
||||||
|
PyFile_WriteString(buf, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append instances in the uncollectable set to a Python
|
||||||
|
* reachable list of garbage. The programmer has to deal with
|
||||||
|
* this if they insist on creating this type of structure. */
|
||||||
|
handle_finalizers(&finalizers, old);
|
||||||
|
|
||||||
|
allocated = 0;
|
||||||
|
PyErr_Clear(); /* in case writing to sys.stderr failed */
|
||||||
|
return n+m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
collect_generations(void)
|
||||||
|
{
|
||||||
|
static long collections0 = 0;
|
||||||
|
static long collections1 = 0;
|
||||||
|
long n;
|
||||||
|
|
||||||
|
|
||||||
|
if (collections1 > threshold2) {
|
||||||
|
generation = 2;
|
||||||
|
gc_list_merge(&generation0, &generation2);
|
||||||
|
gc_list_merge(&generation1, &generation2);
|
||||||
|
if (generation2.gc_next != &generation2) {
|
||||||
|
n = collect(&generation2, &generation2);
|
||||||
|
}
|
||||||
|
collections1 = 0;
|
||||||
|
} else if (collections0 > threshold1) {
|
||||||
|
generation = 1;
|
||||||
|
collections1++;
|
||||||
|
gc_list_merge(&generation0, &generation1);
|
||||||
|
if (generation1.gc_next != &generation1) {
|
||||||
|
n = collect(&generation1, &generation2);
|
||||||
|
}
|
||||||
|
collections0 = 0;
|
||||||
|
} else {
|
||||||
|
generation = 0;
|
||||||
|
collections0++;
|
||||||
|
if (generation0.gc_next != &generation0) {
|
||||||
|
n = collect(&generation0, &generation1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyGC_Insert(PyObject *op)
|
||||||
|
{
|
||||||
|
/* collection lock since collecting may cause allocations */
|
||||||
|
static int collecting = 0;
|
||||||
|
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
if (!PyObject_IS_GC(op)) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (threshold0 && allocated > threshold0 && !collecting) {
|
||||||
|
collecting++;
|
||||||
|
collect_generations();
|
||||||
|
collecting--;
|
||||||
|
}
|
||||||
|
allocated++;
|
||||||
|
gc_list_append(PyObject_AS_GC(op), &generation0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyGC_Remove(PyObject *op)
|
||||||
|
{
|
||||||
|
PyGC_Head *g = PyObject_AS_GC(op);
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
if (!PyObject_IS_GC(op)) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
gc_list_remove(g);
|
||||||
|
if (allocated > 0) {
|
||||||
|
allocated--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char collect__doc__[] =
|
||||||
|
"collect() -> n\n"
|
||||||
|
"\n"
|
||||||
|
"Run a full collection. The number of unreachable objects is returned.\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Py_collect(self, args)
|
||||||
|
PyObject *self;
|
||||||
|
PyObject *args;
|
||||||
|
{
|
||||||
|
long n;
|
||||||
|
|
||||||
|
if(!PyArg_ParseTuple(args, "")) /* check no args */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
generation = 2;
|
||||||
|
gc_list_merge(&generation0, &generation2);
|
||||||
|
gc_list_merge(&generation1, &generation2);
|
||||||
|
n = collect(&generation2, &generation2);
|
||||||
|
|
||||||
|
return Py_BuildValue("i", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char set_debug__doc__[] =
|
||||||
|
"set_debug(flags) -> None\n"
|
||||||
|
"\n"
|
||||||
|
"Set the garbage collection debugging flags. Debugging information is\n"
|
||||||
|
"written to sys.stderr.\n"
|
||||||
|
"\n"
|
||||||
|
"flags is an integer and can have the following bits turned on:\n"
|
||||||
|
"\n"
|
||||||
|
" DEBUG_STATS - Print statistics during collection.\n"
|
||||||
|
" DEBUG_COLLECTABLE - Print collectable objects found.\n"
|
||||||
|
" DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n"
|
||||||
|
" DEBUG_INSTANCES - Print instance objects.\n"
|
||||||
|
" DEBUG_OBJECTS - Print objects other than instances.\n"
|
||||||
|
" DEBUG_LEAK - Debug leaking programs (everything but STATS).\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Py_set_debug(self, args)
|
||||||
|
PyObject *self;
|
||||||
|
PyObject *args;
|
||||||
|
{
|
||||||
|
if (!PyArg_ParseTuple(args, "l", &debug))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char get_debug__doc__[] =
|
||||||
|
"get_debug() -> flags\n"
|
||||||
|
"\n"
|
||||||
|
"Get the garbage collection debugging flags.\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Py_get_debug(self, args)
|
||||||
|
PyObject *self;
|
||||||
|
PyObject *args;
|
||||||
|
{
|
||||||
|
if(!PyArg_ParseTuple(args, "")) /* no args */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return Py_BuildValue("i", debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char set_thresh__doc__[] =
|
||||||
|
"set_threshold(threshold0, [threhold1, threshold2]) -> None\n"
|
||||||
|
"\n"
|
||||||
|
"Sets the collection thresholds. Setting threshold0 to zero disables\n"
|
||||||
|
"collection.\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Py_set_thresh(self, args)
|
||||||
|
PyObject *self;
|
||||||
|
PyObject *args;
|
||||||
|
{
|
||||||
|
if (!PyArg_ParseTuple(args, "i|ii", &threshold0,
|
||||||
|
&threshold1, &threshold2))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char get_thresh__doc__[] =
|
||||||
|
"get_threshold() -> (threshold0, threshold1, threshold2)\n"
|
||||||
|
"\n"
|
||||||
|
"Return the current collection thresholds\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
Py_get_thresh(self, args)
|
||||||
|
PyObject *self;
|
||||||
|
PyObject *args;
|
||||||
|
{
|
||||||
|
if(!PyArg_ParseTuple(args, "")) /* no args */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return Py_BuildValue("(iii)", threshold0, threshold1, threshold2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char gc__doc__ [] =
|
||||||
|
"This module provides access to the garbage collector for reference cycles.\n"
|
||||||
|
"\n"
|
||||||
|
"collect() -- Do a full collection right now.\n"
|
||||||
|
"set_debug() -- Set debugging flags.\n"
|
||||||
|
"get_debug() -- Get debugging flags.\n"
|
||||||
|
"set_threshold() -- Set the collection thresholds.\n"
|
||||||
|
"get_threshold() -- Return the current the collection thresholds.\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
static PyMethodDef GcMethods[] = {
|
||||||
|
{"set_debug", Py_set_debug, METH_VARARGS, set_debug__doc__},
|
||||||
|
{"get_debug", Py_get_debug, METH_VARARGS, get_debug__doc__},
|
||||||
|
{"set_threshold", Py_set_thresh, METH_VARARGS, set_thresh__doc__},
|
||||||
|
{"get_threshold", Py_get_thresh, METH_VARARGS, get_thresh__doc__},
|
||||||
|
{"collect", Py_collect, METH_VARARGS, collect__doc__},
|
||||||
|
{NULL, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
initgc(void)
|
||||||
|
{
|
||||||
|
PyObject *m;
|
||||||
|
PyObject *d;
|
||||||
|
|
||||||
|
m = Py_InitModule4("gc",
|
||||||
|
GcMethods,
|
||||||
|
gc__doc__,
|
||||||
|
NULL,
|
||||||
|
PYTHON_API_VERSION);
|
||||||
|
d = PyModule_GetDict(m);
|
||||||
|
if (garbage == NULL) {
|
||||||
|
garbage = PyList_New(0);
|
||||||
|
}
|
||||||
|
PyDict_SetItemString(d, "garbage", garbage);
|
||||||
|
PyDict_SetItemString(d, "DEBUG_STATS",
|
||||||
|
PyInt_FromLong(DEBUG_STATS));
|
||||||
|
PyDict_SetItemString(d, "DEBUG_COLLECTABLE",
|
||||||
|
PyInt_FromLong(DEBUG_COLLECTABLE));
|
||||||
|
PyDict_SetItemString(d, "DEBUG_UNCOLLECTABLE",
|
||||||
|
PyInt_FromLong(DEBUG_UNCOLLECTABLE));
|
||||||
|
PyDict_SetItemString(d, "DEBUG_INSTANCES",
|
||||||
|
PyInt_FromLong(DEBUG_INSTANCES));
|
||||||
|
PyDict_SetItemString(d, "DEBUG_OBJECTS",
|
||||||
|
PyInt_FromLong(DEBUG_OBJECTS));
|
||||||
|
PyDict_SetItemString(d, "DEBUG_LEAK",
|
||||||
|
PyInt_FromLong(DEBUG_LEAK));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WITH_CYCLE_GC */
|
|
@ -56,6 +56,7 @@ new_instance(unused, args)
|
||||||
Py_INCREF(dict);
|
Py_INCREF(dict);
|
||||||
inst->in_class = (PyClassObject *)klass;
|
inst->in_class = (PyClassObject *)klass;
|
||||||
inst->in_dict = dict;
|
inst->in_dict = dict;
|
||||||
|
PyObject_GC_Init(inst);
|
||||||
return (PyObject *)inst;
|
return (PyObject *)inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,7 @@ PyClass_New(bases, dict, name)
|
||||||
Py_XINCREF(op->cl_getattr);
|
Py_XINCREF(op->cl_getattr);
|
||||||
Py_XINCREF(op->cl_setattr);
|
Py_XINCREF(op->cl_setattr);
|
||||||
Py_XINCREF(op->cl_delattr);
|
Py_XINCREF(op->cl_delattr);
|
||||||
|
PyObject_GC_Init(op);
|
||||||
return (PyObject *) op;
|
return (PyObject *) op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +142,7 @@ static void
|
||||||
class_dealloc(op)
|
class_dealloc(op)
|
||||||
PyClassObject *op;
|
PyClassObject *op;
|
||||||
{
|
{
|
||||||
|
PyObject_GC_Fini(op);
|
||||||
Py_DECREF(op->cl_bases);
|
Py_DECREF(op->cl_bases);
|
||||||
Py_DECREF(op->cl_dict);
|
Py_DECREF(op->cl_dict);
|
||||||
Py_XDECREF(op->cl_name);
|
Py_XDECREF(op->cl_name);
|
||||||
|
@ -428,7 +430,7 @@ PyTypeObject PyClass_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"class",
|
"class",
|
||||||
sizeof(PyClassObject) + PyGC_INFO_SIZE,
|
sizeof(PyClassObject) + PyGC_HEAD_SIZE,
|
||||||
0,
|
0,
|
||||||
(destructor)class_dealloc, /*tp_dealloc*/
|
(destructor)class_dealloc, /*tp_dealloc*/
|
||||||
0, /*tp_print*/
|
0, /*tp_print*/
|
||||||
|
@ -490,6 +492,7 @@ PyInstance_New(class, arg, kw)
|
||||||
if (inst == NULL)
|
if (inst == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
inst->in_dict = PyDict_New();
|
inst->in_dict = PyDict_New();
|
||||||
|
PyObject_GC_Init(inst);
|
||||||
if (inst->in_dict == NULL) {
|
if (inst->in_dict == NULL) {
|
||||||
PyObject_DEL(inst);
|
PyObject_DEL(inst);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -539,11 +542,12 @@ instance_dealloc(inst)
|
||||||
PyObject *error_type, *error_value, *error_traceback;
|
PyObject *error_type, *error_value, *error_traceback;
|
||||||
PyObject *del;
|
PyObject *del;
|
||||||
static PyObject *delstr;
|
static PyObject *delstr;
|
||||||
|
extern long _Py_RefTotal;
|
||||||
|
PyObject_GC_Fini(inst);
|
||||||
/* Call the __del__ method if it exists. First temporarily
|
/* Call the __del__ method if it exists. First temporarily
|
||||||
revive the object and save the current exception, if any. */
|
revive the object and save the current exception, if any. */
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
/* much too complicated if Py_TRACE_REFS defined */
|
/* much too complicated if Py_TRACE_REFS defined */
|
||||||
extern long _Py_RefTotal;
|
|
||||||
inst->ob_type = &PyInstance_Type;
|
inst->ob_type = &PyInstance_Type;
|
||||||
_Py_NewReference((PyObject *)inst);
|
_Py_NewReference((PyObject *)inst);
|
||||||
_Py_RefTotal--; /* compensate for increment in NEWREF */
|
_Py_RefTotal--; /* compensate for increment in NEWREF */
|
||||||
|
@ -591,6 +595,7 @@ instance_dealloc(inst)
|
||||||
#ifdef COUNT_ALLOCS
|
#ifdef COUNT_ALLOCS
|
||||||
inst->ob_type->tp_free--;
|
inst->ob_type->tp_free--;
|
||||||
#endif
|
#endif
|
||||||
|
PyObject_GC_Init((PyObject *)inst);
|
||||||
return; /* __del__ added a reference; don't delete now */
|
return; /* __del__ added a reference; don't delete now */
|
||||||
}
|
}
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
|
@ -598,7 +603,9 @@ instance_dealloc(inst)
|
||||||
inst->ob_type->tp_free--; /* compensate for increment in UNREF */
|
inst->ob_type->tp_free--; /* compensate for increment in UNREF */
|
||||||
#endif
|
#endif
|
||||||
_Py_ForgetReference((PyObject *)inst);
|
_Py_ForgetReference((PyObject *)inst);
|
||||||
|
#ifndef WITH_CYCLE_GC
|
||||||
inst->ob_type = NULL;
|
inst->ob_type = NULL;
|
||||||
|
#endif
|
||||||
#endif /* Py_TRACE_REFS */
|
#endif /* Py_TRACE_REFS */
|
||||||
Py_DECREF(inst->in_class);
|
Py_DECREF(inst->in_class);
|
||||||
Py_XDECREF(inst->in_dict);
|
Py_XDECREF(inst->in_dict);
|
||||||
|
@ -1510,7 +1517,7 @@ PyTypeObject PyInstance_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"instance",
|
"instance",
|
||||||
sizeof(PyInstanceObject) + PyGC_INFO_SIZE,
|
sizeof(PyInstanceObject) + PyGC_HEAD_SIZE,
|
||||||
0,
|
0,
|
||||||
(destructor)instance_dealloc, /*tp_dealloc*/
|
(destructor)instance_dealloc, /*tp_dealloc*/
|
||||||
0, /*tp_print*/
|
0, /*tp_print*/
|
||||||
|
@ -1568,6 +1575,7 @@ PyMethod_New(func, self, class)
|
||||||
im->im_self = self;
|
im->im_self = self;
|
||||||
Py_INCREF(class);
|
Py_INCREF(class);
|
||||||
im->im_class = class;
|
im->im_class = class;
|
||||||
|
PyObject_GC_Init(im);
|
||||||
return (PyObject *)im;
|
return (PyObject *)im;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1643,6 +1651,7 @@ static void
|
||||||
instancemethod_dealloc(im)
|
instancemethod_dealloc(im)
|
||||||
register PyMethodObject *im;
|
register PyMethodObject *im;
|
||||||
{
|
{
|
||||||
|
PyObject_GC_Fini(im);
|
||||||
Py_DECREF(im->im_func);
|
Py_DECREF(im->im_func);
|
||||||
Py_XDECREF(im->im_self);
|
Py_XDECREF(im->im_self);
|
||||||
Py_DECREF(im->im_class);
|
Py_DECREF(im->im_class);
|
||||||
|
@ -1745,7 +1754,7 @@ PyTypeObject PyMethod_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"instance method",
|
"instance method",
|
||||||
sizeof(PyMethodObject) + PyGC_INFO_SIZE,
|
sizeof(PyMethodObject) + PyGC_HEAD_SIZE,
|
||||||
0,
|
0,
|
||||||
(destructor)instancemethod_dealloc, /*tp_dealloc*/
|
(destructor)instancemethod_dealloc, /*tp_dealloc*/
|
||||||
0, /*tp_print*/
|
0, /*tp_print*/
|
||||||
|
|
|
@ -129,6 +129,7 @@ PyDict_New()
|
||||||
mp->ma_table = NULL;
|
mp->ma_table = NULL;
|
||||||
mp->ma_fill = 0;
|
mp->ma_fill = 0;
|
||||||
mp->ma_used = 0;
|
mp->ma_used = 0;
|
||||||
|
PyObject_GC_Init(mp);
|
||||||
return (PyObject *)mp;
|
return (PyObject *)mp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,6 +482,7 @@ dict_dealloc(mp)
|
||||||
register int i;
|
register int i;
|
||||||
register dictentry *ep;
|
register dictentry *ep;
|
||||||
Py_TRASHCAN_SAFE_BEGIN(mp)
|
Py_TRASHCAN_SAFE_BEGIN(mp)
|
||||||
|
PyObject_GC_Fini(mp);
|
||||||
for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
|
for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
|
||||||
if (ep->me_key != NULL) {
|
if (ep->me_key != NULL) {
|
||||||
Py_DECREF(ep->me_key);
|
Py_DECREF(ep->me_key);
|
||||||
|
@ -1087,7 +1089,7 @@ PyTypeObject PyDict_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"dictionary",
|
"dictionary",
|
||||||
sizeof(dictobject) + PyGC_INFO_SIZE,
|
sizeof(dictobject) + PyGC_HEAD_SIZE,
|
||||||
0,
|
0,
|
||||||
(destructor)dict_dealloc, /*tp_dealloc*/
|
(destructor)dict_dealloc, /*tp_dealloc*/
|
||||||
(printfunc)dict_print, /*tp_print*/
|
(printfunc)dict_print, /*tp_print*/
|
||||||
|
|
|
@ -63,6 +63,7 @@ PyFunction_New(code, globals)
|
||||||
Py_INCREF(doc);
|
Py_INCREF(doc);
|
||||||
op->func_doc = doc;
|
op->func_doc = doc;
|
||||||
}
|
}
|
||||||
|
PyObject_GC_Init(op);
|
||||||
return (PyObject *)op;
|
return (PyObject *)op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +187,7 @@ static void
|
||||||
func_dealloc(op)
|
func_dealloc(op)
|
||||||
PyFunctionObject *op;
|
PyFunctionObject *op;
|
||||||
{
|
{
|
||||||
|
PyObject_GC_Fini(op);
|
||||||
Py_DECREF(op->func_code);
|
Py_DECREF(op->func_code);
|
||||||
Py_DECREF(op->func_globals);
|
Py_DECREF(op->func_globals);
|
||||||
Py_DECREF(op->func_name);
|
Py_DECREF(op->func_name);
|
||||||
|
@ -277,7 +279,7 @@ PyTypeObject PyFunction_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"function",
|
"function",
|
||||||
sizeof(PyFunctionObject) + PyGC_INFO_SIZE,
|
sizeof(PyFunctionObject) + PyGC_HEAD_SIZE,
|
||||||
0,
|
0,
|
||||||
(destructor)func_dealloc, /*tp_dealloc*/
|
(destructor)func_dealloc, /*tp_dealloc*/
|
||||||
0, /*tp_print*/
|
0, /*tp_print*/
|
||||||
|
|
|
@ -72,10 +72,11 @@ PyList_New(size)
|
||||||
}
|
}
|
||||||
/* PyObject_NewVar is inlined */
|
/* PyObject_NewVar is inlined */
|
||||||
op = (PyListObject *) PyObject_MALLOC(sizeof(PyListObject)
|
op = (PyListObject *) PyObject_MALLOC(sizeof(PyListObject)
|
||||||
+ PyGC_INFO_SIZE);
|
+ PyGC_HEAD_SIZE);
|
||||||
if (op == NULL) {
|
if (op == NULL) {
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
}
|
}
|
||||||
|
op = (PyListObject *) PyObject_FROM_GC(op);
|
||||||
if (size <= 0) {
|
if (size <= 0) {
|
||||||
op->ob_item = NULL;
|
op->ob_item = NULL;
|
||||||
}
|
}
|
||||||
|
@ -89,6 +90,7 @@ PyList_New(size)
|
||||||
PyObject_INIT_VAR(op, &PyList_Type, size);
|
PyObject_INIT_VAR(op, &PyList_Type, size);
|
||||||
for (i = 0; i < size; i++)
|
for (i = 0; i < size; i++)
|
||||||
op->ob_item[i] = NULL;
|
op->ob_item[i] = NULL;
|
||||||
|
PyObject_GC_Init(op);
|
||||||
return (PyObject *) op;
|
return (PyObject *) op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +218,7 @@ list_dealloc(op)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
Py_TRASHCAN_SAFE_BEGIN(op)
|
Py_TRASHCAN_SAFE_BEGIN(op)
|
||||||
|
PyObject_GC_Fini(op);
|
||||||
if (op->ob_item != NULL) {
|
if (op->ob_item != NULL) {
|
||||||
/* Do it backwards, for Christian Tismer.
|
/* Do it backwards, for Christian Tismer.
|
||||||
There's a simple test case where somehow this reduces
|
There's a simple test case where somehow this reduces
|
||||||
|
@ -1498,7 +1501,7 @@ PyTypeObject PyList_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"list",
|
"list",
|
||||||
sizeof(PyListObject) + PyGC_INFO_SIZE,
|
sizeof(PyListObject) + PyGC_HEAD_SIZE,
|
||||||
0,
|
0,
|
||||||
(destructor)list_dealloc, /*tp_dealloc*/
|
(destructor)list_dealloc, /*tp_dealloc*/
|
||||||
(printfunc)list_print, /*tp_print*/
|
(printfunc)list_print, /*tp_print*/
|
||||||
|
@ -1577,7 +1580,7 @@ static PyTypeObject immutable_list_type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"list (immutable, during sort)",
|
"list (immutable, during sort)",
|
||||||
sizeof(PyListObject) + PyGC_INFO_SIZE,
|
sizeof(PyListObject) + PyGC_HEAD_SIZE,
|
||||||
0,
|
0,
|
||||||
0, /*tp_dealloc*/ /* Cannot happen */
|
0, /*tp_dealloc*/ /* Cannot happen */
|
||||||
(printfunc)list_print, /*tp_print*/
|
(printfunc)list_print, /*tp_print*/
|
||||||
|
|
|
@ -124,6 +124,10 @@ PyObject_Init(op, tp)
|
||||||
"NULL object passed to PyObject_Init");
|
"NULL object passed to PyObject_Init");
|
||||||
return op;
|
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) */
|
/* Any changes should be reflected in PyObject_INIT (objimpl.h) */
|
||||||
op->ob_type = tp;
|
op->ob_type = tp;
|
||||||
_Py_NewReference(op);
|
_Py_NewReference(op);
|
||||||
|
@ -141,6 +145,10 @@ PyObject_InitVar(op, tp, size)
|
||||||
"NULL object passed to PyObject_InitVar");
|
"NULL object passed to PyObject_InitVar");
|
||||||
return op;
|
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 */
|
/* Any changes should be reflected in PyObject_INIT_VAR */
|
||||||
op->ob_size = size;
|
op->ob_size = size;
|
||||||
op->ob_type = tp;
|
op->ob_type = tp;
|
||||||
|
@ -156,6 +164,10 @@ _PyObject_New(tp)
|
||||||
op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp));
|
op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp));
|
||||||
if (op == NULL)
|
if (op == NULL)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
#ifdef WITH_CYCLE_GC
|
||||||
|
if (PyType_IS_GC(tp))
|
||||||
|
op = (PyObject *) PyObject_FROM_GC(op);
|
||||||
|
#endif
|
||||||
return PyObject_INIT(op, tp);
|
return PyObject_INIT(op, tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +180,10 @@ _PyObject_NewVar(tp, size)
|
||||||
op = (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size));
|
op = (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size));
|
||||||
if (op == NULL)
|
if (op == NULL)
|
||||||
return (PyVarObject *)PyErr_NoMemory();
|
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);
|
return PyObject_INIT_VAR(op, tp, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,9 +191,23 @@ void
|
||||||
_PyObject_Del(op)
|
_PyObject_Del(op)
|
||||||
PyObject *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
|
int
|
||||||
PyObject_Print(op, fp, flags)
|
PyObject_Print(op, fp, flags)
|
||||||
PyObject *op;
|
PyObject *op;
|
||||||
|
@ -917,8 +947,10 @@ _Py_Dealloc(op)
|
||||||
{
|
{
|
||||||
destructor dealloc = op->ob_type->tp_dealloc;
|
destructor dealloc = op->ob_type->tp_dealloc;
|
||||||
_Py_ForgetReference(op);
|
_Py_ForgetReference(op);
|
||||||
|
#ifndef WITH_CYCLE_GC
|
||||||
if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL-1)
|
if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL-1)
|
||||||
op->ob_type = NULL;
|
op->ob_type = NULL;
|
||||||
|
#endif
|
||||||
(*dealloc)(op);
|
(*dealloc)(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ PyTuple_New(size)
|
||||||
/* Check for overflow */
|
/* Check for overflow */
|
||||||
if (nbytes / sizeof(PyObject *) != (size_t)size ||
|
if (nbytes / sizeof(PyObject *) != (size_t)size ||
|
||||||
(nbytes += sizeof(PyTupleObject) - sizeof(PyObject *)
|
(nbytes += sizeof(PyTupleObject) - sizeof(PyObject *)
|
||||||
+ PyGC_INFO_SIZE)
|
+ PyGC_HEAD_SIZE)
|
||||||
<= 0)
|
<= 0)
|
||||||
{
|
{
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
@ -103,7 +103,7 @@ PyTuple_New(size)
|
||||||
op = (PyTupleObject *) PyObject_MALLOC(nbytes);
|
op = (PyTupleObject *) PyObject_MALLOC(nbytes);
|
||||||
if (op == NULL)
|
if (op == NULL)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
op = (PyTupleObject *) PyObject_FROM_GC(op);
|
||||||
PyObject_INIT_VAR(op, &PyTuple_Type, size);
|
PyObject_INIT_VAR(op, &PyTuple_Type, size);
|
||||||
}
|
}
|
||||||
for (i = 0; i < size; i++)
|
for (i = 0; i < size; i++)
|
||||||
|
@ -115,6 +115,7 @@ PyTuple_New(size)
|
||||||
Py_INCREF(op); /* extra INCREF so that this is never freed */
|
Py_INCREF(op); /* extra INCREF so that this is never freed */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
PyObject_GC_Init(op);
|
||||||
return (PyObject *) op;
|
return (PyObject *) op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +182,7 @@ tupledealloc(op)
|
||||||
register int i;
|
register int i;
|
||||||
register int len = op->ob_size;
|
register int len = op->ob_size;
|
||||||
Py_TRASHCAN_SAFE_BEGIN(op)
|
Py_TRASHCAN_SAFE_BEGIN(op)
|
||||||
|
PyObject_GC_Fini(op);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
i = len;
|
i = len;
|
||||||
while (--i >= 0)
|
while (--i >= 0)
|
||||||
|
@ -453,7 +455,7 @@ PyTypeObject PyTuple_Type = {
|
||||||
PyObject_HEAD_INIT(&PyType_Type)
|
PyObject_HEAD_INIT(&PyType_Type)
|
||||||
0,
|
0,
|
||||||
"tuple",
|
"tuple",
|
||||||
sizeof(PyTupleObject) - sizeof(PyObject *) + PyGC_INFO_SIZE,
|
sizeof(PyTupleObject) - sizeof(PyObject *) + PyGC_HEAD_SIZE,
|
||||||
sizeof(PyObject *),
|
sizeof(PyObject *),
|
||||||
(destructor)tupledealloc, /*tp_dealloc*/
|
(destructor)tupledealloc, /*tp_dealloc*/
|
||||||
(printfunc)tupleprint, /*tp_print*/
|
(printfunc)tupleprint, /*tp_print*/
|
||||||
|
@ -557,12 +559,27 @@ _PyTuple_Resize(pv, newsize, last_is_sticky)
|
||||||
} else
|
} else
|
||||||
#endif
|
#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 *)
|
sv = (PyTupleObject *)
|
||||||
PyObject_REALLOC((char *)v, sizeof(PyTupleObject)
|
PyObject_REALLOC((char *)v, sizeof(PyTupleObject)
|
||||||
+ PyGC_INFO_SIZE
|
+ PyGC_HEAD_SIZE
|
||||||
+ newsize * sizeof(PyObject *));
|
+ newsize * sizeof(PyObject *));
|
||||||
|
#endif
|
||||||
*pv = (PyObject *) sv;
|
*pv = (PyObject *) sv;
|
||||||
if (sv == NULL) {
|
if (sv == NULL) {
|
||||||
|
PyObject_GC_Init((PyObject *)v);
|
||||||
PyObject_DEL(v);
|
PyObject_DEL(v);
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -578,6 +595,7 @@ _PyTuple_Resize(pv, newsize, last_is_sticky)
|
||||||
sv->ob_item[i - sizediff] = NULL;
|
sv->ob_item[i - sizediff] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PyObject_GC_Init(sv);
|
||||||
sv->ob_size = newsize;
|
sv->ob_size = newsize;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,9 @@ extern void initbinascii();
|
||||||
#endif
|
#endif
|
||||||
extern void initcmath();
|
extern void initcmath();
|
||||||
extern void initerrno();
|
extern void initerrno();
|
||||||
|
#ifdef WITH_CYCLE_GC
|
||||||
|
extern void initgc();
|
||||||
|
#endif
|
||||||
#ifndef MS_WIN64
|
#ifndef MS_WIN64
|
||||||
extern void initimageop();
|
extern void initimageop();
|
||||||
#endif
|
#endif
|
||||||
|
@ -89,6 +92,9 @@ struct _inittab _PyImport_Inittab[] = {
|
||||||
#endif
|
#endif
|
||||||
{"cmath", initcmath},
|
{"cmath", initcmath},
|
||||||
{"errno", initerrno},
|
{"errno", initerrno},
|
||||||
|
#ifdef WITH_CYCLE_GC
|
||||||
|
{"gc", initgc},
|
||||||
|
#endif
|
||||||
#ifndef MS_WIN64
|
#ifndef MS_WIN64
|
||||||
{"imageop", initimageop},
|
{"imageop", initimageop},
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -451,6 +451,9 @@ typedef long intptr_t;
|
||||||
/* Define if you want to use the GNU readline library */
|
/* Define if you want to use the GNU readline library */
|
||||||
/* #define WITH_READLINE 1 */
|
/* #define WITH_READLINE 1 */
|
||||||
|
|
||||||
|
/* Define if you want cycle garbage collection */
|
||||||
|
/* #define WITH_CYCLE_GC 1 */
|
||||||
|
|
||||||
/* Define if you have clock. */
|
/* Define if you have clock. */
|
||||||
/* #define HAVE_CLOCK */
|
/* #define HAVE_CLOCK */
|
||||||
|
|
||||||
|
|
|
@ -657,6 +657,20 @@ SOURCE=..\Objects\funcobject.c
|
||||||
|
|
||||||
!ENDIF
|
!ENDIF
|
||||||
|
|
||||||
|
# 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
|
# End Source File
|
||||||
# Begin Source File
|
# Begin Source File
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue