Christian Tismer's "trashcan" patch:
authorGuido van Rossum <guido@python.org>
Mon, 13 Mar 2000 16:01:29 +0000 (16:01 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 13 Mar 2000 16:01:29 +0000 (16:01 +0000)
Added wrapping macros to dictobject.c, listobject.c, tupleobject.c,
frameobject.c, traceback.c that safely prevends core dumps
on stack overflow. Macros and functions in object.c, object.h.
The method is an "elevator destructor" that turns cascading
deletes into tail recursive behavior when some limit is hit.

Include/object.h
Objects/dictobject.c
Objects/frameobject.c
Objects/listobject.c
Objects/object.c
Objects/tupleobject.c
Python/traceback.c

index f718509b2a622713491d679aa38ac49b86366f0e..243de295e4f1084e9ee900a1bfd386203e05a081 100644 (file)
@@ -514,6 +514,53 @@ it carefully, it may save lots of calls to Py_INCREF() and Py_DECREF() at
 times.
 */
 
+/*
+  trashcan
+  CT 2k0130
+  non-recursively destroy nested objects
+
+  CT 2k0223
+  redefinition for better locality and less overhead.
+
+  Objects that want to be recursion safe need to use
+  the macroes 
+               Py_TRASHCAN_SAFE_BEGIN(name)
+  and
+               Py_TRASHCAN_SAFE_END(name)
+  surrounding their actual deallocation code.
+
+  It would be nice to do this using the thread state.
+  Also, we could do an exact stack measure then.
+  Unfortunately, deallocations also take place when
+  the thread state is undefined.
+*/
+
+#define PyTrash_UNWIND_LEVEL 50
+
+#define Py_TRASHCAN_SAFE_BEGIN(op) \
+       { \
+               ++_PyTrash_delete_nesting; \
+               if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL) { \
+
+#define Py_TRASHCAN_SAFE_END(op) \
+               ;} \
+               else \
+                       _PyTrash_deposit_object((PyObject*)op);\
+               --_PyTrash_delete_nesting; \
+               if (_PyTrash_delete_later && _PyTrash_delete_nesting <= 0) \
+                       _PyTrash_destroy_list(); \
+       } \
+
+extern DL_IMPORT(void) _PyTrash_deposit_object Py_PROTO((PyObject*));
+extern DL_IMPORT(void) _PyTrash_destroy_list Py_PROTO(());
+
+extern DL_IMPORT(int) _PyTrash_delete_nesting;
+extern DL_IMPORT(PyObject *) _PyTrash_delete_later;
+
+/* swap the "xx" to check the speed loss */
+
+#define xxPy_TRASHCAN_SAFE_BEGIN(op) 
+#define xxPy_TRASHCAN_SAFE_END(op) ;
 #ifdef __cplusplus
 }
 #endif
