summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2002-07-11 06:23:50 (GMT)
committerTim Peters <tim.peters@gmail.com>2002-07-11 06:23:50 (GMT)
commit3459251d5a2b9589e03e818effc357e5e60ee4a9 (patch)
treedd1a63fc9f5904fb0032b87adbfb5dfd684ce823
parentdc1e70987f49aa23bf1d07f32c476edeba0cec30 (diff)
downloadcpython-3459251d5a2b9589e03e818effc357e5e60ee4a9.zip
cpython-3459251d5a2b9589e03e818effc357e5e60ee4a9.tar.gz
cpython-3459251d5a2b9589e03e818effc357e5e60ee4a9.tar.bz2
object.h special-build macro minefield: renamed all the new lexical
helper macros to something saner, and used them appropriately in other files too, to reduce #ifdef blocks. classobject.c, instance_dealloc(): One of my worst Python Memories is trying to fix this routine a few years ago when COUNT_ALLOCS was defined but Py_TRACE_REFS wasn't. The special-build code here is way too complicated. Now it's much simpler. Difference: in a Py_TRACE_REFS build, the instance is no longer in the doubly-linked list of live objects while its __del__ method is executing, and that may be visible via sys.getobjects() called from a __del__ method. Tough -- the object is presumed dead while its __del__ is executing anyway, and not calling _Py_NewReference() at the start allows enormous code simplification. typeobject.c, call_finalizer(): The special-build instance_dealloc() pain apparently spread to here too via cut-'n-paste, and this is much simpler now too. In addition, I didn't understand why this routine was calling _PyObject_GC_TRACK() after a resurrection, since there's no plausible way _PyObject_GC_UNTRACK() could have been called on the object by this point. I suspect it was left over from pasting the instance_delloc() code. Instead asserted that the object is still tracked. Caution: I suspect we don't have a test that actually exercises the subtype_dealloc() __del__-resurrected-me code.
-rw-r--r--Include/object.h100
-rw-r--r--Objects/classobject.c87
-rw-r--r--Objects/object.c8
-rw-r--r--Objects/stringobject.c4
-rw-r--r--Objects/tupleobject.c4
-rw-r--r--Objects/typeobject.c66
6 files changed, 112 insertions, 157 deletions
diff --git a/Include/object.h b/Include/object.h
index 1f9100e..736095a 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -51,35 +51,10 @@ A standard interface exists for objects that contain an array of items
whose size is determined when the object is allocated.
*/
-#ifdef Py_DEBUG
-/* Turn on aggregate reference counting. This arranges that extern
- * _Py_RefTotal hold a count of all references, the sum of ob_refcnt
- * across all objects. The value can be gotten programatically via
- * sys.gettotalrefcount() (which exists only if Py_REF_DEBUG is enabled).
- * In a debug-mode build, this is where the "8288" comes from in
- *
- * >>> 23
- * 23
- * [8288 refs]
- * >>>
- *
- * Note that if this count increases when you're not storing away new objects,
- * there's probably a leak. Remember, though, that in interactive mode the
- * special name "_" holds a reference to the last result displayed!
- * Py_REF_DEBUG also checks after every decref to verify that the refcount
- * hasn't gone negative, and causes an immediate fatal error if it has.
- */
-#define Py_REF_DEBUG
-
-/* Turn on heavy reference debugging. This is major surgery. Every PyObject
- * grows two more pointers, to maintain a doubly-linked list of all live
- * heap-allocated objects (note that, e.g., most builtin type objects are
- * not in this list, as they're statically allocated). This list can be
- * materialized into a Python list via sys.getobjects() (which exists only
- * if Py_TRACE_REFS is enabled). Py_TRACE_REFS implies Py_REF_DEBUG.
- */
+/* Py_DEBUG implies Py_TRACE_REFS. */
+#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS)
#define Py_TRACE_REFS
-#endif /* Py_DEBUG */
+#endif
/* Py_TRACE_REFS implies Py_REF_DEBUG. */
#if defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
@@ -536,40 +511,45 @@ variable first, both of which are slower; and in a multi-threaded
environment the global variable trick is not safe.)
*/
+/* First define a pile of simple helper macros, one set per special
+ * build symbol. These either expand to the obvious things, or to
+ * nothing at all when the special mode isn't in effect. The main
+ * macros can later be defined just once then, yet expand to different
+ * things depending on which special build options are and aren't in effect.
+ * Trust me <wink>: while painful, this is 20x easier to understand than,
+ * e.g, defining _Py_NewReference five different times in a maze of nested
+ * #ifdefs (we used to do that -- it was impenetrable).
+ */
#ifdef Py_REF_DEBUG
extern DL_IMPORT(long) _Py_RefTotal;
extern DL_IMPORT(void) _Py_NegativeRefcount(const char *fname,
int lineno, PyObject *op);
-#define _PyMAYBE_BUMP_REFTOTAL _Py_RefTotal++
-#define _PyMAYBE_DROP_REFTOTAL _Py_RefTotal--
-#define _PyMAYBE_BUMP_REFTOTAL_COMMA _PyMAYBE_BUMP_REFTOTAL ,
-#define _PyMAYBE_DROP_REFTOTAL_COMMA _PyMAYBE_DROP_REFTOTAL ,
-#define _PyMAYBE_CHECK_REFCNT(OP) \
+#define _Py_INC_REFTOTAL _Py_RefTotal++
+#define _Py_DEC_REFTOTAL _Py_RefTotal--
+#define _Py_REF_DEBUG_COMMA ,
+#define _Py_CHECK_REFCNT(OP) \
{ if ((OP)->ob_refcnt < 0) \
_Py_NegativeRefcount(__FILE__, __LINE__, \
(PyObject *)(OP)); \
}
#else
-#define _PyMAYBE_BUMP_REFTOTAL
-#define _PyMAYBE_DROP_REFTOTAL
-#define _PyMAYBE_BUMP_REFTOTAL_COMMA
-#define _PyMAYBE_DROP_REFTOTAL_COMMA
-#define _PyMAYBE_CHECK_REFCNT(OP) /* a semicolon */;
+#define _Py_INC_REFTOTAL
+#define _Py_DEC_REFTOTAL
+#define _Py_REF_DEBUG_COMMA
+#define _Py_CHECK_REFCNT(OP) /* a semicolon */;
#endif /* Py_REF_DEBUG */
#ifdef COUNT_ALLOCS
extern DL_IMPORT(void) inc_count(PyTypeObject *);
-#define _PyMAYBE_BUMP_COUNT(OP) inc_count((OP)->ob_type)
-#define _PyMAYBE_BUMP_FREECOUNT(OP) (OP)->ob_type->tp_frees++
-#define _PyMAYBE_DROP_FREECOUNT(OP) (OP)->ob_type->tp_frees--
-#define _PyMAYBE_BUMP_COUNT_COMMA(OP) _PyMAYBE_BUMP_COUNT(OP) ,
-#define _PyMAYBE_BUMP_FREECOUNT_COMMA(OP) _PyMAYBE_BUMP_FREECOUNT(OP) ,
+#define _Py_INC_TPALLOCS(OP) inc_count((OP)->ob_type)
+#define _Py_INC_TPFREES(OP) (OP)->ob_type->tp_frees++
+#define _Py_DEC_TPFREES(OP) (OP)->ob_type->tp_frees--
+#define _Py_COUNT_ALLOCS_COMMA ,
#else
-#define _PyMAYBE_BUMP_COUNT(OP)
-#define _PyMAYBE_BUMP_FREECOUNT(OP)
-#define _PyMAYBE_DROP_FREECOUNT(OP)
-#define _PyMAYBE_BUMP_COUNT_COMMA(OP)
-#define _PyMAYBE_BUMP_FREECOUNT_COMMA(OP)
+#define _Py_INC_TPALLOCS(OP)
+#define _Py_INC_TPFREES(OP)
+#define _Py_DEC_TPFREES(OP)
+#define _Py_COUNT_ALLOCS_COMMA
#endif /* COUNT_ALLOCS */
#ifdef Py_TRACE_REFS
@@ -584,27 +564,27 @@ extern DL_IMPORT(void) _Py_ResetReferences(void);
/* Without Py_TRACE_REFS, there's little enough to do that we expand code
* inline.
*/
-#define _Py_NewReference(op) ( \
- _PyMAYBE_BUMP_COUNT_COMMA(op) \
- _PyMAYBE_BUMP_REFTOTAL_COMMA \
+#define _Py_NewReference(op) ( \
+ _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA \
+ _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
(op)->ob_refcnt = 1)
-#define _Py_ForgetReference(op) _PyMAYBE_BUMP_FREECOUNT(op)
+#define _Py_ForgetReference(op) _Py_INC_TPFREES(op)
#define _Py_Dealloc(op) ( \
- _PyMAYBE_BUMP_FREECOUNT_COMMA(op) \
+ _Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA \
(*(op)->ob_type->tp_dealloc)((PyObject *)(op)))
#endif /* !Py_TRACE_REFS */
-#define Py_INCREF(op) ( \
- _PyMAYBE_BUMP_REFTOTAL_COMMA \
+#define Py_INCREF(op) ( \
+ _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
(op)->ob_refcnt++)
-#define Py_DECREF(op) \
- if (_PyMAYBE_DROP_REFTOTAL_COMMA \
- --(op)->ob_refcnt != 0) \
- _PyMAYBE_CHECK_REFCNT(op) \
- else \
+#define Py_DECREF(op) \
+ if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \
+ --(op)->ob_refcnt != 0) \
+ _Py_CHECK_REFCNT(op) \
+ else \
_Py_Dealloc((PyObject *)(op))
/* Macros to use in case the object pointer may be NULL: */
diff --git a/Objects/classobject.c b/Objects/classobject.c
index 90a0e22..32e8d2a 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -615,31 +615,15 @@ instance_dealloc(register PyInstanceObject *inst)
PyObject *error_type, *error_value, *error_traceback;
PyObject *del;
static PyObject *delstr;
-#ifdef Py_REF_DEBUG
- extern long _Py_RefTotal;
-#endif
+
_PyObject_GC_UNTRACK(inst);
if (inst->in_weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) inst);
/* Temporarily resurrect the object. */
-#ifdef Py_TRACE_REFS
-#ifndef Py_REF_DEBUG
-# error "Py_TRACE_REFS defined but Py_REF_DEBUG not."
-#endif
- /* much too complicated if Py_TRACE_REFS defined */
- inst->ob_type = &PyInstance_Type;
- _Py_NewReference((PyObject *)inst);
-#ifdef COUNT_ALLOCS
- /* compensate for boost in _Py_NewReference; note that
- * _Py_RefTotal was also boosted; we'll knock that down later.
- */
- inst->ob_type->tp_allocs--;
-#endif
-#else /* !Py_TRACE_REFS */
- /* Py_INCREF boosts _Py_RefTotal if Py_REF_DEBUG is defined */
- Py_INCREF(inst);
-#endif /* !Py_TRACE_REFS */
+ assert(inst->ob_type == &PyInstance_Type);
+ assert(inst->ob_refcnt == 0);
+ inst->ob_refcnt = 1;
/* Save the current exception, if any. */
PyErr_Fetch(&error_type, &error_value, &error_traceback);
@@ -656,32 +640,37 @@ instance_dealloc(register PyInstanceObject *inst)
}
/* 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.
*/
-#ifdef Py_REF_DEBUG
- /* _Py_RefTotal was boosted either by _Py_NewReference or
- * Py_INCREF above.
- */
- _Py_RefTotal--;
-#endif
- if (--inst->ob_refcnt > 0) {
-#ifdef COUNT_ALLOCS
- inst->ob_type->tp_frees--;
-#endif
- _PyObject_GC_TRACK(inst);
- return; /* __del__ added a reference; don't delete now */
+ assert(inst->ob_refcnt > 0);
+ if (--inst->ob_refcnt == 0) {
+ Py_DECREF(inst->in_class);
+ Py_XDECREF(inst->in_dict);
+ PyObject_GC_Del(inst);
}
-#ifdef Py_TRACE_REFS
- _Py_ForgetReference((PyObject *)inst);
+ else {
+ int refcnt = inst->ob_refcnt;
+ /* __del__ resurrected it! Make it look like the original
+ * Py_DECREF never happened.
+ */
+ _Py_NewReference((PyObject *)inst);
+ inst->ob_refcnt = refcnt;
+ _PyObject_GC_TRACK(inst);
+ /* 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
- /* compensate for increment in _Py_ForgetReference */
- inst->ob_type->tp_frees--;
-#endif
+ --inst->ob_type->tp_frees;
+ --inst->ob_type->tp_allocs;
#endif
- Py_DECREF(inst->in_class);
- Py_XDECREF(inst->in_dict);
- PyObject_GC_Del(inst);
+ }
}
static PyObject *
@@ -1097,7 +1086,7 @@ sliceobj_from_intint(int i, int j)
start = PyInt_FromLong((long)i);
if (!start)
return NULL;
-
+
end = PyInt_FromLong((long)j);
if (!end) {
Py_DECREF(start);
@@ -1131,9 +1120,9 @@ instance_slice(PyInstanceObject *inst, int i, int j)
if (func == NULL)
return NULL;
arg = Py_BuildValue("(N)", sliceobj_from_intint(i, j));
- } else
+ } else
arg = Py_BuildValue("(ii)", i, j);
-
+
if (arg == NULL) {
Py_DECREF(func);
return NULL;
@@ -1266,7 +1255,7 @@ instance_contains(PyInstanceObject *inst, PyObject *member)
res = PyEval_CallObject(func, arg);
Py_DECREF(func);
Py_DECREF(arg);
- if(res == NULL)
+ if(res == NULL)
return -1;
ret = PyObject_IsTrue(res);
Py_DECREF(res);
@@ -1339,7 +1328,7 @@ static PyObject *coerce_obj;
/* Try one half of a binary operator involving a class instance. */
static PyObject *
-half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc,
+half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc,
int swapped)
{
PyObject *args;
@@ -1347,7 +1336,7 @@ half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc,
PyObject *coerced = NULL;
PyObject *v1;
PyObject *result;
-
+
if (!PyInstance_Check(v)) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
@@ -1708,7 +1697,7 @@ bin_power(PyObject *v, PyObject *w)
/* This version is for ternary calls only (z != None) */
static PyObject *
instance_pow(PyObject *v, PyObject *w, PyObject *z)
-{
+{
if (z == Py_None) {
return do_binop(v, w, "__pow__", "__rpow__", bin_power);
}
@@ -1777,7 +1766,7 @@ instance_ipow(PyObject *v, PyObject *w, PyObject *z)
#define NAME_OPS 6
static PyObject **name_op = NULL;
-static int
+static int
init_name_op(void)
{
int i;
@@ -1818,7 +1807,7 @@ half_richcompare(PyObject *v, PyObject *w, int op)
instance_getattr2 directly because it will not set an
exception on failure. */
if (((PyInstanceObject *)v)->in_class->cl_getattr == NULL) {
- method = instance_getattr2((PyInstanceObject *)v,
+ method = instance_getattr2((PyInstanceObject *)v,
name_op[op]);
if (method == NULL) {
assert(!PyErr_Occurred());
diff --git a/Objects/object.c b/Objects/object.c
index 4987ff3..19f9a7a 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -7,7 +7,7 @@
#include "macglue.h"
#endif
-#if defined( Py_TRACE_REFS ) || defined( Py_REF_DEBUG )
+#ifdef Py_REF_DEBUG
DL_IMPORT(long) _Py_RefTotal;
#endif
@@ -1876,13 +1876,13 @@ _Py_ResetReferences(void)
void
_Py_NewReference(PyObject *op)
{
- _Py_RefTotal++;
+ _Py_INC_REFTOTAL;
op->ob_refcnt = 1;
op->_ob_next = refchain._ob_next;
op->_ob_prev = &refchain;
refchain._ob_next->_ob_prev = op;
refchain._ob_next = op;
- _PyMAYBE_BUMP_COUNT(op);
+ _Py_INC_TPALLOCS(op);
}
void
@@ -1907,7 +1907,7 @@ _Py_ForgetReference(register PyObject *op)
op->_ob_next->_ob_prev = op->_ob_prev;
op->_ob_prev->_ob_next = op->_ob_next;
op->_ob_next = op->_ob_prev = NULL;
- _PyMAYBE_BUMP_FREECOUNT(op);
+ _Py_INC_TPFREES(op);
}
void
diff --git a/Objects/stringobject.c b/Objects/stringobject.c
index 5d6478b..0543f80 100644
--- a/Objects/stringobject.c
+++ b/Objects/stringobject.c
@@ -3060,9 +3060,7 @@ _PyString_Resize(PyObject **pv, int newsize)
return -1;
}
/* XXX UNREF/NEWREF interface should be more symmetrical */
-#ifdef Py_REF_DEBUG
- --_Py_RefTotal;
-#endif
+ _Py_DEC_REFTOTAL;
_Py_ForgetReference(v);
*pv = (PyObject *)
PyObject_REALLOC((char *)v,
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 4c264fd..1a15bb7 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -677,9 +677,7 @@ _PyTuple_Resize(PyObject **pv, int newsize)
}
/* XXX UNREF/NEWREF interface should be more symmetrical */
-#ifdef Py_REF_DEBUG
- --_Py_RefTotal;
-#endif
+ _Py_DEC_REFTOTAL;
_PyObject_GC_UNTRACK(v);
_Py_ForgetReference((PyObject *) v);
/* DECREF items deleted by shrinkage */
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 2beb3b3..a7afa9b 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -365,22 +365,8 @@ call_finalizer(PyObject *self)
PyObject *error_type, *error_value, *error_traceback;
/* Temporarily resurrect the object. */
-#ifdef Py_TRACE_REFS
-#ifndef Py_REF_DEBUG
-# error "Py_TRACE_REFS defined but Py_REF_DEBUG not."
-#endif
- /* much too complicated if Py_TRACE_REFS defined */
- _Py_NewReference((PyObject *)self);
-#ifdef COUNT_ALLOCS
- /* compensate for boost in _Py_NewReference; note that
- * _Py_RefTotal was also boosted; we'll knock that down later.
- */
- self->ob_type->tp_allocs--;
-#endif
-#else /* !Py_TRACE_REFS */
- /* Py_INCREF boosts _Py_RefTotal if Py_REF_DEBUG is defined */
- Py_INCREF(self);
-#endif /* !Py_TRACE_REFS */
+ assert(self->ob_refcnt == 0);
+ self->ob_refcnt = 1;
/* Save the current exception, if any. */
PyErr_Fetch(&error_type, &error_value, &error_traceback);
@@ -402,28 +388,32 @@ call_finalizer(PyObject *self)
/* Undo the temporary resurrection; can't use DECREF here, it would
* cause a recursive call.
*/
-#ifdef Py_REF_DEBUG
- /* _Py_RefTotal was boosted either by _Py_NewReference or
- * Py_INCREF above.
+ 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(_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.
*/
- _Py_RefTotal--;
-#endif
- if (--self->ob_refcnt > 0) {
-#ifdef COUNT_ALLOCS
- self->ob_type->tp_frees--;
-#endif
- _PyObject_GC_TRACK(self);
- return -1; /* __del__ added a reference; don't delete now */
- }
-#ifdef Py_TRACE_REFS
- _Py_ForgetReference((PyObject *)self);
#ifdef COUNT_ALLOCS
- /* compensate for increment in _Py_ForgetReference */
- self->ob_type->tp_frees--;
+ --self->ob_type->tp_frees;
+ --self->ob_type->tp_allocs;
#endif
-#endif
-
- return 0;
+ return -1; /* __del__ added a reference; don't delete now */
}
static void
@@ -1387,13 +1377,13 @@ type_getattro(PyTypeObject *type, PyObject *name)
/* No readable descriptor found yet */
meta_get = NULL;
-
+
/* Look for the attribute in the metatype */
meta_attribute = _PyType_Lookup(metatype, name);
if (meta_attribute != NULL) {
meta_get = meta_attribute->ob_type->tp_descr_get;
-
+
if (meta_get != NULL && PyDescr_IsData(meta_attribute)) {
/* Data descriptors implement tp_descr_set to intercept
* writes. Assume the attribute is not overridden in
@@ -1416,7 +1406,7 @@ type_getattro(PyTypeObject *type, PyObject *name)
return local_get(attribute, (PyObject *)NULL,
(PyObject *)type);
}
-
+
Py_INCREF(attribute);
return attribute;
}