Commit 43411b56 authored by Neil Schemenauer's avatar Neil Schemenauer

Make more things internal to this file. Remove

visit_finalizer_reachable since it's the same as visit_reachable.
Rename visit_reachable to visit_move.  Objects can now have the GC type
flag set, reachable by tp_traverse and not be in a GC linked list.  This
should make the collector more robust and easier to use by extension
module writers.  Add memory management functions for container objects
(new, del, resize).
parent cf22946c
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
Based on a post on the python-dev list. Ideas from Guido van Rossum, Based on a post on the python-dev list. Ideas from Guido van Rossum,
Eric Tiedemann, and various others. Eric Tiedemann, and various others.
http://www.arctrix.com/nas/python/gc.html http://www.arctrix.com/nas/python/gc/
http://www.python.org/pipermail/python-dev/2000-March/003869.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/004010.html
http://www.python.org/pipermail/python-dev/2000-March/004022.html http://www.python.org/pipermail/python-dev/2000-March/004022.html
...@@ -18,18 +18,21 @@ ...@@ -18,18 +18,21 @@
*/ */
#include "Python.h" #include "Python.h"
#ifdef WITH_CYCLE_GC #ifdef WITH_CYCLE_GC
/* magic gc_refs value */ /* Get an object's GC head */
#define GC_MOVED -1 #define AS_GC(o) ((PyGC_Head *)(o)-1)
/* Get the object given the GC head */
#define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
/*** Global GC state ***/ /*** Global GC state ***/
/* linked lists of container objects */ /* linked lists of container objects */
static PyGC_Head generation0 = {&generation0, &generation0, 0}; PyGC_Head _PyGC_generation0 = {&_PyGC_generation0, &_PyGC_generation0, 0};
static PyGC_Head generation1 = {&generation1, &generation1, 0}; static PyGC_Head generation1 = {&generation1, &generation1, 0};
static PyGC_Head generation2 = {&generation2, &generation2, 0}; static PyGC_Head generation2 = {&generation2, &generation2, 0};
static int generation = 0; /* current generation being collected */ static int generation = 0; /* current generation being collected */
...@@ -43,6 +46,9 @@ static int threshold2 = 10; /* generation1 collections before collecting 2 */ ...@@ -43,6 +46,9 @@ static int threshold2 = 10; /* generation1 collections before collecting 2 */
/* net new objects allocated since last collection */ /* net new objects allocated since last collection */
static int allocated; static int allocated;
/* true if we are currently running the collector */
static int collecting;
/* set for debugging information */ /* set for debugging information */
#define DEBUG_STATS (1<<0) /* print collection statistics */ #define DEBUG_STATS (1<<0) /* print collection statistics */
#define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */ #define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */
...@@ -57,6 +63,9 @@ static int allocated; ...@@ -57,6 +63,9 @@ static int allocated;
DEBUG_SAVEALL DEBUG_SAVEALL
static int debug; static int debug;
/* Special gc_refs value */
#define GC_MOVED -123
/* list of uncollectable objects */ /* list of uncollectable objects */
static PyObject *garbage; static PyObject *garbage;
...@@ -86,10 +95,7 @@ gc_list_remove(PyGC_Head *node) ...@@ -86,10 +95,7 @@ gc_list_remove(PyGC_Head *node)
{ {
node->gc_prev->gc_next = node->gc_next; node->gc_prev->gc_next = node->gc_next;
node->gc_next->gc_prev = node->gc_prev; node->gc_next->gc_prev = node->gc_prev;
#ifdef Py_DEBUG node->gc_next = NULL; /* object is not currently tracked */
node->gc_prev = NULL;
node->gc_next = NULL;
#endif
} }
static void static void
...@@ -137,13 +143,14 @@ gc_list_size(PyGC_Head *list) ...@@ -137,13 +143,14 @@ gc_list_size(PyGC_Head *list)
/*** end of list stuff ***/ /*** end of list stuff ***/
/* Set all gc_refs = ob_refcnt */ /* Set all gc_refs = ob_refcnt */
static void static void
update_refs(PyGC_Head *containers) update_refs(PyGC_Head *containers)
{ {
PyGC_Head *gc = containers->gc_next; PyGC_Head *gc = containers->gc_next;
for (; gc != containers; gc=gc->gc_next) { for (; gc != containers; gc=gc->gc_next) {
gc->gc_refs = PyObject_FROM_GC(gc)->ob_refcnt; gc->gc_refs = FROM_GC(gc)->ob_refcnt;
} }
} }
...@@ -151,7 +158,9 @@ static int ...@@ -151,7 +158,9 @@ static int
visit_decref(PyObject *op, void *data) visit_decref(PyObject *op, void *data)
{ {
if (op && PyObject_IS_GC(op)) { if (op && PyObject_IS_GC(op)) {
PyObject_AS_GC(op)->gc_refs--; PyGC_Head *gc = AS_GC(op);
if (gc->gc_next != NULL)
AS_GC(op)->gc_refs--;
} }
return 0; return 0;
} }
...@@ -163,8 +172,8 @@ subtract_refs(PyGC_Head *containers) ...@@ -163,8 +172,8 @@ subtract_refs(PyGC_Head *containers)
traverseproc traverse; traverseproc traverse;
PyGC_Head *gc = containers->gc_next; PyGC_Head *gc = containers->gc_next;
for (; gc != containers; gc=gc->gc_next) { for (; gc != containers; gc=gc->gc_next) {
traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse; traverse = FROM_GC(gc)->ob_type->tp_traverse;
(void) traverse(PyObject_FROM_GC(gc), (void) traverse(FROM_GC(gc),
(visitproc)visit_decref, (visitproc)visit_decref,
NULL); NULL);
} }
...@@ -188,13 +197,13 @@ move_roots(PyGC_Head *containers, PyGC_Head *roots) ...@@ -188,13 +197,13 @@ move_roots(PyGC_Head *containers, PyGC_Head *roots)
} }
static int static int
visit_reachable(PyObject *op, PyGC_Head *roots) visit_move(PyObject *op, PyGC_Head *tolist)
{ {
if (PyObject_IS_GC(op)) { if (PyObject_IS_GC(op)) {
PyGC_Head *gc = PyObject_AS_GC(op); PyGC_Head *gc = AS_GC(op);
if (gc && gc->gc_refs != GC_MOVED) { if (gc->gc_next != NULL && gc->gc_refs != GC_MOVED) {
gc_list_remove(gc); gc_list_remove(gc);
gc_list_append(gc, roots); gc_list_append(gc, tolist);
gc->gc_refs = GC_MOVED; gc->gc_refs = GC_MOVED;
} }
} }
...@@ -209,15 +218,15 @@ move_root_reachable(PyGC_Head *reachable) ...@@ -209,15 +218,15 @@ move_root_reachable(PyGC_Head *reachable)
PyGC_Head *gc = reachable->gc_next; PyGC_Head *gc = reachable->gc_next;
for (; gc != reachable; gc=gc->gc_next) { for (; gc != reachable; gc=gc->gc_next) {
/* careful, reachable list is growing here */ /* careful, reachable list is growing here */
PyObject *op = PyObject_FROM_GC(gc); PyObject *op = FROM_GC(gc);
traverse = op->ob_type->tp_traverse; traverse = op->ob_type->tp_traverse;
(void) traverse(op, (void) traverse(op,
(visitproc)visit_reachable, (visitproc)visit_move,
(void *)reachable); (void *)reachable);
} }
} }
/* move all objects with finalizers (instances with __del__) */ /* Move all objects with finalizers (instances with __del__) */
static void static void
move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
{ {
...@@ -230,7 +239,7 @@ move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) ...@@ -230,7 +239,7 @@ move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
Py_FatalError("PyGC: can't initialize __del__ string"); Py_FatalError("PyGC: can't initialize __del__ string");
} }
for (; gc != unreachable; gc=next) { for (; gc != unreachable; gc=next) {
PyObject *op = PyObject_FROM_GC(gc); PyObject *op = FROM_GC(gc);
next = gc->gc_next; next = gc->gc_next;
if (PyInstance_Check(op) && PyObject_HasAttr(op, delstr)) { if (PyInstance_Check(op) && PyObject_HasAttr(op, delstr)) {
gc_list_remove(gc); gc_list_remove(gc);
...@@ -239,22 +248,6 @@ move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) ...@@ -239,22 +248,6 @@ move_finalizers(PyGC_Head *unreachable, PyGC_Head *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 */ /* Move objects referenced from roots to roots */
static void static void
move_finalizer_reachable(PyGC_Head *finalizers) move_finalizer_reachable(PyGC_Head *finalizers)
...@@ -263,9 +256,9 @@ move_finalizer_reachable(PyGC_Head *finalizers) ...@@ -263,9 +256,9 @@ move_finalizer_reachable(PyGC_Head *finalizers)
PyGC_Head *gc = finalizers->gc_next; PyGC_Head *gc = finalizers->gc_next;
for (; gc != finalizers; gc=gc->gc_next) { for (; gc != finalizers; gc=gc->gc_next) {
/* careful, finalizers list is growing here */ /* careful, finalizers list is growing here */
traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse; traverse = FROM_GC(gc)->ob_type->tp_traverse;
(void) traverse(PyObject_FROM_GC(gc), (void) traverse(FROM_GC(gc),
(visitproc)visit_finalizer_reachable, (visitproc)visit_move,
(void *)finalizers); (void *)finalizers);
} }
} }
...@@ -306,7 +299,7 @@ handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old) ...@@ -306,7 +299,7 @@ handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
} }
for (gc = finalizers->gc_next; gc != finalizers; for (gc = finalizers->gc_next; gc != finalizers;
gc = finalizers->gc_next) { gc = finalizers->gc_next) {
PyObject *op = PyObject_FROM_GC(gc); PyObject *op = FROM_GC(gc);
if ((debug & DEBUG_SAVEALL) || PyInstance_Check(op)) { if ((debug & DEBUG_SAVEALL) || PyInstance_Check(op)) {
/* If SAVEALL is not set then just append /* If SAVEALL is not set then just append
* instances to the list of garbage. We assume * instances to the list of garbage. We assume
...@@ -330,7 +323,7 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old) ...@@ -330,7 +323,7 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
while (unreachable->gc_next != unreachable) { while (unreachable->gc_next != unreachable) {
PyGC_Head *gc = unreachable->gc_next; PyGC_Head *gc = unreachable->gc_next;
PyObject *op = PyObject_FROM_GC(gc); PyObject *op = FROM_GC(gc);
if (debug & DEBUG_SAVEALL) { if (debug & DEBUG_SAVEALL) {
PyList_Append(garbage, op); PyList_Append(garbage, op);
} }
...@@ -366,7 +359,7 @@ collect(PyGC_Head *young, PyGC_Head *old) ...@@ -366,7 +359,7 @@ collect(PyGC_Head *young, PyGC_Head *old)
"gc: collecting generation %d...\n" "gc: collecting generation %d...\n"
"gc: objects in each generation: %ld %ld %ld\n", "gc: objects in each generation: %ld %ld %ld\n",
generation, generation,
gc_list_size(&generation0), gc_list_size(&_PyGC_generation0),
gc_list_size(&generation1), gc_list_size(&generation1),
gc_list_size(&generation2)); gc_list_size(&generation2));
} }
...@@ -407,7 +400,7 @@ collect(PyGC_Head *young, PyGC_Head *old) ...@@ -407,7 +400,7 @@ collect(PyGC_Head *young, PyGC_Head *old)
gc = gc->gc_next) { gc = gc->gc_next) {
m++; m++;
if (debug & DEBUG_COLLECTABLE) { if (debug & DEBUG_COLLECTABLE) {
debug_cycle("collectable", PyObject_FROM_GC(gc)); debug_cycle("collectable", FROM_GC(gc));
} }
} }
/* call tp_clear on objects in the collectable set. This will cause /* call tp_clear on objects in the collectable set. This will cause
...@@ -421,7 +414,7 @@ collect(PyGC_Head *young, PyGC_Head *old) ...@@ -421,7 +414,7 @@ collect(PyGC_Head *young, PyGC_Head *old)
gc = gc->gc_next) { gc = gc->gc_next) {
n++; n++;
if (debug & DEBUG_UNCOLLECTABLE) { if (debug & DEBUG_UNCOLLECTABLE) {
debug_cycle("uncollectable", PyObject_FROM_GC(gc)); debug_cycle("uncollectable", FROM_GC(gc));
} }
} }
if (debug & DEBUG_STATS) { if (debug & DEBUG_STATS) {
...@@ -461,7 +454,7 @@ collect_generations(void) ...@@ -461,7 +454,7 @@ collect_generations(void)
if (collections1 > threshold2) { if (collections1 > threshold2) {
generation = 2; generation = 2;
gc_list_merge(&generation0, &generation2); gc_list_merge(&_PyGC_generation0, &generation2);
gc_list_merge(&generation1, &generation2); gc_list_merge(&generation1, &generation2);
if (generation2.gc_next != &generation2) { if (generation2.gc_next != &generation2) {
n = collect(&generation2, &generation2); n = collect(&generation2, &generation2);
...@@ -471,7 +464,7 @@ collect_generations(void) ...@@ -471,7 +464,7 @@ collect_generations(void)
else if (collections0 > threshold1) { else if (collections0 > threshold1) {
generation = 1; generation = 1;
collections1++; collections1++;
gc_list_merge(&generation0, &generation1); gc_list_merge(&_PyGC_generation0, &generation1);
if (generation1.gc_next != &generation1) { if (generation1.gc_next != &generation1) {
n = collect(&generation1, &generation2); n = collect(&generation1, &generation2);
} }
...@@ -480,52 +473,13 @@ collect_generations(void) ...@@ -480,52 +473,13 @@ collect_generations(void)
else { else {
generation = 0; generation = 0;
collections0++; collections0++;
if (generation0.gc_next != &generation0) { if (_PyGC_generation0.gc_next != &_PyGC_generation0) {
n = collect(&generation0, &generation1); n = collect(&_PyGC_generation0, &generation1);
} }
} }
return n; 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 (allocated > threshold0 &&
enabled &&
threshold0 &&
!collecting &&
!PyErr_Occurred()) {
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 gc_enable__doc__[] = static char gc_enable__doc__[] =
"enable() -> None\n" "enable() -> None\n"
"\n" "\n"
...@@ -595,7 +549,7 @@ gc_collect(PyObject *self, PyObject *args) ...@@ -595,7 +549,7 @@ gc_collect(PyObject *self, PyObject *args)
return NULL; return NULL;
generation = 2; generation = 2;
gc_list_merge(&generation0, &generation2); gc_list_merge(&_PyGC_generation0, &generation2);
gc_list_merge(&generation1, &generation2); gc_list_merge(&generation1, &generation2);
n = collect(&generation2, &generation2); n = collect(&generation2, &generation2);
...@@ -693,7 +647,7 @@ gc_referents_for(PyObject *objs, PyGC_Head *list, PyObject *resultlist) ...@@ -693,7 +647,7 @@ gc_referents_for(PyObject *objs, PyGC_Head *list, PyObject *resultlist)
PyObject *obj; PyObject *obj;
traverseproc traverse; traverseproc traverse;
for (gc = list->gc_next; gc != list; gc = gc->gc_next) { for (gc = list->gc_next; gc != list; gc = gc->gc_next) {
obj = PyObject_FROM_GC(gc); obj = FROM_GC(gc);
traverse = obj->ob_type->tp_traverse; traverse = obj->ob_type->tp_traverse;
if (obj == objs || obj == resultlist) if (obj == objs || obj == resultlist)
continue; continue;
...@@ -713,7 +667,7 @@ static PyObject * ...@@ -713,7 +667,7 @@ static PyObject *
gc_get_referents(PyObject *self, PyObject *args) gc_get_referents(PyObject *self, PyObject *args)
{ {
PyObject *result = PyList_New(0); PyObject *result = PyList_New(0);
if (!(gc_referents_for(args, &generation0, result) && if (!(gc_referents_for(args, &_PyGC_generation0, result) &&
gc_referents_for(args, &generation1, result) && gc_referents_for(args, &generation1, result) &&
gc_referents_for(args, &generation2, result))) { gc_referents_for(args, &generation2, result))) {
Py_DECREF(result); Py_DECREF(result);
...@@ -735,7 +689,7 @@ append_objects(PyObject *py_list, PyGC_Head *gc_list) ...@@ -735,7 +689,7 @@ append_objects(PyObject *py_list, PyGC_Head *gc_list)
{ {
PyGC_Head *gc; PyGC_Head *gc;
for (gc = gc_list->gc_next; gc != gc_list; gc = gc->gc_next) { for (gc = gc_list->gc_next; gc != gc_list; gc = gc->gc_next) {
PyObject *op = PyObject_FROM_GC(gc); PyObject *op = FROM_GC(gc);
if (op != py_list) { if (op != py_list) {
Py_INCREF(op); Py_INCREF(op);
PyList_Append(py_list, op); PyList_Append(py_list, op);
...@@ -751,7 +705,7 @@ gc_get_objects(PyObject *self, PyObject *args) ...@@ -751,7 +705,7 @@ gc_get_objects(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, ":get_objects")) /* check no args */ if (!PyArg_ParseTuple(args, ":get_objects")) /* check no args */
return NULL; return NULL;
result = PyList_New(0); result = PyList_New(0);
append_objects(result, &generation0); append_objects(result, &_PyGC_generation0);
append_objects(result, &generation1); append_objects(result, &generation1);
append_objects(result, &generation2); append_objects(result, &generation2);
return result; return result;
...@@ -820,4 +774,105 @@ initgc(void) ...@@ -820,4 +774,105 @@ initgc(void)
PyInt_FromLong(DEBUG_LEAK)); PyInt_FromLong(DEBUG_LEAK));
} }
/* for debugging */
void _PyGC_Dump(PyGC_Head *g)
{
_PyObject_Dump(FROM_GC(g));
}
#endif /* WITH_CYCLE_GC */ #endif /* WITH_CYCLE_GC */
/* extension modules might be compiled with GC support so these
functions must always be available */
void
_PyObject_GC_Track(PyObject *op)
{
_PyObject_GC_TRACK(op);
}
void
_PyObject_GC_UnTrack(PyObject *op)
{
_PyObject_GC_UNTRACK(op);
}
PyObject *
_PyObject_GC_Malloc(PyTypeObject *tp, int size)
{
PyObject *op;
#ifdef WITH_CYCLE_GC
PyGC_Head *g = PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size) +
sizeof(PyGC_Head));
if (g == NULL)
return (PyObject *)PyErr_NoMemory();
g->gc_next = NULL;
allocated++;
if (allocated > threshold0 &&
enabled &&
threshold0 &&
!collecting &&
!PyErr_Occurred()) {
collecting = 1;
collect_generations();
collecting = 0;
}
op = FROM_GC(g);
#else
op = PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size));
if (op == NULL)
return (PyObject *)PyErr_NoMemory();
#endif
return op;
}
PyObject *
_PyObject_GC_New(PyTypeObject *tp)
{
PyObject *op = _PyObject_GC_Malloc(tp, 0);
return PyObject_INIT(op, tp);
}
PyVarObject *
_PyObject_GC_NewVar(PyTypeObject *tp, int size)
{
PyVarObject *op = (PyVarObject *) _PyObject_GC_Malloc(tp, size);
return PyObject_INIT_VAR(op, tp, size);
}
PyVarObject *
_PyObject_GC_Resize(PyVarObject *op, int size)
{
#ifdef WITH_CYCLE_GC
PyGC_Head *g = AS_GC(op);
g = PyObject_REALLOC(g, _PyObject_VAR_SIZE(op->ob_type, size) +
sizeof(PyGC_Head));
if (g == NULL)
return (PyVarObject *)PyErr_NoMemory();
op = (PyVarObject *) FROM_GC(g);
#else
op = PyObject_REALLOC(op, _PyObject_VAR_SIZE(op->ob_type, size));
if (op == NULL)
return (PyVarObject *)PyErr_NoMemory();
#endif
op->ob_size = size;
return op;
}
void
_PyObject_GC_Del(PyObject *op)
{
#ifdef WITH_CYCLE_GC
PyGC_Head *g = AS_GC(op);
if (g->gc_next != NULL)
gc_list_remove(g);
if (allocated > 0) {
allocated--;
}
PyObject_FREE(g);
#else
PyObject_FREE(op);
#endif
}
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