diff -rcN pytmp/Include/objimpl.h pygc/Include/objimpl.h *** pytmp/Include/objimpl.h Tue Jun 27 13:23:17 2000 --- pygc/Include/objimpl.h Tue Jun 27 13:27:16 2000 *************** *** 256,262 **** #define PyObject_GC_Init(op) #define PyObject_GC_Fini(op) ! #endif #ifdef __cplusplus } --- 256,300 ---- #define PyObject_GC_Init(op) #define PyObject_GC_Fini(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(op) ! #define PyObject_GC_Fini(op) _PyGC_Remove(op) ! ! /* Structure *prefixed* to container objects participating in GC */ ! typedef struct _gcinfo { ! struct _gcinfo *gc_next; ! struct _gcinfo *gc_prev; ! int gc_refs; ! } PyGCInfo; ! ! #define PyGC_INFO_SIZE sizeof(PyGCInfo) ! ! /* Test if a type has GC info */ ! #define PyGC_TYPE_HAS_INFO(t) PyType_HasFeature((t), Py_TPFLAGS_GC) ! ! /* Test if an object has GC info */ ! #define PyGC_HAS_INFO(o) PyGC_TYPE_HAS_INFO((o)->ob_type) ! ! /* Get an object's GC info -- NULL if the object has none */ ! #define PyGC_INFO(o) (PyGC_HAS_INFO(o) ? ((PyGCInfo *)(o)-1) : NULL) ! ! /* Unsafe version of PyGC_INFO() -- only call if PyGC_HAS_INFO(p) is true */ ! #define PyGC_INFO_UNSAFE(o) ((PyGCInfo *)(o)-1) ! ! /* Get the object given the PyGCInfo */ ! #define PyGC_OBJ(g) ((PyObject *)((g)+1)) ! ! /* Interpreter macro, use PyObject_Del for extension types. */ ! #define PyObject_GC_DEL(op) PyObject_FREE(PyGC_INFO(op)) ! ! #endif /* WITH_CYCLE_GC */ #ifdef __cplusplus } diff -rcN pytmp/Lib/test/test_gc.py pygc/Lib/test/test_gc.py *** pytmp/Lib/test/test_gc.py Wed Dec 31 17:00:00 1969 --- pygc/Lib/test/test_gc.py Tue Jun 27 13:27:16 2000 *************** *** 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() diff -rcN pytmp/Modules/Setup.thread.in pygc/Modules/Setup.thread.in *** pytmp/Modules/Setup.thread.in Tue Apr 25 17:33:19 2000 --- pygc/Modules/Setup.thread.in Tue Jun 27 13:27:16 2000 *************** *** 9,11 **** --- 9,14 ---- # support threads. @USE_THREAD_MODULE@thread threadmodule.c + + # Garbage collection enabled with --with-cycle-gc + @USE_GC_MODULE@gc gcmodule.c diff -rcN pytmp/Modules/gcmodule.c pygc/Modules/gcmodule.c *** pytmp/Modules/gcmodule.c Wed Dec 31 17:00:00 1969 --- pygc/Modules/gcmodule.c Tue Jun 27 13:44:54 2000 *************** *** 0 **** --- 1,675 ---- + /* + + Reference Cycle Garbage Collection + ================================== + + Neil Schemenauer + + 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 PyGCInfo generation0 = {&generation0, &generation0, 0}; + static PyGCInfo generation1 = {&generation1, &generation1, 0}; + static PyGCInfo 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(PyGCInfo *list) + { + list->gc_prev = list; + list->gc_next = list; + } + + static void + gc_list_append(PyGCInfo *node, PyGCInfo *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(PyGCInfo *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(PyGCInfo *from, PyGCInfo *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(PyGCInfo *from, PyGCInfo *to) + { + PyGCInfo *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(PyGCInfo *list) + { + PyGCInfo *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(PyGCInfo *containers) + { + PyGCInfo *gc = containers->gc_next; + for (; gc != containers; gc=gc->gc_next) { + gc->gc_refs = PyGC_OBJ(gc)->ob_refcnt; + } + } + + static int + visit_decref(PyObject *op, void *data) + { + if (op && PyGC_HAS_INFO(op)) { + PyGC_INFO_UNSAFE(op)->gc_refs--; + } + return 0; + } + + /* Subtract internal references from gc_refs */ + static void + subtract_refs(PyGCInfo *containers) + { + traverseproc traverse; + PyGCInfo *gc = containers->gc_next; + for (; gc != containers; gc=gc->gc_next) { + traverse = PyGC_OBJ(gc)->ob_type->tp_traverse; + (void) traverse(PyGC_OBJ(gc), + (visitproc)visit_decref, + NULL); + } + } + + /* Append objects with gc_refs > 0 to roots list */ + static void + move_roots(PyGCInfo *containers, PyGCInfo *roots) + { + PyGCInfo *next; + PyGCInfo *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, PyGCInfo *roots) + { + PyGCInfo *gc = PyGC_INFO(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(PyGCInfo *reachable) + { + traverseproc traverse; + PyGCInfo *gc = reachable->gc_next; + for (; gc != reachable; gc=gc->gc_next) { + /* careful, reachable list is growing here */ + PyObject *op = PyGC_OBJ(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(PyGCInfo *unreachable, PyGCInfo *finalizers) + { + PyGCInfo *next; + PyGCInfo *gc = unreachable->gc_next; + static PyObject *delstr; + if (delstr == NULL) { + delstr = PyString_InternFromString("__del__"); + } + for (; gc != unreachable; gc=next) { + PyObject *op = PyGC_OBJ(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, PyGCInfo *finalizers) + { + PyGCInfo *gc = PyGC_INFO(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(PyGCInfo *finalizers) + { + traverseproc traverse; + PyGCInfo *gc = finalizers->gc_next; + for (; gc != finalizers; gc=gc->gc_next) { + /* careful, finalizers list is growing here */ + traverse = PyGC_OBJ(gc)->ob_type->tp_traverse; + (void) traverse(PyGC_OBJ(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(PyGCInfo *finalizers, PyGCInfo *old) + { + PyGCInfo *gc; + if (garbage == NULL) { + garbage = PyList_New(0); + } + for (gc = finalizers->gc_next; gc != finalizers; + gc = finalizers->gc_next) { + PyObject *op = PyGC_OBJ(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(PyGCInfo *unreachable, PyGCInfo *old) + { + inquiry clear; + + while (unreachable->gc_next != unreachable) { + PyGCInfo *gc = unreachable->gc_next; + PyObject *op = PyGC_OBJ(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(PyGCInfo *young, PyGCInfo *old) + { + long n = 0; + long m = 0; + PyGCInfo reachable; + PyGCInfo unreachable; + PyGCInfo finalizers; + PyGCInfo *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 ", PyGC_OBJ(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 ", PyGC_OBJ(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 (!PyGC_HAS_INFO(op)) { + abort(); + } + #endif + if (threshold0 && allocated > threshold0 && !collecting) { + collecting++; + collect_generations(); + collecting--; + } + allocated++; + gc_list_append(PyGC_INFO(op), &generation0); + } + + void + _PyGC_Remove(PyObject *op) + { + PyGCInfo *g = PyGC_INFO(op); + #ifdef Py_DEBUG + if (!PyGC_HAS_INFO(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 */ diff -rcN pytmp/Objects/classobject.c pygc/Objects/classobject.c *** pytmp/Objects/classobject.c Tue Jun 27 13:23:18 2000 --- pygc/Objects/classobject.c Tue Jun 27 13:27:16 2000 *************** *** 603,609 **** --- 603,611 ---- 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); *************** *** 1785,1790 **** while (free_list) { PyMethodObject *im = free_list; free_list = (PyMethodObject *)(im->im_self); ! PyObject_DEL(im); } } --- 1787,1792 ---- while (free_list) { PyMethodObject *im = free_list; free_list = (PyMethodObject *)(im->im_self); ! PyObject_GC_DEL(im); } } diff -rcN pytmp/Objects/listobject.c pygc/Objects/listobject.c *** pytmp/Objects/listobject.c Tue Jun 27 13:23:18 2000 --- pygc/Objects/listobject.c Tue Jun 27 13:27:16 2000 *************** *** 76,81 **** --- 76,84 ---- if (op == NULL) { return PyErr_NoMemory(); } + #ifdef WITH_CYCLE_GC + op = (PyListObject *) PyGC_OBJ((PyGCInfo *)op); + #endif if (size <= 0) { op->ob_item = NULL; } diff -rcN pytmp/Objects/object.c pygc/Objects/object.c *** pytmp/Objects/object.c Fri Jun 23 16:21:08 2000 --- pygc/Objects/object.c Tue Jun 27 13:27:16 2000 *************** *** 122,127 **** --- 122,131 ---- "NULL object passed to PyObject_Init"); return op; } + #ifdef WITH_CYCLE_GC + if (PyGC_TYPE_HAS_INFO(tp)) + op = (PyObject *) PyGC_OBJ((PyGCInfo *)op); + #endif /* Any changes should be reflected in PyObject_INIT (objimpl.h) */ op->ob_type = tp; _Py_NewReference(op); *************** *** 139,144 **** --- 143,152 ---- "NULL object passed to PyObject_InitVar"); return op; } + #ifdef WITH_CYCLE_GC + if (PyGC_TYPE_HAS_INFO(tp)) + op = (PyVarObject *) PyGC_OBJ((PyGCInfo *)op); + #endif /* Any changes should be reflected in PyObject_INIT_VAR */ op->ob_size = size; op->ob_type = tp; *************** *** 154,159 **** --- 162,171 ---- op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp)); if (op == NULL) return PyErr_NoMemory(); + #ifdef WITH_CYCLE_GC + if (PyGC_TYPE_HAS_INFO(tp)) + op = (PyObject *) PyGC_OBJ((PyGCInfo *)op); + #endif return PyObject_INIT(op, tp); } *************** *** 166,171 **** --- 178,187 ---- op = (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE(tp, size)); if (op == NULL) return (PyVarObject *)PyErr_NoMemory(); + #ifdef WITH_CYCLE_GC + if (PyGC_TYPE_HAS_INFO(tp)) + op = (PyVarObject *) PyGC_OBJ((PyGCInfo *)op); + #endif return PyObject_INIT_VAR(op, tp, size); } *************** *** 173,181 **** _PyObject_Del(op) PyObject *op; { ! PyObject_FREE(op); } int PyObject_Print(op, fp, flags) PyObject *op; --- 189,211 ---- _PyObject_Del(op) PyObject *op; { ! #ifdef WITH_CYCLE_GC ! if (PyGC_TYPE_HAS_INFO(op->ob_type)) { ! PyGCInfo *g = PyGC_INFO(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; *************** *** 858,865 **** --- 888,897 ---- { 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); } diff -rcN pytmp/Objects/tupleobject.c pygc/Objects/tupleobject.c *** pytmp/Objects/tupleobject.c Tue Jun 27 13:23:18 2000 --- pygc/Objects/tupleobject.c Tue Jun 27 13:27:16 2000 *************** *** 103,108 **** --- 103,111 ---- op = (PyTupleObject *) PyObject_MALLOC(nbytes); if (op == NULL) return PyErr_NoMemory(); + #ifdef WITH_CYCLE_GC + op = (PyTupleObject *) PyGC_OBJ((PyGCInfo *)op); + #endif PyObject_INIT_VAR(op, &PyTuple_Type, size); } for (i = 0; i < size; i++) *************** *** 558,567 **** --- 561,584 ---- } else #endif { + #ifdef WITH_CYCLE_GC + PyGCInfo *g = PyGC_INFO((PyObject *)v); + PyObject_GC_Fini((PyObject *)v); + sv = (PyTupleObject *) + PyObject_REALLOC((char *)g, sizeof(PyTupleObject) + + PyGC_INFO_SIZE + + newsize * sizeof(PyObject *)); + if (g == NULL) { + sv = NULL; + } else { + sv = (PyTupleObject *)PyGC_OBJ(g); + } + #else sv = (PyTupleObject *) PyObject_REALLOC((char *)v, sizeof(PyTupleObject) + PyGC_INFO_SIZE + newsize * sizeof(PyObject *)); + #endif *pv = (PyObject *) sv; if (sv == NULL) { PyObject_GC_Init((PyObject *)v); diff -rcN pytmp/PC/config.c pygc/PC/config.c *** pytmp/PC/config.c Mon May 8 12:24:03 2000 --- pygc/PC/config.c Tue Jun 27 13:27:16 2000 *************** *** 43,48 **** --- 43,51 ---- #endif extern void initcmath(); extern void initerrno(); + #ifdef WITH_CYCLE_GC + extern void initgc(); + #endif #ifndef MS_WIN64 extern void initimageop(); #endif *************** *** 89,94 **** --- 92,100 ---- #endif {"cmath", initcmath}, {"errno", initerrno}, + #ifdef WITH_CYCLE_GC + {"gc", initgc}, + #endif #ifndef MS_WIN64 {"imageop", initimageop}, #endif diff -rcN pytmp/PC/config.h pygc/PC/config.h *** pytmp/PC/config.h Fri Jun 23 16:21:08 2000 --- pygc/PC/config.h Tue Jun 27 13:27:16 2000 *************** *** 429,434 **** --- 429,437 ---- /* 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 */ diff -rcN pytmp/PCbuild/python16.dsp pygc/PCbuild/python16.dsp *** pytmp/PCbuild/python16.dsp Wed Jun 7 13:08:39 2000 --- pygc/PCbuild/python16.dsp Tue Jun 27 13:28:28 2000 *************** *** 660,665 **** --- 660,680 ---- # 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 !IF "$(CFG)" == "python16 - Win32 Release" diff -rcN pytmp/config.h.in pygc/config.h.in *** pytmp/config.h.in Fri Jun 23 16:21:08 2000 --- pygc/config.h.in Tue Jun 27 13:27:16 2000 *************** *** 210,215 **** --- 210,218 ---- (shared library plus accessory files). */ #undef WITH_NEXT_FRAMEWORK + /* Define if you want cycle garbage collection */ + #undef WITH_CYCLE_GC + /* The number of bytes in an off_t. */ #undef SIZEOF_OFF_T diff -rcN pytmp/configure.in pygc/configure.in *** pytmp/configure.in Fri Jun 23 16:21:08 2000 --- pygc/configure.in Tue Jun 27 13:27:16 2000 *************** *** 1084,1089 **** --- 1084,1100 ---- fi], [AC_MSG_RESULT(no)]) + # Check for GC support + AC_SUBST(USE_GC_MODULE) + USE_GC_MODULE="#" + AC_MSG_CHECKING(for --with-cycle-gc) + AC_ARG_WITH(cycle-gc, [--with-cycle-gc enable garbage collection], [ + AC_MSG_RESULT($withval) + AC_DEFINE(WITH_CYCLE_GC) + USE_GC_MODULE= + ], + AC_MSG_RESULT(no)) + # THIS MUST BE LAST, IT CAN BREAK OTHER TESTS! # Add sys/socket.h to confdefs.h cat >> confdefs.h <<\EOF