diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2012-09-05 23:17:42 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2012-09-05 23:17:42 (GMT) |
commit | 5b4faae30748c09930fa053442e1d6ff2823823c (patch) | |
tree | 8372fb8975488a6cb3fb5295ef52badfb31aa93d /Objects | |
parent | 11946fbe804d99d26724e65dcb061cda6666c4e9 (diff) | |
parent | 56cd62c04a7fbd9d923de000e6e6734cf8ac9ddc (diff) | |
download | cpython-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.c | 37 | ||||
-rw-r--r-- | Objects/typeobject.c | 5 |
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: |