summaryrefslogtreecommitdiffstats
path: root/Objects/object.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2024-04-17 10:08:05 (GMT)
committerGitHub <noreply@github.com>2024-04-17 10:08:05 (GMT)
commit147cd0581e35a10204776029aeaa7fa1901056bc (patch)
tree224715ae49724e26786a6b7a18e2e5c3e4d2deb4 /Objects/object.c
parentc917b3e8e113a3e1ffe118e581fac29eaf365191 (diff)
downloadcpython-147cd0581e35a10204776029aeaa7fa1901056bc.zip
cpython-147cd0581e35a10204776029aeaa7fa1901056bc.tar.gz
cpython-147cd0581e35a10204776029aeaa7fa1901056bc.tar.bz2
GH-117760: Streamline the trashcan mechanism (GH-117763)
Diffstat (limited to 'Objects/object.c')
-rw-r--r--Objects/object.c114
1 files changed, 15 insertions, 99 deletions
diff --git a/Objects/object.c b/Objects/object.c
index 016d0e1..3830db0 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -2709,33 +2709,31 @@ finally:
/* Trashcan support. */
-#define _PyTrash_UNWIND_LEVEL 50
-
/* Add op to the gcstate->trash_delete_later list. Called when the current
* call-stack depth gets large. op must be a currently untracked gc'ed
* object, with refcount 0. Py_DECREF must already have been called on it.
*/
-static void
-_PyTrash_thread_deposit_object(struct _py_trashcan *trash, PyObject *op)
+void
+_PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
{
_PyObject_ASSERT(op, _PyObject_IS_GC(op));
_PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op));
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
#ifdef Py_GIL_DISABLED
_PyObject_ASSERT(op, op->ob_tid == 0);
- op->ob_tid = (uintptr_t)trash->delete_later;
+ op->ob_tid = (uintptr_t)tstate->delete_later;
#else
- _PyGCHead_SET_PREV(_Py_AS_GC(op), (PyGC_Head*)trash->delete_later);
+ _PyGCHead_SET_PREV(_Py_AS_GC(op), (PyGC_Head*)tstate->delete_later);
#endif
- trash->delete_later = op;
+ tstate->delete_later = op;
}
/* Deallocate all the objects in the gcstate->trash_delete_later list.
* Called when the call-stack unwinds again. */
-static void
-_PyTrash_thread_destroy_chain(struct _py_trashcan *trash)
+void
+_PyTrash_thread_destroy_chain(PyThreadState *tstate)
{
- /* We need to increase trash_delete_nesting here, otherwise,
+ /* We need to increase c_recursion_remaining here, otherwise,
_PyTrash_thread_destroy_chain will be called recursively
and then possibly crash. An example that may crash without
increase:
@@ -2746,17 +2744,17 @@ _PyTrash_thread_destroy_chain(struct _py_trashcan *trash)
tups = [(tup,) for tup in tups]
del tups
*/
- assert(trash->delete_nesting == 0);
- ++trash->delete_nesting;
- while (trash->delete_later) {
- PyObject *op = trash->delete_later;
+ assert(tstate->c_recursion_remaining > Py_TRASHCAN_HEADROOM);
+ tstate->c_recursion_remaining--;
+ while (tstate->delete_later) {
+ PyObject *op = tstate->delete_later;
destructor dealloc = Py_TYPE(op)->tp_dealloc;
#ifdef Py_GIL_DISABLED
- trash->delete_later = (PyObject*) op->ob_tid;
+ tstate->delete_later = (PyObject*) op->ob_tid;
op->ob_tid = 0;
#else
- trash->delete_later = (PyObject*) _PyGCHead_PREV(_Py_AS_GC(op));
+ tstate->delete_later = (PyObject*) _PyGCHead_PREV(_Py_AS_GC(op));
#endif
/* Call the deallocator directly. This used to try to
@@ -2767,92 +2765,10 @@ _PyTrash_thread_destroy_chain(struct _py_trashcan *trash)
*/
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
(*dealloc)(op);
- assert(trash->delete_nesting == 1);
- }
- --trash->delete_nesting;
-}
-
-
-static struct _py_trashcan *
-_PyTrash_get_state(PyThreadState *tstate)
-{
- if (tstate != NULL) {
- return &tstate->trash;
- }
- // The current thread must be finalizing.
- // Fall back to using thread-local state.
- // XXX Use thread-local variable syntax?
- assert(PyThread_tss_is_created(&_PyRuntime.trashTSSkey));
- struct _py_trashcan *trash =
- (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey);
- if (trash == NULL) {
- trash = PyMem_RawMalloc(sizeof(struct _py_trashcan));
- if (trash == NULL) {
- Py_FatalError("Out of memory");
- }
- PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)trash);
- }
- return trash;
-}
-
-static void
-_PyTrash_clear_state(PyThreadState *tstate)
-{
- if (tstate != NULL) {
- assert(tstate->trash.delete_later == NULL);
- return;
- }
- if (PyThread_tss_is_created(&_PyRuntime.trashTSSkey)) {
- struct _py_trashcan *trash =
- (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey);
- if (trash != NULL) {
- PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)NULL);
- PyMem_RawFree(trash);
- }
}
+ tstate->c_recursion_remaining++;
}
-
-int
-_PyTrash_begin(PyThreadState *tstate, PyObject *op)
-{
- // XXX Make sure the GIL is held.
- struct _py_trashcan *trash = _PyTrash_get_state(tstate);
- if (trash->delete_nesting >= _PyTrash_UNWIND_LEVEL) {
- /* Store the object (to be deallocated later) and jump past
- * Py_TRASHCAN_END, skipping the body of the deallocator */
- _PyTrash_thread_deposit_object(trash, op);
- return 1;
- }
- ++trash->delete_nesting;
- return 0;
-}
-
-
-void
-_PyTrash_end(PyThreadState *tstate)
-{
- // XXX Make sure the GIL is held.
- struct _py_trashcan *trash = _PyTrash_get_state(tstate);
- --trash->delete_nesting;
- if (trash->delete_nesting <= 0) {
- if (trash->delete_later != NULL) {
- _PyTrash_thread_destroy_chain(trash);
- }
- _PyTrash_clear_state(tstate);
- }
-}
-
-
-/* bpo-40170: It's only be used in Py_TRASHCAN_BEGIN macro to hide
- implementation details. */
-int
-_PyTrash_cond(PyObject *op, destructor dealloc)
-{
- return Py_TYPE(op)->tp_dealloc == dealloc;
-}
-
-
void _Py_NO_RETURN
_PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg,
const char *file, int line, const char *function)