summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2012-09-05 23:17:42 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2012-09-05 23:17:42 (GMT)
commit5b4faae30748c09930fa053442e1d6ff2823823c (patch)
tree8372fb8975488a6cb3fb5295ef52badfb31aa93d /Objects
parent11946fbe804d99d26724e65dcb061cda6666c4e9 (diff)
parent56cd62c04a7fbd9d923de000e6e6734cf8ac9ddc (diff)
downloadcpython-5b4faae30748c09930fa053442e1d6ff2823823c.zip
cpython-5b4faae30748c09930fa053442e1d6ff2823823c.tar.gz
cpython-5b4faae30748c09930fa053442e1d6ff2823823c.tar.bz2
Issue #13992: The trashcan mechanism is now thread-safe. This eliminates
sporadic crashes in multi-thread programs when several long deallocator chains ran concurrently and involved subclasses of built-in container types. Note that the trashcan functions are part of the stable ABI, therefore they have to be kept around for binary compatibility of extensions.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/object.c37
-rw-r--r--Objects/typeobject.c5
2 files changed, 42 insertions, 0 deletions
diff --git a/Objects/object.c b/Objects/object.c
index f4c0208..f417184 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1954,6 +1954,18 @@ _PyTrash_deposit_object(PyObject *op)
_PyTrash_delete_later = op;
}
+/* The equivalent API, using per-thread state recursion info */
+void
+_PyTrash_thread_deposit_object(PyObject *op)
+{
+ PyThreadState *tstate = PyThreadState_GET();
+ assert(PyObject_IS_GC(op));
+ assert(_Py_AS_GC(op)->gc.gc_refs == _PyGC_REFS_UNTRACKED);
+ assert(op->ob_refcnt == 0);
+ _Py_AS_GC(op)->gc.gc_prev = (PyGC_Head *) tstate->trash_delete_later;
+ tstate->trash_delete_later = op;
+}
+
/* Dealloccate all the objects in the _PyTrash_delete_later list. Called when
* the call-stack unwinds again.
*/
@@ -1980,6 +1992,31 @@ _PyTrash_destroy_chain(void)
}
}
+/* The equivalent API, using per-thread state recursion info */
+void
+_PyTrash_thread_destroy_chain(void)
+{
+ PyThreadState *tstate = PyThreadState_GET();
+ while (tstate->trash_delete_later) {
+ PyObject *op = tstate->trash_delete_later;
+ destructor dealloc = Py_TYPE(op)->tp_dealloc;
+
+ tstate->trash_delete_later =
+ (PyObject*) _Py_AS_GC(op)->gc.gc_prev;
+
+ /* Call the deallocator directly. This used to try to
+ * fool Py_DECREF into calling it indirectly, but
+ * Py_DECREF was already called on this object, and in
+ * assorted non-release builds calling Py_DECREF again ends
+ * up distorting allocation statistics.
+ */
+ assert(op->ob_refcnt == 0);
+ ++tstate->trash_delete_nesting;
+ (*dealloc)(op);
+ --tstate->trash_delete_nesting;
+ }
+}
+
#ifndef Py_TRACE_REFS
/* For Py_LIMITED_API, we need an out-of-line version of _Py_Dealloc.
Define this here, so we can undefine the macro. */
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index bc01b0d..bd55ede 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -891,6 +891,7 @@ subtype_dealloc(PyObject *self)
{
PyTypeObject *type, *base;
destructor basedealloc;
+ PyThreadState *tstate = PyThreadState_GET();
/* Extract the type; we expect it to be a heap type */
type = Py_TYPE(self);
@@ -940,8 +941,10 @@ subtype_dealloc(PyObject *self)
/* See explanation at end of function for full disclosure */
PyObject_GC_UnTrack(self);
++_PyTrash_delete_nesting;
+ ++ tstate->trash_delete_nesting;
Py_TRASHCAN_SAFE_BEGIN(self);
--_PyTrash_delete_nesting;
+ -- tstate->trash_delete_nesting;
/* DO NOT restore GC tracking at this point. weakref callbacks
* (if any, and whether directly here or indirectly in something we
* call) may trigger GC, and if self is tracked at that point, it
@@ -1020,8 +1023,10 @@ subtype_dealloc(PyObject *self)
endlabel:
++_PyTrash_delete_nesting;
+ ++ tstate->trash_delete_nesting;
Py_TRASHCAN_SAFE_END(self);
--_PyTrash_delete_nesting;
+ -- tstate->trash_delete_nesting;
/* Explanation of the weirdness around the trashcan macros: