From b1f28580798aefe37beae60e6a3d31a342cf7645 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Fri, 28 Feb 2020 11:09:42 -0800 Subject: [PATCH 1/2] Remove memory leaks, with this patch it doesn't leak anymore (Windows Visual Studio, checked using Visual Leak Detector, https://kinddragon.github.io/vld) This patch uses Py_AtExit to make sure it is called at the very end. It may conflict with other platforms/configurations (embedded etc). --- include/pybind11/detail/internals.h | 19 +++++++++++++++++++ include/pybind11/pybind11.h | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 6224dfb226..a265e19cb9 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -212,6 +212,24 @@ inline internals **&get_internals_pp() { return internals_pp; } +inline void delete_internals_pp() { + detail::internals** internals_ptr_ptr = get_internals_pp(); + if (internals_ptr_ptr) { + auto*& internals_ptr = *internals_ptr_ptr; + auto elem = internals_ptr->registered_types_cpp.begin(); + while (elem != internals_ptr->registered_types_cpp.end()) { + type_info* ti = elem->second; + delete ti; + elem++; + } + internals_ptr->registered_types_cpp.clear(); + delete internals_ptr; + delete internals_ptr_ptr; + internals_ptr_ptr = nullptr; + } +} + + inline void translate_exception(std::exception_ptr p) { try { if (p) std::rethrow_exception(p); @@ -269,6 +287,7 @@ PYBIND11_NOINLINE inline internals &get_internals() { (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); #endif } else { + Py_AtExit(delete_internals_pp); if (!internals_pp) internals_pp = new internals*(); auto *&internals_ptr = *internals_pp; internals_ptr = new internals(); diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index d95d61f7bb..24b250de02 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -794,7 +794,8 @@ class module : public object { explicit module(const char *name, const char *doc = nullptr) { if (!options::show_user_defined_docstrings()) doc = nullptr; #if PY_MAJOR_VERSION >= 3 - PyModuleDef *def = new PyModuleDef(); + static PyModuleDef def2; + PyModuleDef* def = &def2; std::memset(def, 0, sizeof(PyModuleDef)); def->m_name = name; def->m_doc = doc; From 72bfd812e4c19ec87b976a2762079cdb32b27abf Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Fri, 28 Feb 2020 11:39:07 -0800 Subject: [PATCH 2/2] undo the static PyModuleDef part (re-introducing 1 leak) --- include/pybind11/pybind11.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 24b250de02..d95d61f7bb 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -794,8 +794,7 @@ class module : public object { explicit module(const char *name, const char *doc = nullptr) { if (!options::show_user_defined_docstrings()) doc = nullptr; #if PY_MAJOR_VERSION >= 3 - static PyModuleDef def2; - PyModuleDef* def = &def2; + PyModuleDef *def = new PyModuleDef(); std::memset(def, 0, sizeof(PyModuleDef)); def->m_name = name; def->m_doc = doc;