| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 1 | /* | 
|  | 2 |  | 
|  | 3 | Reference Cycle Garbage Collection | 
|  | 4 | ================================== | 
|  | 5 |  | 
| Neil Schemenauer | b2c2c9e | 2000-10-04 16:34:09 +0000 | [diff] [blame] | 6 | Neil Schemenauer <nas@arctrix.com> | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 7 |  | 
|  | 8 | Based on a post on the python-dev list.  Ideas from Guido van Rossum, | 
|  | 9 | Eric Tiedemann, and various others. | 
|  | 10 |  | 
| Neil Schemenauer | b2c2c9e | 2000-10-04 16:34:09 +0000 | [diff] [blame] | 11 | http://www.arctrix.com/nas/python/gc.html | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 12 | http://www.python.org/pipermail/python-dev/2000-March/003869.html | 
|  | 13 | http://www.python.org/pipermail/python-dev/2000-March/004010.html | 
|  | 14 | http://www.python.org/pipermail/python-dev/2000-March/004022.html | 
|  | 15 |  | 
|  | 16 | For a highlevel view of the collection process, read the collect | 
|  | 17 | function. | 
|  | 18 |  | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 19 | */ | 
|  | 20 |  | 
|  | 21 |  | 
|  | 22 | #include "Python.h" | 
|  | 23 |  | 
|  | 24 | #ifdef WITH_CYCLE_GC | 
|  | 25 |  | 
|  | 26 | /* magic gc_refs value */ | 
|  | 27 | #define GC_MOVED -1 | 
|  | 28 |  | 
|  | 29 | /*** Global GC state ***/ | 
|  | 30 |  | 
|  | 31 | /* linked lists of container objects */ | 
|  | 32 | static PyGC_Head generation0 = {&generation0, &generation0, 0}; | 
|  | 33 | static PyGC_Head generation1 = {&generation1, &generation1, 0}; | 
|  | 34 | static PyGC_Head generation2 = {&generation2, &generation2, 0}; | 
|  | 35 | static int generation = 0; /* current generation being collected */ | 
|  | 36 |  | 
|  | 37 | /* collection frequencies, XXX tune these */ | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 38 | static int enabled = 1; /* automatic collection enabled? */ | 
| Jeremy Hylton | 3263dc2b | 2000-09-05 15:44:50 +0000 | [diff] [blame] | 39 | static int threshold0 = 700; /* net new containers before collection */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 40 | static int threshold1 = 10;  /* generation0 collections before collecting 1 */ | 
|  | 41 | static int threshold2 = 10;  /* generation1 collections before collecting 2 */ | 
|  | 42 |  | 
|  | 43 | /* net new objects allocated since last collection */ | 
|  | 44 | static int allocated; | 
|  | 45 |  | 
|  | 46 | /* set for debugging information */ | 
|  | 47 | #define DEBUG_STATS		(1<<0) /* print collection statistics */ | 
|  | 48 | #define DEBUG_COLLECTABLE	(1<<1) /* print collectable objects */ | 
|  | 49 | #define DEBUG_UNCOLLECTABLE	(1<<2) /* print uncollectable objects */ | 
|  | 50 | #define DEBUG_INSTANCES		(1<<3) /* print instances */ | 
|  | 51 | #define DEBUG_OBJECTS		(1<<4) /* print other objects */ | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 52 | #define DEBUG_SAVEALL		(1<<5) /* save all garbage in gc.garbage */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 53 | #define DEBUG_LEAK		DEBUG_COLLECTABLE | \ | 
|  | 54 | DEBUG_UNCOLLECTABLE | \ | 
|  | 55 | DEBUG_INSTANCES | \ | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 56 | DEBUG_OBJECTS | \ | 
|  | 57 | DEBUG_SAVEALL | 
| Jeremy Hylton | b709df3 | 2000-09-01 02:47:25 +0000 | [diff] [blame] | 58 | static int debug; | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 59 |  | 
|  | 60 | /* list of uncollectable objects */ | 
|  | 61 | static PyObject *garbage; | 
|  | 62 |  | 
| Jeremy Hylton | b709df3 | 2000-09-01 02:47:25 +0000 | [diff] [blame] | 63 | /* Python string to use if unhandled exception occurs */ | 
|  | 64 | static PyObject *gc_str; | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 65 |  | 
|  | 66 | /*** list functions ***/ | 
|  | 67 |  | 
|  | 68 | static void | 
|  | 69 | gc_list_init(PyGC_Head *list) | 
|  | 70 | { | 
|  | 71 | list->gc_prev = list; | 
|  | 72 | list->gc_next = list; | 
|  | 73 | } | 
|  | 74 |  | 
|  | 75 | static void | 
|  | 76 | gc_list_append(PyGC_Head *node, PyGC_Head *list) | 
|  | 77 | { | 
|  | 78 | node->gc_next = list; | 
|  | 79 | node->gc_prev = list->gc_prev; | 
|  | 80 | node->gc_prev->gc_next = node; | 
|  | 81 | list->gc_prev = node; | 
|  | 82 | } | 
|  | 83 |  | 
|  | 84 | static void | 
|  | 85 | gc_list_remove(PyGC_Head *node) | 
|  | 86 | { | 
|  | 87 | node->gc_prev->gc_next = node->gc_next; | 
|  | 88 | node->gc_next->gc_prev = node->gc_prev; | 
|  | 89 | #ifdef Py_DEBUG | 
|  | 90 | node->gc_prev = NULL; | 
|  | 91 | node->gc_next = NULL; | 
|  | 92 | #endif | 
|  | 93 | } | 
|  | 94 |  | 
|  | 95 | static void | 
|  | 96 | gc_list_move(PyGC_Head *from, PyGC_Head *to) | 
|  | 97 | { | 
|  | 98 | if (from->gc_next == from) { | 
|  | 99 | /* empty from list */ | 
|  | 100 | gc_list_init(to); | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 101 | } | 
|  | 102 | else { | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 103 | to->gc_next = from->gc_next; | 
|  | 104 | to->gc_next->gc_prev = to; | 
|  | 105 | to->gc_prev = from->gc_prev; | 
|  | 106 | to->gc_prev->gc_next = to; | 
|  | 107 | } | 
|  | 108 | gc_list_init(from); | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 | /* append a list onto another list, from becomes an empty list */ | 
|  | 112 | static void | 
|  | 113 | gc_list_merge(PyGC_Head *from, PyGC_Head *to) | 
|  | 114 | { | 
|  | 115 | PyGC_Head *tail; | 
|  | 116 | if (from->gc_next != from) { | 
|  | 117 | tail = to->gc_prev; | 
|  | 118 | tail->gc_next = from->gc_next; | 
|  | 119 | tail->gc_next->gc_prev = tail; | 
|  | 120 | to->gc_prev = from->gc_prev; | 
|  | 121 | to->gc_prev->gc_next = to; | 
|  | 122 | } | 
|  | 123 | gc_list_init(from); | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | static long | 
|  | 127 | gc_list_size(PyGC_Head *list) | 
|  | 128 | { | 
|  | 129 | PyGC_Head *gc; | 
|  | 130 | long n = 0; | 
|  | 131 | for (gc = list->gc_next; gc != list; gc = gc->gc_next) { | 
|  | 132 | n++; | 
|  | 133 | } | 
|  | 134 | return n; | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | /*** end of list stuff ***/ | 
|  | 138 |  | 
|  | 139 |  | 
|  | 140 | /* Set all gc_refs = ob_refcnt */ | 
|  | 141 | static void | 
|  | 142 | update_refs(PyGC_Head *containers) | 
|  | 143 | { | 
|  | 144 | PyGC_Head *gc = containers->gc_next; | 
|  | 145 | for (; gc != containers; gc=gc->gc_next) { | 
|  | 146 | gc->gc_refs = PyObject_FROM_GC(gc)->ob_refcnt; | 
|  | 147 | } | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | static int | 
|  | 151 | visit_decref(PyObject *op, void *data) | 
|  | 152 | { | 
|  | 153 | if (op && PyObject_IS_GC(op)) { | 
|  | 154 | PyObject_AS_GC(op)->gc_refs--; | 
|  | 155 | } | 
|  | 156 | return 0; | 
|  | 157 | } | 
|  | 158 |  | 
|  | 159 | /* Subtract internal references from gc_refs */ | 
|  | 160 | static void | 
|  | 161 | subtract_refs(PyGC_Head *containers) | 
|  | 162 | { | 
|  | 163 | traverseproc traverse; | 
|  | 164 | PyGC_Head *gc = containers->gc_next; | 
|  | 165 | for (; gc != containers; gc=gc->gc_next) { | 
|  | 166 | traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse; | 
|  | 167 | (void) traverse(PyObject_FROM_GC(gc), | 
|  | 168 | (visitproc)visit_decref, | 
|  | 169 | NULL); | 
|  | 170 | } | 
|  | 171 | } | 
|  | 172 |  | 
|  | 173 | /* Append objects with gc_refs > 0 to roots list */ | 
|  | 174 | static void | 
|  | 175 | move_roots(PyGC_Head *containers, PyGC_Head *roots) | 
|  | 176 | { | 
|  | 177 | PyGC_Head *next; | 
|  | 178 | PyGC_Head *gc = containers->gc_next; | 
|  | 179 | while (gc != containers) { | 
|  | 180 | next = gc->gc_next; | 
|  | 181 | if (gc->gc_refs > 0) { | 
|  | 182 | gc_list_remove(gc); | 
|  | 183 | gc_list_append(gc, roots); | 
|  | 184 | gc->gc_refs = GC_MOVED; | 
|  | 185 | } | 
|  | 186 | gc = next; | 
|  | 187 | } | 
|  | 188 | } | 
|  | 189 |  | 
|  | 190 | static int | 
|  | 191 | visit_reachable(PyObject *op, PyGC_Head *roots) | 
|  | 192 | { | 
|  | 193 | if (PyObject_IS_GC(op)) { | 
|  | 194 | PyGC_Head *gc = PyObject_AS_GC(op); | 
|  | 195 | if (gc && gc->gc_refs != GC_MOVED) { | 
|  | 196 | gc_list_remove(gc); | 
|  | 197 | gc_list_append(gc, roots); | 
|  | 198 | gc->gc_refs = GC_MOVED; | 
|  | 199 | } | 
|  | 200 | } | 
|  | 201 | return 0; | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | /* Move objects referenced from reachable to reachable set. */ | 
|  | 205 | static void | 
|  | 206 | move_root_reachable(PyGC_Head *reachable) | 
|  | 207 | { | 
|  | 208 | traverseproc traverse; | 
|  | 209 | PyGC_Head *gc = reachable->gc_next; | 
|  | 210 | for (; gc != reachable; gc=gc->gc_next) { | 
|  | 211 | /* careful, reachable list is growing here */ | 
|  | 212 | PyObject *op = PyObject_FROM_GC(gc); | 
|  | 213 | traverse = op->ob_type->tp_traverse; | 
|  | 214 | (void) traverse(op, | 
|  | 215 | (visitproc)visit_reachable, | 
|  | 216 | (void *)reachable); | 
|  | 217 | } | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | /* move all objects with finalizers (instances with __del__) */ | 
|  | 221 | static void | 
|  | 222 | move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) | 
|  | 223 | { | 
|  | 224 | PyGC_Head *next; | 
|  | 225 | PyGC_Head *gc = unreachable->gc_next; | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 226 | static PyObject *delstr = NULL; | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 227 | if (delstr == NULL) { | 
|  | 228 | delstr = PyString_InternFromString("__del__"); | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 229 | if (delstr == NULL) | 
|  | 230 | Py_FatalError("PyGC: can't initialize __del__ string"); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 231 | } | 
|  | 232 | for (; gc != unreachable; gc=next) { | 
|  | 233 | PyObject *op = PyObject_FROM_GC(gc); | 
|  | 234 | next = gc->gc_next; | 
|  | 235 | if (PyInstance_Check(op) && PyObject_HasAttr(op, delstr)) { | 
|  | 236 | gc_list_remove(gc); | 
|  | 237 | gc_list_append(gc, finalizers); | 
|  | 238 | } | 
|  | 239 | } | 
|  | 240 | } | 
|  | 241 |  | 
|  | 242 |  | 
|  | 243 | /* called by tp_traverse */ | 
|  | 244 | static int | 
|  | 245 | visit_finalizer_reachable(PyObject *op, PyGC_Head *finalizers) | 
|  | 246 | { | 
|  | 247 | if (PyObject_IS_GC(op)) { | 
|  | 248 | PyGC_Head *gc = PyObject_AS_GC(op); | 
|  | 249 | if (gc && gc->gc_refs != GC_MOVED) { | 
|  | 250 | gc_list_remove(gc); | 
|  | 251 | gc_list_append(gc, finalizers); | 
|  | 252 | gc->gc_refs = GC_MOVED; | 
|  | 253 | } | 
|  | 254 | } | 
|  | 255 | return 0; | 
|  | 256 | } | 
|  | 257 |  | 
|  | 258 | /* Move objects referenced from roots to roots */ | 
|  | 259 | static void | 
|  | 260 | move_finalizer_reachable(PyGC_Head *finalizers) | 
|  | 261 | { | 
|  | 262 | traverseproc traverse; | 
|  | 263 | PyGC_Head *gc = finalizers->gc_next; | 
|  | 264 | for (; gc != finalizers; gc=gc->gc_next) { | 
|  | 265 | /* careful, finalizers list is growing here */ | 
|  | 266 | traverse = PyObject_FROM_GC(gc)->ob_type->tp_traverse; | 
|  | 267 | (void) traverse(PyObject_FROM_GC(gc), | 
|  | 268 | (visitproc)visit_finalizer_reachable, | 
|  | 269 | (void *)finalizers); | 
|  | 270 | } | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | static void | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 274 | debug_instance(char *msg, PyInstanceObject *inst) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 275 | { | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 276 | char *cname; | 
|  | 277 | /* be careful not to create new dictionaries */ | 
|  | 278 | PyObject *classname = inst->in_class->cl_name; | 
|  | 279 | if (classname != NULL && PyString_Check(classname)) | 
|  | 280 | cname = PyString_AsString(classname); | 
|  | 281 | else | 
|  | 282 | cname = "?"; | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 283 | PySys_WriteStderr("gc: %.100s <%.100s instance at %p>\n", | 
|  | 284 | msg, cname, inst); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 285 | } | 
|  | 286 |  | 
|  | 287 | static void | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 288 | debug_cycle(char *msg, PyObject *op) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 289 | { | 
|  | 290 | if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) { | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 291 | debug_instance(msg, (PyInstanceObject *)op); | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 292 | } | 
|  | 293 | else if (debug & DEBUG_OBJECTS) { | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 294 | PySys_WriteStderr("gc: %.100s <%.100s %p>\n", | 
|  | 295 | msg, op->ob_type->tp_name, op); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 296 | } | 
|  | 297 | } | 
|  | 298 |  | 
|  | 299 | /* Handle uncollectable garbage (cycles with finalizers). */ | 
|  | 300 | static void | 
|  | 301 | handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old) | 
|  | 302 | { | 
|  | 303 | PyGC_Head *gc; | 
|  | 304 | if (garbage == NULL) { | 
|  | 305 | garbage = PyList_New(0); | 
|  | 306 | } | 
|  | 307 | for (gc = finalizers->gc_next; gc != finalizers; | 
|  | 308 | gc = finalizers->gc_next) { | 
|  | 309 | PyObject *op = PyObject_FROM_GC(gc); | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 310 | if ((debug & DEBUG_SAVEALL) || PyInstance_Check(op)) { | 
|  | 311 | /* If SAVEALL is not set then just append | 
|  | 312 | * instances to the list of garbage.  We assume | 
|  | 313 | * that all objects in the finalizers list are | 
|  | 314 | * reachable from instances. */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 315 | PyList_Append(garbage, op); | 
|  | 316 | } | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 317 | /* object is now reachable again */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 318 | gc_list_remove(gc); | 
|  | 319 | gc_list_append(gc, old); | 
|  | 320 | } | 
|  | 321 | } | 
|  | 322 |  | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 323 | /* Break reference cycles by clearing the containers involved.	This is | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 324 | * tricky business as the lists can be changing and we don't know which | 
|  | 325 | * objects may be freed.  It is possible I screwed something up here. */ | 
|  | 326 | static void | 
|  | 327 | delete_garbage(PyGC_Head *unreachable, PyGC_Head *old) | 
|  | 328 | { | 
|  | 329 | inquiry clear; | 
|  | 330 |  | 
|  | 331 | while (unreachable->gc_next != unreachable) { | 
|  | 332 | PyGC_Head *gc = unreachable->gc_next; | 
|  | 333 | PyObject *op = PyObject_FROM_GC(gc); | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 334 | if (debug & DEBUG_SAVEALL) { | 
|  | 335 | PyList_Append(garbage, op); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 336 | } | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 337 | else { | 
|  | 338 | if ((clear = op->ob_type->tp_clear) != NULL) { | 
|  | 339 | Py_INCREF(op); | 
|  | 340 | clear((PyObject *)op); | 
|  | 341 | Py_DECREF(op); | 
|  | 342 | } | 
|  | 343 | } | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 344 | if (unreachable->gc_next == gc) { | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 345 | /* object is still alive, move it, it may die later */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 346 | gc_list_remove(gc); | 
|  | 347 | gc_list_append(gc, old); | 
|  | 348 | } | 
|  | 349 | } | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 | /* This is the main function.  Read this to understand how the | 
|  | 353 | * collection process works. */ | 
|  | 354 | static long | 
|  | 355 | collect(PyGC_Head *young, PyGC_Head *old) | 
|  | 356 | { | 
|  | 357 | long n = 0; | 
|  | 358 | long m = 0; | 
|  | 359 | PyGC_Head reachable; | 
|  | 360 | PyGC_Head unreachable; | 
|  | 361 | PyGC_Head finalizers; | 
|  | 362 | PyGC_Head *gc; | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 363 |  | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 364 | if (debug & DEBUG_STATS) { | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 365 | PySys_WriteStderr( | 
|  | 366 | "gc: collecting generation %d...\n" | 
|  | 367 | "gc: objects in each generation: %ld %ld %ld\n", | 
|  | 368 | generation, | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 369 | gc_list_size(&generation0), | 
|  | 370 | gc_list_size(&generation1), | 
|  | 371 | gc_list_size(&generation2)); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 372 | } | 
|  | 373 |  | 
|  | 374 | /* Using ob_refcnt and gc_refs, calculate which objects in the | 
|  | 375 | * container set are reachable from outside the set (ie. have a | 
|  | 376 | * refcount greater than 0 when all the references within the | 
|  | 377 | * set are taken into account */ | 
|  | 378 | update_refs(young); | 
|  | 379 | subtract_refs(young); | 
|  | 380 |  | 
|  | 381 | /* Move everything reachable from outside the set into the | 
|  | 382 | * reachable set (ie. gc_refs > 0).  Next, move everything | 
|  | 383 | * reachable from objects in the reachable set. */ | 
|  | 384 | gc_list_init(&reachable); | 
|  | 385 | move_roots(young, &reachable); | 
|  | 386 | move_root_reachable(&reachable); | 
|  | 387 |  | 
|  | 388 | /* move unreachable objects to a temporary list, new objects can be | 
|  | 389 | * allocated after this point */ | 
|  | 390 | gc_list_init(&unreachable); | 
|  | 391 | gc_list_move(young, &unreachable); | 
|  | 392 |  | 
|  | 393 | /* move reachable objects to next generation */ | 
|  | 394 | gc_list_merge(&reachable, old); | 
|  | 395 |  | 
|  | 396 | /* Move objects reachable from finalizers, we can't safely delete | 
|  | 397 | * them.  Python programmers should take care not to create such | 
|  | 398 | * things.  For Python finalizers means instance objects with | 
|  | 399 | * __del__ methods. */ | 
|  | 400 | gc_list_init(&finalizers); | 
|  | 401 | move_finalizers(&unreachable, &finalizers); | 
|  | 402 | move_finalizer_reachable(&finalizers); | 
|  | 403 |  | 
|  | 404 | /* Collect statistics on collectable objects found and print | 
|  | 405 | * debugging information. */ | 
|  | 406 | for (gc = unreachable.gc_next; gc != &unreachable; | 
|  | 407 | gc = gc->gc_next) { | 
|  | 408 | m++; | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 409 | if (debug & DEBUG_COLLECTABLE) { | 
|  | 410 | debug_cycle("collectable", PyObject_FROM_GC(gc)); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 411 | } | 
|  | 412 | } | 
|  | 413 | /* call tp_clear on objects in the collectable set.  This will cause | 
|  | 414 | * the reference cycles to be broken. It may also cause some objects in | 
|  | 415 | * finalizers to be freed */ | 
|  | 416 | delete_garbage(&unreachable, old); | 
|  | 417 |  | 
|  | 418 | /* Collect statistics on uncollectable objects found and print | 
|  | 419 | * debugging information. */ | 
|  | 420 | for (gc = finalizers.gc_next; gc != &finalizers; | 
|  | 421 | gc = gc->gc_next) { | 
|  | 422 | n++; | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 423 | if (debug & DEBUG_UNCOLLECTABLE) { | 
|  | 424 | debug_cycle("uncollectable", PyObject_FROM_GC(gc)); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 425 | } | 
|  | 426 | } | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 427 | if (debug & DEBUG_STATS) { | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 428 | if (m == 0 && n == 0) { | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 429 | PySys_WriteStderr("gc: done.\n"); | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 430 | } | 
|  | 431 | else { | 
| Jeremy Hylton | 0625777 | 2000-08-31 15:10:24 +0000 | [diff] [blame] | 432 | PySys_WriteStderr( | 
|  | 433 | "gc: done, %ld unreachable, %ld uncollectable.\n", | 
|  | 434 | n+m, n); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 435 | } | 
|  | 436 | } | 
|  | 437 |  | 
|  | 438 | /* Append instances in the uncollectable set to a Python | 
|  | 439 | * reachable list of garbage.  The programmer has to deal with | 
|  | 440 | * this if they insist on creating this type of structure. */ | 
|  | 441 | handle_finalizers(&finalizers, old); | 
|  | 442 |  | 
| Jeremy Hylton | b709df3 | 2000-09-01 02:47:25 +0000 | [diff] [blame] | 443 | if (PyErr_Occurred()) { | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 444 | if (gc_str == NULL) { | 
|  | 445 | gc_str = PyString_FromString("garbage collection"); | 
|  | 446 | } | 
| Jeremy Hylton | b709df3 | 2000-09-01 02:47:25 +0000 | [diff] [blame] | 447 | PyErr_WriteUnraisable(gc_str); | 
|  | 448 | Py_FatalError("unexpected exception during garbage collection"); | 
|  | 449 | } | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 450 | allocated = 0; | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 451 | return n+m; | 
|  | 452 | } | 
|  | 453 |  | 
|  | 454 | static long | 
|  | 455 | collect_generations(void) | 
|  | 456 | { | 
|  | 457 | static long collections0 = 0; | 
|  | 458 | static long collections1 = 0; | 
| Vladimir Marangozov | b16714b | 2000-07-10 05:37:39 +0000 | [diff] [blame] | 459 | long n = 0; | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 460 |  | 
|  | 461 |  | 
|  | 462 | if (collections1 > threshold2) { | 
|  | 463 | generation = 2; | 
|  | 464 | gc_list_merge(&generation0, &generation2); | 
|  | 465 | gc_list_merge(&generation1, &generation2); | 
|  | 466 | if (generation2.gc_next != &generation2) { | 
|  | 467 | n = collect(&generation2, &generation2); | 
|  | 468 | } | 
|  | 469 | collections1 = 0; | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 470 | } | 
|  | 471 | else if (collections0 > threshold1) { | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 472 | generation = 1; | 
|  | 473 | collections1++; | 
|  | 474 | gc_list_merge(&generation0, &generation1); | 
|  | 475 | if (generation1.gc_next != &generation1) { | 
|  | 476 | n = collect(&generation1, &generation2); | 
|  | 477 | } | 
|  | 478 | collections0 = 0; | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 479 | } | 
|  | 480 | else { | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 481 | generation = 0; | 
|  | 482 | collections0++; | 
|  | 483 | if (generation0.gc_next != &generation0) { | 
|  | 484 | n = collect(&generation0, &generation1); | 
|  | 485 | } | 
|  | 486 | } | 
|  | 487 | return n; | 
|  | 488 | } | 
|  | 489 |  | 
|  | 490 | void | 
|  | 491 | _PyGC_Insert(PyObject *op) | 
|  | 492 | { | 
|  | 493 | /* collection lock since collecting may cause allocations */ | 
|  | 494 | static int collecting = 0; | 
|  | 495 |  | 
|  | 496 | #ifdef Py_DEBUG | 
|  | 497 | if (!PyObject_IS_GC(op)) { | 
|  | 498 | abort(); | 
|  | 499 | } | 
|  | 500 | #endif | 
| Neil Schemenauer | 97d723b | 2000-10-04 16:25:07 +0000 | [diff] [blame] | 501 | if (allocated > threshold0 && | 
|  | 502 | enabled && | 
|  | 503 | threshold0 && | 
|  | 504 | !collecting && | 
|  | 505 | !PyErr_Occurred()) { | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 506 | collecting++; | 
|  | 507 | collect_generations(); | 
|  | 508 | collecting--; | 
|  | 509 | } | 
|  | 510 | allocated++; | 
|  | 511 | gc_list_append(PyObject_AS_GC(op), &generation0); | 
|  | 512 | } | 
|  | 513 |  | 
|  | 514 | void | 
|  | 515 | _PyGC_Remove(PyObject *op) | 
|  | 516 | { | 
|  | 517 | PyGC_Head *g = PyObject_AS_GC(op); | 
|  | 518 | #ifdef Py_DEBUG | 
|  | 519 | if (!PyObject_IS_GC(op)) { | 
|  | 520 | abort(); | 
|  | 521 | } | 
|  | 522 | #endif | 
|  | 523 | gc_list_remove(g); | 
|  | 524 | if (allocated > 0) { | 
|  | 525 | allocated--; | 
|  | 526 | } | 
|  | 527 | } | 
|  | 528 |  | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 529 | static char gc_enable__doc__[] = | 
|  | 530 | "enable() -> None\n" | 
|  | 531 | "\n" | 
|  | 532 | "Enable automatic garbage collection.\n" | 
|  | 533 | ; | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 534 |  | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 535 | static PyObject * | 
|  | 536 | gc_enable(PyObject *self, PyObject *args) | 
|  | 537 | { | 
|  | 538 |  | 
|  | 539 | if (!PyArg_ParseTuple(args, ":enable"))	/* check no args */ | 
|  | 540 | return NULL; | 
|  | 541 |  | 
|  | 542 | enabled = 1; | 
|  | 543 |  | 
|  | 544 | Py_INCREF(Py_None); | 
|  | 545 | return Py_None; | 
|  | 546 | } | 
|  | 547 |  | 
|  | 548 | static char gc_disable__doc__[] = | 
|  | 549 | "disable() -> None\n" | 
|  | 550 | "\n" | 
|  | 551 | "Disable automatic garbage collection.\n" | 
|  | 552 | ; | 
|  | 553 |  | 
|  | 554 | static PyObject * | 
|  | 555 | gc_disable(PyObject *self, PyObject *args) | 
|  | 556 | { | 
|  | 557 |  | 
|  | 558 | if (!PyArg_ParseTuple(args, ":disable"))	/* check no args */ | 
|  | 559 | return NULL; | 
|  | 560 |  | 
|  | 561 | enabled = 0; | 
|  | 562 |  | 
|  | 563 | Py_INCREF(Py_None); | 
|  | 564 | return Py_None; | 
|  | 565 | } | 
|  | 566 |  | 
|  | 567 | static char gc_isenabled__doc__[] = | 
|  | 568 | "isenabled() -> status\n" | 
|  | 569 | "\n" | 
|  | 570 | "Returns true if automatic garbage collection is enabled.\n" | 
|  | 571 | ; | 
|  | 572 |  | 
|  | 573 | static PyObject * | 
|  | 574 | gc_isenabled(PyObject *self, PyObject *args) | 
|  | 575 | { | 
|  | 576 |  | 
|  | 577 | if (!PyArg_ParseTuple(args, ":isenabled"))	/* check no args */ | 
|  | 578 | return NULL; | 
|  | 579 |  | 
|  | 580 | return Py_BuildValue("i", enabled); | 
|  | 581 | } | 
|  | 582 |  | 
|  | 583 | static char gc_collect__doc__[] = | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 584 | "collect() -> n\n" | 
|  | 585 | "\n" | 
|  | 586 | "Run a full collection.  The number of unreachable objects is returned.\n" | 
|  | 587 | ; | 
|  | 588 |  | 
|  | 589 | static PyObject * | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 590 | gc_collect(PyObject *self, PyObject *args) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 591 | { | 
|  | 592 | long n; | 
|  | 593 |  | 
| Fred Drake | cc1be24 | 2000-07-12 04:42:23 +0000 | [diff] [blame] | 594 | if (!PyArg_ParseTuple(args, ":collect"))	/* check no args */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 595 | return NULL; | 
|  | 596 |  | 
|  | 597 | generation = 2; | 
|  | 598 | gc_list_merge(&generation0, &generation2); | 
|  | 599 | gc_list_merge(&generation1, &generation2); | 
|  | 600 | n = collect(&generation2, &generation2); | 
|  | 601 |  | 
| Neil Schemenauer | 7760cff | 2000-09-22 22:35:36 +0000 | [diff] [blame] | 602 | return Py_BuildValue("l", n); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 603 | } | 
|  | 604 |  | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 605 | static char gc_set_debug__doc__[] = | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 606 | "set_debug(flags) -> None\n" | 
|  | 607 | "\n" | 
|  | 608 | "Set the garbage collection debugging flags. Debugging information is\n" | 
|  | 609 | "written to sys.stderr.\n" | 
|  | 610 | "\n" | 
|  | 611 | "flags is an integer and can have the following bits turned on:\n" | 
|  | 612 | "\n" | 
|  | 613 | "  DEBUG_STATS - Print statistics during collection.\n" | 
|  | 614 | "  DEBUG_COLLECTABLE - Print collectable objects found.\n" | 
|  | 615 | "  DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n" | 
|  | 616 | "  DEBUG_INSTANCES - Print instance objects.\n" | 
|  | 617 | "  DEBUG_OBJECTS - Print objects other than instances.\n" | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 618 | "  DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.\n" | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 619 | "  DEBUG_LEAK - Debug leaking programs (everything but STATS).\n" | 
|  | 620 | ; | 
|  | 621 |  | 
|  | 622 | static PyObject * | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 623 | gc_set_debug(PyObject *self, PyObject *args) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 624 | { | 
| Neil Schemenauer | 7760cff | 2000-09-22 22:35:36 +0000 | [diff] [blame] | 625 | if (!PyArg_ParseTuple(args, "i:set_debug", &debug)) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 626 | return NULL; | 
|  | 627 |  | 
|  | 628 | Py_INCREF(Py_None); | 
|  | 629 | return Py_None; | 
|  | 630 | } | 
|  | 631 |  | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 632 | static char gc_get_debug__doc__[] = | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 633 | "get_debug() -> flags\n" | 
|  | 634 | "\n" | 
|  | 635 | "Get the garbage collection debugging flags.\n" | 
|  | 636 | ; | 
|  | 637 |  | 
|  | 638 | static PyObject * | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 639 | gc_get_debug(PyObject *self, PyObject *args) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 640 | { | 
| Fred Drake | cc1be24 | 2000-07-12 04:42:23 +0000 | [diff] [blame] | 641 | if (!PyArg_ParseTuple(args, ":get_debug"))	/* no args */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 642 | return NULL; | 
|  | 643 |  | 
|  | 644 | return Py_BuildValue("i", debug); | 
|  | 645 | } | 
|  | 646 |  | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 647 | static char gc_set_thresh__doc__[] = | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 648 | "set_threshold(threshold0, [threhold1, threshold2]) -> None\n" | 
|  | 649 | "\n" | 
|  | 650 | "Sets the collection thresholds.  Setting threshold0 to zero disables\n" | 
|  | 651 | "collection.\n" | 
|  | 652 | ; | 
|  | 653 |  | 
|  | 654 | static PyObject * | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 655 | gc_set_thresh(PyObject *self, PyObject *args) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 656 | { | 
| Fred Drake | cc1be24 | 2000-07-12 04:42:23 +0000 | [diff] [blame] | 657 | if (!PyArg_ParseTuple(args, "i|ii:set_threshold", &threshold0, | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 658 | &threshold1, &threshold2)) | 
|  | 659 | return NULL; | 
|  | 660 |  | 
|  | 661 | Py_INCREF(Py_None); | 
|  | 662 | return Py_None; | 
|  | 663 | } | 
|  | 664 |  | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 665 | static char gc_get_thresh__doc__[] = | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 666 | "get_threshold() -> (threshold0, threshold1, threshold2)\n" | 
|  | 667 | "\n" | 
|  | 668 | "Return the current collection thresholds\n" | 
|  | 669 | ; | 
|  | 670 |  | 
|  | 671 | static PyObject * | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 672 | gc_get_thresh(PyObject *self, PyObject *args) | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 673 | { | 
| Fred Drake | cc1be24 | 2000-07-12 04:42:23 +0000 | [diff] [blame] | 674 | if (!PyArg_ParseTuple(args, ":get_threshold"))	/* no args */ | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 675 | return NULL; | 
|  | 676 |  | 
|  | 677 | return Py_BuildValue("(iii)", threshold0, threshold1, threshold2); | 
|  | 678 | } | 
|  | 679 |  | 
|  | 680 |  | 
|  | 681 | static char gc__doc__ [] = | 
|  | 682 | "This module provides access to the garbage collector for reference cycles.\n" | 
|  | 683 | "\n" | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 684 | "enable() -- Enable automatic garbage collection.\n" | 
|  | 685 | "disable() -- Disable automatic garbage collection.\n" | 
|  | 686 | "isenabled() -- Returns true if automatic collection is enabled.\n" | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 687 | "collect() -- Do a full collection right now.\n" | 
|  | 688 | "set_debug() -- Set debugging flags.\n" | 
|  | 689 | "get_debug() -- Get debugging flags.\n" | 
|  | 690 | "set_threshold() -- Set the collection thresholds.\n" | 
|  | 691 | "get_threshold() -- Return the current the collection thresholds.\n" | 
|  | 692 | ; | 
|  | 693 |  | 
|  | 694 | static PyMethodDef GcMethods[] = { | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 695 | {"enable",	   gc_enable,	  METH_VARARGS, gc_enable__doc__}, | 
|  | 696 | {"disable",	   gc_disable,	  METH_VARARGS, gc_disable__doc__}, | 
| Vladimir Marangozov | f9d20c3 | 2000-08-06 22:45:31 +0000 | [diff] [blame] | 697 | {"isenabled",	   gc_isenabled,  METH_VARARGS, gc_isenabled__doc__}, | 
|  | 698 | {"set_debug",	   gc_set_debug,  METH_VARARGS, gc_set_debug__doc__}, | 
|  | 699 | {"get_debug",	   gc_get_debug,  METH_VARARGS, gc_get_debug__doc__}, | 
|  | 700 | {"set_threshold",  gc_set_thresh, METH_VARARGS, gc_set_thresh__doc__}, | 
|  | 701 | {"get_threshold",  gc_get_thresh, METH_VARARGS, gc_get_thresh__doc__}, | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 702 | {"collect",	   gc_collect,	  METH_VARARGS, gc_collect__doc__}, | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 703 | {NULL,	NULL}		/* Sentinel */ | 
|  | 704 | }; | 
|  | 705 |  | 
|  | 706 | void | 
|  | 707 | initgc(void) | 
|  | 708 | { | 
|  | 709 | PyObject *m; | 
|  | 710 | PyObject *d; | 
|  | 711 |  | 
|  | 712 | m = Py_InitModule4("gc", | 
|  | 713 | GcMethods, | 
|  | 714 | gc__doc__, | 
|  | 715 | NULL, | 
|  | 716 | PYTHON_API_VERSION); | 
|  | 717 | d = PyModule_GetDict(m); | 
|  | 718 | if (garbage == NULL) { | 
|  | 719 | garbage = PyList_New(0); | 
|  | 720 | } | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 721 | PyDict_SetItemString(d, "garbage", garbage); | 
|  | 722 | PyDict_SetItemString(d, "DEBUG_STATS", | 
|  | 723 | PyInt_FromLong(DEBUG_STATS)); | 
|  | 724 | PyDict_SetItemString(d, "DEBUG_COLLECTABLE", | 
|  | 725 | PyInt_FromLong(DEBUG_COLLECTABLE)); | 
|  | 726 | PyDict_SetItemString(d, "DEBUG_UNCOLLECTABLE", | 
|  | 727 | PyInt_FromLong(DEBUG_UNCOLLECTABLE)); | 
|  | 728 | PyDict_SetItemString(d, "DEBUG_INSTANCES", | 
|  | 729 | PyInt_FromLong(DEBUG_INSTANCES)); | 
|  | 730 | PyDict_SetItemString(d, "DEBUG_OBJECTS", | 
|  | 731 | PyInt_FromLong(DEBUG_OBJECTS)); | 
| Neil Schemenauer | 544de1e | 2000-09-22 15:22:38 +0000 | [diff] [blame] | 732 | PyDict_SetItemString(d, "DEBUG_SAVEALL", | 
|  | 733 | PyInt_FromLong(DEBUG_SAVEALL)); | 
| Jeremy Hylton | c5007aa | 2000-06-30 05:02:53 +0000 | [diff] [blame] | 734 | PyDict_SetItemString(d, "DEBUG_LEAK", | 
|  | 735 | PyInt_FromLong(DEBUG_LEAK)); | 
|  | 736 | } | 
|  | 737 |  | 
|  | 738 | #endif /* WITH_CYCLE_GC */ |