]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport to 2.2.x:
authorGuido van Rossum <guido@python.org>
Tue, 4 Jun 2002 21:19:55 +0000 (21:19 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 4 Jun 2002 21:19:55 +0000 (21:19 +0000)
Address SF bug 519621: slots weren't traversed by GC.

While I was at it, I added a tp_clear handler and changed the
tp_dealloc handler to use the clear_slots helper for the tp_clear
handler.

Also set mp->flags = READONLY for the __weakref__ pseudo-slot.

[Note that I am *not* backporting the part of that patch that
tightened the __slot__ rules.]

Lib/test/test_descr.py
Misc/NEWS
Objects/typeobject.c

index 3f7e5dda47a2eb11cc397856dbc204f532e32582..5048b823f81b9a0e727e4244675bffec8b68be76 100644 (file)
@@ -1093,6 +1093,18 @@ def slots():
     del x
     vereq(Counted.counter, 0)
 
+    # Test cyclical leaks [SF bug 519621]
+    class F(object):
+        __slots__ = ['a', 'b']
+    log = []
+    s = F()
+    s.a = [Counted(), s]
+    vereq(Counted.counter, 1)
+    s = None
+    import gc
+    gc.collect()
+    vereq(Counted.counter, 0)
+
 def dynamics():
     if verbose: print "Testing class attribute propagation..."
     class D(object):
index 48c9b321a6b3dcbbe5342cf8b78d7f7ed2e836b3..cd2ad39150040ef1f685398141c963da67954562 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -4,6 +4,9 @@ Release date: dd-mmm-2002
 
 Core and builtins
 
+- Classes using __slots__ are now properly garbage collected.
+  [SF bug 519621]
+
 - Repaired a slow memory leak possible only in programs creating a
   great many cyclic structures involving frames.  Reported on
   SourceForge as bug 543148.
index c2264541cec895e0baf5c386bcc156d1c7e49e12..3736c848372190b9fbc245cd02f470b5d808228d 100644 (file)
@@ -4,6 +4,20 @@
 #include "Python.h"
 #include "structmember.h"
 
+#include <ctype.h>
+
+/* The *real* layout of a type object when allocated on the heap */
+/* XXX Should we publish this in a header file? */
+typedef struct {
+       PyTypeObject type;
+       PyNumberMethods as_number;
+       PySequenceMethods as_sequence;
+       PyMappingMethods as_mapping;
+       PyBufferProcs as_buffer;
+       PyObject *name, *slots;
+       PyMemberDef members[1];
+} etype;
+
 static PyMemberDef type_members[] = {
        {"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY},
        {"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY},
@@ -222,17 +236,44 @@ PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
 /* Helpers for subtyping */
 
+static int
+traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg)
+{
+       int i, n;
+       PyMemberDef *mp;
+
+       n = type->ob_size;
+       mp = ((etype *)type)->members;
+       for (i = 0; i < n; i++, mp++) {
+               if (mp->type == T_OBJECT_EX) {
+                       char *addr = (char *)self + mp->offset;
+                       PyObject *obj = *(PyObject **)addr;
+                       if (obj != NULL) {
+                               int err = visit(obj, arg);
+                               if (err)
+                                       return err;
+                       }
+               }
+       }
+       return 0;
+}
+
 static int
 subtype_traverse(PyObject *self, visitproc visit, void *arg)
 {
        PyTypeObject *type, *base;
-       traverseproc f;
-       int err;
+       traverseproc basetraverse;
 
-       /* Find the nearest base with a different tp_traverse */
+       /* Find the nearest base with a different tp_traverse,
+          and traverse slots while we're at it */
        type = self->ob_type;
-       base = type->tp_base;
-       while ((f = base->tp_traverse) == subtype_traverse) {
+       base = type;
+       while ((basetraverse = base->tp_traverse) == subtype_traverse) {
+               if (base->ob_size) {
+                       int err = traverse_slots(base, self, visit, arg);
+                       if (err)
+                               return err;
+               }
                base = base->tp_base;
                assert(base);
        }
@@ -240,14 +281,63 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg)
        if (type->tp_dictoffset != base->tp_dictoffset) {
                PyObject **dictptr = _PyObject_GetDictPtr(self);
                if (dictptr && *dictptr) {
-                       err = visit(*dictptr, arg);
+                       int err = visit(*dictptr, arg);
                        if (err)
                                return err;
                }
        }
 
-       if (f)
-               return f(self, visit, arg);
+       if (basetraverse)
+               return basetraverse(self, visit, arg);
+       return 0;
+}
+
+static void
+clear_slots(PyTypeObject *type, PyObject *self)
+{
+       int i, n;
+       PyMemberDef *mp;
+
+       n = type->ob_size;
+       mp = ((etype *)type)->members;
+       for (i = 0; i < n; i++, mp++) {
+               if (mp->type == T_OBJECT_EX && !(mp->flags & READONLY)) {
+                       char *addr = (char *)self + mp->offset;
+                       PyObject *obj = *(PyObject **)addr;
+                       if (obj != NULL) {
+                               Py_DECREF(obj);
+                               *(PyObject **)addr = NULL;
+                       }
+               }
+       }
+}
+
+static int
+subtype_clear(PyObject *self)
+{
+       PyTypeObject *type, *base;
+       inquiry baseclear;
+
+       /* Find the nearest base with a different tp_clear
+          and clear slots while we're at it */
+       type = self->ob_type;
+       base = type;
+       while ((baseclear = base->tp_clear) == subtype_clear) {
+               if (base->ob_size)
+                       clear_slots(base, self);
+               base = base->tp_base;
+               assert(base);
+       }
+
+       if (type->tp_dictoffset != base->tp_dictoffset) {
+               PyObject **dictptr = _PyObject_GetDictPtr(self);
+               if (dictptr && *dictptr) {
+                       PyDict_Clear(*dictptr);
+               }
+       }
+
+       if (baseclear)
+               return baseclear(self);
        return 0;
 }
 
@@ -326,41 +416,24 @@ static void
 subtype_dealloc(PyObject *self)
 {
        PyTypeObject *type, *base;
-       destructor f;
+       destructor basedealloc;
 
        /* This exists so we can DECREF self->ob_type */
 
        if (call_finalizer(self) < 0)
                return;
 
-       /* Find the nearest base with a different tp_dealloc */
+       /* Find the nearest base with a different tp_dealloc
+          and clear slots while we're at it */
        type = self->ob_type;
-       base = type->tp_base;
-       while ((f = base->tp_dealloc) == subtype_dealloc) {
+       base = type;
+       while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
+               if (base->ob_size)
+                       clear_slots(base, self);
                base = base->tp_base;
                assert(base);
        }
 
-       /* Clear __slots__ variables */
-       if (type->tp_basicsize != base->tp_basicsize &&
-           type->tp_itemsize == 0)
-       {
-               char *addr = ((char *)self);
-               char *p = addr + base->tp_basicsize;
-               char *q = addr + type->tp_basicsize;
-               for (; p < q; p += sizeof(PyObject *)) {
-                       PyObject **pp;
-                       if (p == addr + type->tp_dictoffset ||
-                           p == addr + type->tp_weaklistoffset)
-                               continue;
-                       pp = (PyObject **)p;
-                       if (*pp != NULL) {
-                               Py_DECREF(*pp);
-                               *pp = NULL;
-                       }
-               }
-       }
-
        /* If we added a dict, DECREF it */
        if (type->tp_dictoffset && !base->tp_dictoffset) {
                PyObject **dictptr = _PyObject_GetDictPtr(self);
@@ -382,8 +455,8 @@ subtype_dealloc(PyObject *self)
                _PyObject_GC_UNTRACK(self);
 
        /* Call the base tp_dealloc() */
-       assert(f);
-       f(self);
+       assert(basedealloc);
+       basedealloc(self);
 
        /* Can't reference self beyond this point */
        if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
@@ -393,16 +466,6 @@ subtype_dealloc(PyObject *self)
 
 staticforward PyTypeObject *solid_base(PyTypeObject *type);
 
-typedef struct {
-       PyTypeObject type;
-       PyNumberMethods as_number;
-       PySequenceMethods as_sequence;
-       PyMappingMethods as_mapping;
-       PyBufferProcs as_buffer;
-       PyObject *name, *slots;
-       PyMemberDef members[1];
-} etype;
-
 /* type test with subclassing support */
 
 int
@@ -1142,6 +1205,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
                        if (base->tp_weaklistoffset == 0 &&
                            strcmp(mp->name, "__weakref__") == 0) {
                                mp->type = T_OBJECT;
+                               mp->flags = READONLY;
                                type->tp_weaklistoffset = slotoffset;
                        }
                        slotoffset += sizeof(PyObject *);
@@ -1191,7 +1255,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
        if (type->tp_flags & Py_TPFLAGS_HAVE_GC) {
                type->tp_free = _PyObject_GC_Del;
                type->tp_traverse = subtype_traverse;
-               type->tp_clear = base->tp_clear;
+               type->tp_clear = subtype_clear;
        }
        else
                type->tp_free = _PyObject_Del;