Skip to content

Commit 05576f1

Browse files
committed
Use weakrefs
1 parent b147430 commit 05576f1

File tree

2 files changed

+30
-14
lines changed

2 files changed

+30
-14
lines changed

include/pybind11/detail/class.h

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@
1111

1212
#include <pybind11/attr.h>
1313
#include <pybind11/options.h>
14+
#include <pybind11/pybind11.h>
15+
#include <pybind11/pytypes.h>
1416

1517
#include "exception_translation.h"
1618

1719
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
20+
21+
class weakref;
22+
class cpp_function;
23+
1824
PYBIND11_NAMESPACE_BEGIN(detail)
1925

2026
#if !defined(PYPY_VERSION)
@@ -252,11 +258,6 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
252258
});
253259

254260
PyType_Type.tp_dealloc(obj);
255-
256-
// Release the references to the internals capsules that were acquired in make_new_python_type.
257-
// See the comment there for details on preventing use-after-free during interpreter shutdown.
258-
Py_XDECREF(get_internals_capsule());
259-
Py_XDECREF(get_local_internals_capsule());
260261
}
261262

262263
/** This metaclass is assigned by default to all pybind11 types and is required in order
@@ -834,15 +835,6 @@ inline PyObject *make_new_python_type(const type_record &rec) {
834835

835836
PYBIND11_SET_OLDPY_QUALNAME(type, qualname);
836837

837-
// Prevent use-after-free during interpreter shutdown. GC order is not guaranteed, so the
838-
// internals capsule may be destroyed (resetting internals via internals_shutdown) before all
839-
// pybind11 types are destroyed. If a type's tp_traverse/tp_clear then calls py::cast, it
840-
// would recreate an empty internals and fail because the type registry is gone. By holding
841-
// references to the capsules, we ensure they outlive all pybind11 types. The decref happens
842-
// in pybind11_meta_dealloc.
843-
Py_XINCREF(get_internals_capsule());
844-
Py_XINCREF(get_local_internals_capsule());
845-
846838
return reinterpret_cast<PyObject *>(type);
847839
}
848840

include/pybind11/pybind11.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,30 @@ class generic_type : public object {
16921692

16931693
m_ptr = make_new_python_type(rec);
16941694

1695+
// Prevent use-after-free during interpreter shutdown. GC order is not guaranteed, so the
1696+
// internals capsule may be destroyed (resetting internals via internals_shutdown) before
1697+
// all pybind11 types are destroyed. If a type's tp_traverse/tp_clear then calls py::cast,
1698+
// it would recreate an empty internals and fail because the type registry is gone. By
1699+
// holding references to the capsules, we ensure they outlive all pybind11 types.
1700+
// We use weakrefs on the type with a cpp_function callback. When the type is destroyed,
1701+
// Python will call the callback which releases the capsule reference and the weakref.
1702+
if (PyObject *capsule = get_internals_capsule()) {
1703+
Py_INCREF(capsule);
1704+
(void) weakref(handle(m_ptr), cpp_function([](handle prevent_release) -> void {
1705+
Py_XDECREF(get_internals_capsule());
1706+
prevent_release.dec_ref();
1707+
}))
1708+
.release();
1709+
}
1710+
if (PyObject *capsule = get_local_internals_capsule()) {
1711+
Py_INCREF(capsule);
1712+
(void) weakref(handle(m_ptr), cpp_function([](handle prevent_release) -> void {
1713+
Py_XDECREF(get_local_internals_capsule());
1714+
prevent_release.dec_ref();
1715+
}))
1716+
.release();
1717+
}
1718+
16951719
/* Register supplemental type information in C++ dict */
16961720
auto *tinfo = new detail::type_info();
16971721
tinfo->type = reinterpret_cast<PyTypeObject *>(m_ptr);

0 commit comments

Comments
 (0)