Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e92e610

Browse files
committed
Christian Tismer -- total rewrite on trashcan code.
Improvements: - does no longer need any extra memory - has no relationship to tstate - works in debug mode - can easily be modified for free threading (hi Greg:) Side effects: Trashcan does change the order of object destruction. Prevending that would be quite an immense effort, as my attempts have shown. This version works always the same, with debug mode or not. The slightly changed destruction order should therefore be no problem. Algorithm: While the old idea of delaying the destruction of some obejcts at a certain recursion level was kept, we now no longer aloocate an object to hold these objects. The delayed objects are instead chained together via their ob_type field. The type is encoded via ob_refcnt. When it comes to the destruction of the chain of waiting objects, the topmost object is popped off the chain and revived with type and refcount 1, then it gets a normal Py_DECREF. I am confident that this solution is near optimum for minimizing side effects and code bloat.
1 parent db575db commit e92e610

2 files changed

Lines changed: 69 additions & 17 deletions

File tree

Include/object.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,11 @@ times.
536536
Also, we could do an exact stack measure then.
537537
Unfortunately, deallocations also take place when
538538
the thread state is undefined.
539+
540+
CT 2k0422 complete rewrite.
541+
There is no need to allocate new objects.
542+
Everything is done vialob_refcnt and ob_type now.
543+
Adding support for free-threading should be easy, too.
539544
*/
540545

541546
#define PyTrash_UNWIND_LEVEL 50
@@ -551,11 +556,11 @@ times.
551556
_PyTrash_deposit_object((PyObject*)op);\
552557
--_PyTrash_delete_nesting; \
553558
if (_PyTrash_delete_later && _PyTrash_delete_nesting <= 0) \
554-
_PyTrash_destroy_list(); \
559+
_PyTrash_destroy_chain(); \
555560
} \
556561

557562
extern DL_IMPORT(void) _PyTrash_deposit_object Py_PROTO((PyObject*));
558-
extern DL_IMPORT(void) _PyTrash_destroy_list Py_PROTO(());
563+
extern DL_IMPORT(void) _PyTrash_destroy_chain Py_PROTO(());
559564

560565
extern DL_IMPORT(int) _PyTrash_delete_nesting;
561566
extern DL_IMPORT(PyObject *) _PyTrash_delete_later;
@@ -564,6 +569,7 @@ extern DL_IMPORT(PyObject *) _PyTrash_delete_later;
564569

565570
#define xxPy_TRASHCAN_SAFE_BEGIN(op)
566571
#define xxPy_TRASHCAN_SAFE_END(op) ;
572+
567573
#ifdef __cplusplus
568574
}
569575
#endif

Objects/object.c

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ PERFORMANCE OF THIS SOFTWARE.
3333

3434
#include "Python.h"
3535

36+
/* just for trashcan: */
37+
#include "compile.h"
38+
#include "frameobject.h"
39+
#include "traceback.h"
40+
3641
#if defined( Py_TRACE_REFS ) || defined( Py_REF_DEBUG )
3742
DL_IMPORT(long) _Py_RefTotal;
3843
#endif
@@ -822,7 +827,8 @@ _Py_Dealloc(op)
822827
{
823828
destructor dealloc = op->ob_type->tp_dealloc;
824829
_Py_ForgetReference(op);
825-
op->ob_type = NULL;
830+
if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL-1)
831+
op->ob_type = NULL;
826832
(*dealloc)(op);
827833
}
828834

@@ -1045,37 +1051,77 @@ Py_ReprLeave(obj)
10451051
10461052
CT 2k0325
10471053
added better safe than sorry check for threadstate
1054+
1055+
CT 2k0422
1056+
complete rewrite. We now build a chain via ob_type
1057+
and save the limited number of types in ob_refcnt.
1058+
This is perfect since we don't need any memory.
1059+
A patch for free-threading would need just a lock.
10481060
*/
10491061

1062+
#define Py_TRASHCAN_TUPLE 1
1063+
#define Py_TRASHCAN_LIST 2
1064+
#define Py_TRASHCAN_DICT 3
1065+
#define Py_TRASHCAN_FRAME 4
1066+
#define Py_TRASHCAN_TRACEBACK 5
1067+
/* extend here if other objects want protection */
1068+
10501069
int _PyTrash_delete_nesting = 0;
1070+
10511071
PyObject * _PyTrash_delete_later = NULL;
10521072

10531073
void
10541074
_PyTrash_deposit_object(op)
10551075
PyObject *op;
10561076
{
1057-
PyObject *error_type, *error_value, *error_traceback;
1058-
1059-
if (PyThreadState_GET() != NULL)
1060-
PyErr_Fetch(&error_type, &error_value, &error_traceback);
1061-
1062-
if (!_PyTrash_delete_later)
1063-
_PyTrash_delete_later = PyList_New(0);
1064-
if (_PyTrash_delete_later)
1065-
PyList_Append(_PyTrash_delete_later, (PyObject *)op);
1066-
1067-
if (PyThreadState_GET() != NULL)
1068-
PyErr_Restore(error_type, error_value, error_traceback);
1077+
int typecode;
1078+
PyObject *hold = _PyTrash_delete_later;
1079+
1080+
if (PyTuple_Check(op))
1081+
typecode = Py_TRASHCAN_TUPLE;
1082+
else if (PyList_Check(op))
1083+
typecode = Py_TRASHCAN_LIST;
1084+
else if (PyDict_Check(op))
1085+
typecode = Py_TRASHCAN_DICT;
1086+
else if (PyFrame_Check(op))
1087+
typecode = Py_TRASHCAN_FRAME;
1088+
else if (PyTraceBack_Check(op))
1089+
typecode = Py_TRASHCAN_TRACEBACK;
1090+
op->ob_refcnt = typecode;
1091+
1092+
op->ob_type = (PyTypeObject*)_PyTrash_delete_later;
1093+
_PyTrash_delete_later = op;
10691094
}
10701095

10711096
void
1072-
_PyTrash_destroy_list()
1097+
_PyTrash_destroy_chain()
10731098
{
10741099
while (_PyTrash_delete_later) {
10751100
PyObject *shredder = _PyTrash_delete_later;
1076-
_PyTrash_delete_later = NULL;
1101+
_PyTrash_delete_later = (PyObject*) shredder->ob_type;
1102+
1103+
switch (shredder->ob_refcnt) {
1104+
case Py_TRASHCAN_TUPLE:
1105+
shredder->ob_type = &PyTuple_Type;
1106+
break;
1107+
case Py_TRASHCAN_LIST:
1108+
shredder->ob_type = &PyList_Type;
1109+
break;
1110+
case Py_TRASHCAN_DICT:
1111+
shredder->ob_type = &PyDict_Type;
1112+
break;
1113+
case Py_TRASHCAN_FRAME:
1114+
shredder->ob_type = &PyFrame_Type;
1115+
break;
1116+
case Py_TRASHCAN_TRACEBACK:
1117+
shredder->ob_type = &PyTraceBack_Type;
1118+
break;
1119+
}
1120+
_Py_NewReference(shredder);
1121+
10771122
++_PyTrash_delete_nesting;
10781123
Py_DECREF(shredder);
10791124
--_PyTrash_delete_nesting;
10801125
}
10811126
}
1127+

0 commit comments

Comments
 (0)