]> granicus.if.org Git - python/commitdiff
A modest speedup of object deallocation. call_finalizer() did rather
authorGuido van Rossum <guido@python.org>
Thu, 8 Aug 2002 20:55:20 +0000 (20:55 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 8 Aug 2002 20:55:20 +0000 (20:55 +0000)
a lot of work: it had to save and restore the current exception around
a call to lookup_maybe(), because that could fail in rare cases, and
most objects don't have a __del__ method, so the whole exercise was
usually a waste of time.  Changed this to cache the __del__ method in
the type object just like all other special methods, in a new slot
tp_del.  So now subtype_dealloc() can test whether tp_del is NULL and
skip the whole exercise if it is.  The new slot doesn't need a new
flag bit: subtype_dealloc() is only called if the type was dynamically
allocated by type_new(), so it's guaranteed to have all current slots.
Types defined in C cannot fill in tp_del with a function of their own,
so there's no corresponding "wrapper".  (That functionality is already
available through tp_dealloc.)

Include/object.h
Objects/typeobject.c

index 19460fe381c646f64cf40089aafacfe729831efa..d045be16a0c75b2b99057a689fd16232783788a5 100644 (file)
@@ -315,6 +315,7 @@ typedef struct _typeobject {
        PyObject *tp_cache;
        PyObject *tp_subclasses;
        PyObject *tp_weaklist;
+       destructor tp_del;
 
 #ifdef COUNT_ALLOCS
        /* these must be last and never explicitly initialized */
index d49434448ab6996839f4c0c3abc73b6c6a083b52..edf4e7044d6d66e75ccf10c91c387baadfa6dc56 100644 (file)
@@ -356,68 +356,6 @@ subtype_clear(PyObject *self)
        return 0;
 }
 
-static PyObject *lookup_maybe(PyObject *, char *, PyObject **);
-
-static int
-call_finalizer(PyObject *self)
-{
-       static PyObject *del_str = NULL;
-       PyObject *del, *res;
-       PyObject *error_type, *error_value, *error_traceback;
-
-       /* Temporarily resurrect the object. */
-       assert(self->ob_refcnt == 0);
-       self->ob_refcnt = 1;
-
-       /* Save the current exception, if any. */
-       PyErr_Fetch(&error_type, &error_value, &error_traceback);
-
-       /* Execute __del__ method, if any. */
-       del = lookup_maybe(self, "__del__", &del_str);
-       if (del != NULL) {
-               res = PyEval_CallObject(del, NULL);
-               if (res == NULL)
-                       PyErr_WriteUnraisable(del);
-               else
-                       Py_DECREF(res);
-               Py_DECREF(del);
-       }
-
-       /* Restore the saved exception. */
-       PyErr_Restore(error_type, error_value, error_traceback);
-
-       /* Undo the temporary resurrection; can't use DECREF here, it would
-        * cause a recursive call.
-        */
-       assert(self->ob_refcnt > 0);
-       if (--self->ob_refcnt == 0)
-               return 0;       /* this is the normal path out */
-
-       /* __del__ resurrected it!  Make it look like the original Py_DECREF
-        * never happened.
-        */
-       {
-               int refcnt = self->ob_refcnt;
-               _Py_NewReference(self);
-               self->ob_refcnt = refcnt;
-       }
-       assert(!PyType_IS_GC(self->ob_type) ||
-              _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
-       /* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal, but
-        * _Py_NewReference bumped it again, so that's a wash.
-        * If Py_TRACE_REFS, _Py_NewReference re-added self to the object
-        * chain, so no more to do there either.
-        * If COUNT_ALLOCS, the original decref bumped tp_frees, and
-        * _Py_NewReference bumped tp_allocs:  both of those need to be
-        * undone.
-        */
-#ifdef COUNT_ALLOCS
-       --self->ob_type->tp_frees;
-       --self->ob_type->tp_allocs;
-#endif
-       return -1; /* __del__ added a reference; don't delete now */
-}
-
 static void
 subtype_dealloc(PyObject *self)
 {
@@ -438,8 +376,11 @@ subtype_dealloc(PyObject *self)
                   clear_slots(), or DECREF the dict, or clear weakrefs. */
 
                /* Maybe call finalizer; exit early if resurrected */
-               if (call_finalizer(self) < 0)
-                       return;
+               if (type->tp_del) {
+                       type->tp_del(self);
+                       if (self->ob_refcnt > 0)
+                               return;
+               }
 
                /* Find the nearest base with a different tp_dealloc */
                base = type;
@@ -468,8 +409,11 @@ subtype_dealloc(PyObject *self)
        _PyObject_GC_TRACK(self); /* We'll untrack for real later */
 
        /* Maybe call finalizer; exit early if resurrected */
-       if (call_finalizer(self) < 0)
-               goto endlabel;
+       if (type->tp_del) {
+               type->tp_del(self);
+               if (self->ob_refcnt > 0)
+                       goto endlabel;
+       }
 
        /* Find the nearest base with a different tp_dealloc
           and clear slots while we're at it */
@@ -3721,6 +3665,65 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        return x;
 }
 
+static void
+slot_tp_del(PyObject *self)
+{
+       static PyObject *del_str = NULL;
+       PyObject *del, *res;
+       PyObject *error_type, *error_value, *error_traceback;
+
+       /* Temporarily resurrect the object. */
+       assert(self->ob_refcnt == 0);
+       self->ob_refcnt = 1;
+
+       /* Save the current exception, if any. */
+       PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+       /* Execute __del__ method, if any. */
+       del = lookup_maybe(self, "__del__", &del_str);
+       if (del != NULL) {
+               res = PyEval_CallObject(del, NULL);
+               if (res == NULL)
+                       PyErr_WriteUnraisable(del);
+               else
+                       Py_DECREF(res);
+               Py_DECREF(del);
+       }
+
+       /* Restore the saved exception. */
+       PyErr_Restore(error_type, error_value, error_traceback);
+
+       /* Undo the temporary resurrection; can't use DECREF here, it would
+        * cause a recursive call.
+        */
+       assert(self->ob_refcnt > 0);
+       if (--self->ob_refcnt == 0)
+               return; /* this is the normal path out */
+
+       /* __del__ resurrected it!  Make it look like the original Py_DECREF
+        * never happened.
+        */
+       {
+               int refcnt = self->ob_refcnt;
+               _Py_NewReference(self);
+               self->ob_refcnt = refcnt;
+       }
+       assert(!PyType_IS_GC(self->ob_type) ||
+              _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
+       /* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal, but
+        * _Py_NewReference bumped it again, so that's a wash.
+        * If Py_TRACE_REFS, _Py_NewReference re-added self to the object
+        * chain, so no more to do there either.
+        * If COUNT_ALLOCS, the original decref bumped tp_frees, and
+        * _Py_NewReference bumped tp_allocs:  both of those need to be
+        * undone.
+        */
+#ifdef COUNT_ALLOCS
+       --self->ob_type->tp_frees;
+       --self->ob_type->tp_allocs;
+#endif
+}
+
 
 /* Table mapping __foo__ names to tp_foo offsets and slot_tp_foo wrapper
    functions.  The offsets here are relative to the 'etype' structure, which
@@ -3949,6 +3952,7 @@ static slotdef slotdefs[] = {
               "see x.__class__.__doc__ for signature",
               PyWrapperFlag_KEYWORDS),
        TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""),
+       TPSLOT("__del__", tp_del, slot_tp_del, NULL, ""),
        {NULL}
 };