index ceec39da0ed3e94f19159508dc1b6253b03b8d57..ea32e235b496ba3bbceca9159e879ce6cd29b8e9 100644 (file)
@@ -479,6 +479,7 @@ dict_dealloc(mp)
 {
        register int i;
        register dictentry *ep;
+       Py_TRASHCAN_SAFE_BEGIN(mp)
        for (i = 0, ep = mp->ma_table; i < mp->ma_size; i++, ep++) {
                if (ep->me_key != NULL) {
                        Py_DECREF(ep->me_key);
@@ -489,6 +490,7 @@ dict_dealloc(mp)
        }
        PyMem_XDEL(mp->ma_table);
        PyMem_DEL(mp);
+       Py_TRASHCAN_SAFE_END(mp)
 }
 
 static int
index dcd760cc78a784ffb5564e14d174bc686773880f..4c716cdcd6cae34539b3a77635ab10164dfb92b7 100644 (file)
@@ -103,6 +103,7 @@ frame_dealloc(f)
        int i;
        PyObject **fastlocals;
 
+       Py_TRASHCAN_SAFE_BEGIN(f)
        /* Kill all local variables */
        fastlocals = f->f_localsplus;
        for (i = f->f_nlocals; --i >= 0; ++fastlocals) {
@@ -120,6 +121,7 @@ frame_dealloc(f)
        Py_XDECREF(f->f_exc_traceback);
        f->f_back = free_list;
        free_list = f;
+       Py_TRASHCAN_SAFE_END(f)
 }
 
 PyTypeObject PyFrame_Type = {
index d77b546edb881da88c451557cf2dc095ad13ecdc..673028f31bb18b7522b5a2c74538ec4f44c977cd 100644 (file)
@@ -215,6 +215,7 @@ list_dealloc(op)
        PyListObject *op;
 {
        int i;
+       Py_TRASHCAN_SAFE_BEGIN(op)
        if (op->ob_item != NULL) {
                /* Do it backwards, for Christian Tismer.
                   There's a simple test case where somehow this reduces
@@ -227,6 +228,7 @@ list_dealloc(op)
                free((ANY *)op->ob_item);
        }
        free((ANY *)op);
+       Py_TRASHCAN_SAFE_END(op)
 }
 
 static int
index 7a41aa2e2fe3ff3e0355f02f83695ad48b27c591..d2aa5428d4c29a60800bea653f391108ba586636 100644 (file)
@@ -906,3 +906,48 @@ Py_ReprLeave(obj)
                }
        }
 }
+
+/*
+  trashcan
+  CT 2k0130
+  non-recursively destroy nested objects
+
+  CT 2k0223
+  everything is now done in a macro.
+
+  CT 2k0305
+  modified to use functions, after Tim Peter's suggestion.
+
+  CT 2k0309
+  modified to restore a possible error.
+*/
+
+int _PyTrash_delete_nesting = 0;
+PyObject * _PyTrash_delete_later = NULL;
+
+void
+_PyTrash_deposit_object(op)
+       PyObject *op;
+{
+       PyObject *error_type, *error_value, *error_traceback;
+       PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+       if (!_PyTrash_delete_later)
+               _PyTrash_delete_later = PyList_New(0);
+       if (_PyTrash_delete_later)
+               PyList_Append(_PyTrash_delete_later, (PyObject *)op);
+
+       PyErr_Restore(error_type, error_value, error_traceback);
+}
+
+void
+_PyTrash_destroy_list()
+{
+       while (_PyTrash_delete_later) {
+               PyObject *shredder = _PyTrash_delete_later;
+               _PyTrash_delete_later = NULL;
+               ++_PyTrash_delete_nesting;
+               Py_DECREF(shredder);
+               --_PyTrash_delete_nesting;
+       }
+}
index 568c4b3a162a2ffc5d7c078662149ff15abaf712..5112468d95c0e344d25da45a733c7cfcfe05d2bd 100644 (file)
@@ -172,6 +172,7 @@ tupledealloc(op)
 {
        register int i;
 
+       Py_TRASHCAN_SAFE_BEGIN(op)
        if (op->ob_size > 0) {
                i = op->ob_size;
                while (--i >= 0)
@@ -180,11 +181,13 @@ tupledealloc(op)
                if (op->ob_size < MAXSAVESIZE) {
                        op->ob_item[0] = (PyObject *) free_tuples[op->ob_size];
                        free_tuples[op->ob_size] = op;
-                       return;
+                       goto done; /* return */
                }
 #endif
        }
        free((ANY *)op);
+done:
+       Py_TRASHCAN_SAFE_END(op)
 }
 
 static int
index ca77eaa62a69cad4edfffb64243b1e30aad38aae..e03551d6c18b642f5ba94e0d1c48f8565b1babdd 100644 (file)
@@ -68,9 +68,11 @@ static void
 tb_dealloc(tb)
        tracebackobject *tb;
 {
+       Py_TRASHCAN_SAFE_BEGIN(tb)
        Py_XDECREF(tb->tb_next);
        Py_XDECREF(tb->tb_frame);
        PyMem_DEL(tb);
+       Py_TRASHCAN_SAFE_END(tb)
 }
 
 #define Tracebacktype PyTraceBack_Type