summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorNeil Schemenauer <nascheme@enme.ucalgary.ca>2001-08-30 00:05:51 (GMT)
committerNeil Schemenauer <nascheme@enme.ucalgary.ca>2001-08-30 00:05:51 (GMT)
commit43411b56832819104ac8fb1e31013ad4a7db866c (patch)
tree6649013c2318a49b72dec1091bb8c7b6727eb717 /Modules
parentcf22946c33132544f081f9ba6449be30a9866d8c (diff)
downloadcpython-43411b56832819104ac8fb1e31013ad4a7db866c.zip
cpython-43411b56832819104ac8fb1e31013ad4a7db866c.tar.gz
cpython-43411b56832819104ac8fb1e31013ad4a7db866c.tar.bz2
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).
Diffstat (limited to 'Modules')
-rw-r--r--Modules/gcmodule.c241
1 files changed, 148 insertions, 93 deletions
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index dcf0128..30b6839 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -8,7 +8,7 @@
Based on a post on the python-dev list. Ideas from Guido van Rossum,
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/004010.html
http://www.python.org/pipermail/python-dev/2000-March/004022.html
@@ -18,18 +18,21 @@
*/
-
#include "Python.h"
#ifdef WITH_CYCLE_GC
-/* magic gc_refs value */
-#define GC_MOVED -1
+/* Get an object's GC head */
+#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 ***/
/* 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 generation2 = {&generation2, &generation2, 0};
static int generation = 0; /* current generation being collected */
@@ -43,6 +46,9 @@ static int threshold2 = 10; /* generation1 collections before collecting 2 */
/* net new objects allocated since last collection */
static int allocated;
+/* true if we are currently running the collector */
+static int collecting;
+
/* set for debugging information */
#define DEBUG_STATS (1<<0) /* print collection statistics */
#define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */
@@ -57,6 +63,9 @@ static int allocated;
DEBUG_SAVEALL
static int debug;
+/* Special gc_refs value */
+#define GC_MOVED -123
+
/* list of uncollectable objects */
static PyObject *garbage;
@@ -86,10 +95,7 @@ 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
+ node->gc_next = NULL; /* object is not currently tracked */
}
static void
@@ -137,13 +143,14 @@ gc_list_size(PyGC_Head *list)
/*** 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;
+ gc->gc_refs = FROM_GC(gc)->ob_refcnt;
}
}
@@ -151,7 +158,9 @@ static int
visit_decref(PyObject *op, void *data)
{
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;
}
@@ -163,8 +172,8 @@ 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),
+ traverse = FROM_GC(gc)->ob_type->tp_traverse;
+ (void) traverse(FROM_GC(gc),
(visitproc)visit_decref,
NULL);
}
@@ -188,13 +197,13 @@ move_roots(PyGC_Head *containers, PyGC_Head *roots)
}
static int
-visit_reachable(PyObject *op, PyGC_Head *roots)
+visit_move(PyObject *op, PyGC_Head *tolist)
{
if (PyObject_IS_GC(op)) {
- PyGC_Head *gc = PyObject_AS_GC(op);
- if (gc && gc->gc_refs != GC_MOVED) {
+ PyGC_Head *gc = AS_GC(op);
+ if (gc->gc_next != NULL && gc->gc_refs != GC_MOVED) {
gc_list_remove(gc);
- gc_list_append(gc, roots);
+ gc_list_append(gc, tolist);
gc->gc_refs = GC_MOVED;
}
}
@@ -209,15 +218,15 @@ move_root_reachable(PyGC_Head *reachable)
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);
+ PyObject *op = FROM_GC(gc);
traverse = op->ob_type->tp_traverse;
(void) traverse(op,
- (visitproc)visit_reachable,
+ (visitproc)visit_move,
(void *)reachable);
}
}
-/* move all objects with finalizers (instances with __del__) */
+/* Move all objects with finalizers (instances with __del__) */
static void
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");
}
for (; gc != unreachable; gc=next) {
- PyObject *op = PyObject_FROM_GC(gc);
+ PyObject *op = FROM_GC(gc);
next = gc->gc_next;
if (PyInstance_Check(op) && PyObject_HasAttr(op, delstr)) {
gc_list_remove(gc);
@@ -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 */
static void
move_finalizer_reachable(PyGC_Head *finalizers)
@@ -263,9 +256,9 @@ move_finalizer_reachable(PyGC_Head *finalizers)
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,
+ traverse = FROM_GC(gc)->ob_type->tp_traverse;
+ (void) traverse(FROM_GC(gc),
+ (visitproc)visit_move,
(void *)finalizers);
}
}
@@ -306,7 +299,7 @@ handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
}
for (gc = finalizers->gc_next; gc != finalizers;
gc = finalizers->gc_next) {
- PyObject *op = PyObject_FROM_GC(gc);
+ PyObject *op = FROM_GC(gc);
if ((debug & DEBUG_SAVEALL) || PyInstance_Check(op)) {
/* If SAVEALL is not set then just append
* instances to the list of garbage. We assume
@@ -330,7 +323,7 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
while (unreachable->gc_next != unreachable) {
PyGC_Head *gc = unreachable->gc_next;
- PyObject *op = PyObject_FROM_GC(gc);
+ PyObject *op = FROM_GC(gc);
if (debug & DEBUG_SAVEALL) {
PyList_Append(garbage, op);
}
@@ -366,7 +359,7 @@ collect(PyGC_Head *young, PyGC_Head *old)
"gc: collecting generation %d...\n"
"gc: objects in each generation: %ld %ld %ld\n",
generation,
- gc_list_size(&generation0),
+ gc_list_size(&_PyGC_generation0),
gc_list_size(&generation1),
gc_list_size(&generation2));
}
@@ -407,7 +400,7 @@ collect(PyGC_Head *young, PyGC_Head *old)
gc = gc->gc_next) {
m++;
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
@@ -421,7 +414,7 @@ collect(PyGC_Head *young, PyGC_Head *old)
gc = gc->gc_next) {
n++;
if (debug & DEBUG_UNCOLLECTABLE) {
- debug_cycle("uncollectable", PyObject_FROM_GC(gc));
+ debug_cycle("uncollectable", FROM_GC(gc));
}
}
if (debug & DEBUG_STATS) {
@@ -461,7 +454,7 @@ collect_generations(void)
if (collections1 > threshold2) {
generation = 2;
- gc_list_merge(&generation0, &generation2);
+ gc_list_merge(&_PyGC_generation0, &generation2);
gc_list_merge(&generation1, &generation2);
if (generation2.gc_next != &generation2) {
n = collect(&generation2, &generation2);
@@ -471,7 +464,7 @@ collect_generations(void)
else if (collections0 > threshold1) {
generation = 1;
collections1++;
- gc_list_merge(&generation0, &generation1);
+ gc_list_merge(&_PyGC_generation0, &generation1);
if (generation1.gc_next != &generation1) {
n = collect(&generation1, &generation2);
}
@@ -480,52 +473,13 @@ collect_generations(void)
else {
generation = 0;
collections0++;
- if (generation0.gc_next != &generation0) {
- n = collect(&generation0, &generation1);
+ if (_PyGC_generation0.gc_next != &_PyGC_generation0) {
+ n = collect(&_PyGC_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 (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__[] =
"enable() -> None\n"
"\n"
@@ -595,7 +549,7 @@ gc_collect(PyObject *self, PyObject *args)
return NULL;
generation = 2;
- gc_list_merge(&generation0, &generation2);
+ gc_list_merge(&_PyGC_generation0, &generation2);
gc_list_merge(&generation1, &generation2);
n = collect(&generation2, &generation2);
@@ -693,7 +647,7 @@ gc_referents_for(PyObject *objs, PyGC_Head *list, PyObject *resultlist)
PyObject *obj;
traverseproc traverse;
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;
if (obj == objs || obj == resultlist)
continue;
@@ -713,7 +667,7 @@ static PyObject *
gc_get_referents(PyObject *self, PyObject *args)
{
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, &generation2, result))) {
Py_DECREF(result);
@@ -735,7 +689,7 @@ append_objects(PyObject *py_list, PyGC_Head *gc_list)
{
PyGC_Head *gc;
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) {
Py_INCREF(op);
PyList_Append(py_list, op);
@@ -751,7 +705,7 @@ gc_get_objects(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, ":get_objects")) /* check no args */
return NULL;
result = PyList_New(0);
- append_objects(result, &generation0);
+ append_objects(result, &_PyGC_generation0);
append_objects(result, &generation1);
append_objects(result, &generation2);
return result;
@@ -820,4 +774,105 @@ initgc(void)
PyInt_FromLong(DEBUG_LEAK));
}
+/* for debugging */
+void _PyGC_Dump(PyGC_Head *g)
+{
+ _PyObject_Dump(FROM_GC(g));
+}
+
#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
+}
+