summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2002-08-08 20:55:20 (GMT)
committerGuido van Rossum <guido@python.org>2002-08-08 20:55:20 (GMT)
commitfebd61dc02f0edbe9aa746fabde8ce97fbbeabdb (patch)
tree3bba4c0481fd8fd11777e93b1b46e5579638c5aa
parent12e3c710db23ea9370c3bc54a5f0a2c262ffd0cc (diff)
downloadcpython-febd61dc02f0edbe9aa746fabde8ce97fbbeabdb.zip
cpython-febd61dc02f0edbe9aa746fabde8ce97fbbeabdb.tar.gz
cpython-febd61dc02f0edbe9aa746fabde8ce97fbbeabdb.tar.bz2
A modest speedup of object deallocation. call_finalizer() did rather
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.)
-rw-r--r--Include/object.h1
-rw-r--r--Objects/typeobject.c136
2 files changed, 71 insertions, 66 deletions
diff --git a/Include/object.h b/Include/object.h
index 19460fe..d045be1 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -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 */
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index d494344..edf4e70 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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}
};