summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2012-09-05 22:59:49 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2012-09-05 22:59:49 (GMT)
commit56cd62c04a7fbd9d923de000e6e6734cf8ac9ddc (patch)
treeed5ce8c8953b5f8f6ed136a94629f631083f2594 /Objects
parent1d857453b7065dafdc34a72c1bbb2a993782b383 (diff)
downloadcpython-56cd62c04a7fbd9d923de000e6e6734cf8ac9ddc.zip
cpython-56cd62c04a7fbd9d923de000e6e6734cf8ac9ddc.tar.gz
cpython-56cd62c04a7fbd9d923de000e6e6734cf8ac9ddc.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. Because of this change, a couple extension modules compiled for 3.2.4 (those which use the trashcan mechanism, despite it being undocumented) will not be loadable by 3.2.3 and earlier. However, extension modules compiled for 3.2.3 and earlier will be loadable by 3.2.4.
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 84ec2f3..4730a66 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1876,6 +1876,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.
*/
@@ -1902,6 +1914,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 adb69cb..1bda375 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -847,6 +847,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);
@@ -896,8 +897,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
@@ -976,8 +979,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: