aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/python/py-tui.c52
-rw-r--r--gdb/python/python-internal.h1
-rw-r--r--gdb/python/python.c1
-rw-r--r--gdb/testsuite/gdb.python/tui-window-factory.exp32
4 files changed, 84 insertions, 2 deletions
diff --git a/gdb/python/py-tui.c b/gdb/python/py-tui.c
index e9c9101..9ce7665 100644
--- a/gdb/python/py-tui.c
+++ b/gdb/python/py-tui.c
@@ -21,6 +21,7 @@
#include "defs.h"
#include "arch-utils.h"
#include "python-internal.h"
+#include "gdbsupport/intrusive_list.h"
#ifdef TUI
@@ -268,12 +269,14 @@ tui_py_window::output (const char *text, bool full_window)
user-supplied window constructor. */
class gdbpy_tui_window_maker
+ : public intrusive_list_node<gdbpy_tui_window_maker>
{
public:
explicit gdbpy_tui_window_maker (gdbpy_ref<> &&constr)
: m_constr (std::move (constr))
{
+ m_window_maker_list.push_back (*this);
}
~gdbpy_tui_window_maker ();
@@ -281,12 +284,14 @@ public:
gdbpy_tui_window_maker (gdbpy_tui_window_maker &&other) noexcept
: m_constr (std::move (other.m_constr))
{
+ m_window_maker_list.push_back (*this);
}
gdbpy_tui_window_maker (const gdbpy_tui_window_maker &other)
{
gdbpy_enter enter_py;
m_constr = other.m_constr;
+ m_window_maker_list.push_back (*this);
}
gdbpy_tui_window_maker &operator= (gdbpy_tui_window_maker &&other)
@@ -304,16 +309,43 @@ public:
tui_win_info *operator() (const char *name);
+ /* Reset the m_constr field of all gdbpy_tui_window_maker objects back to
+ nullptr, this will allow the Python object referenced to be
+ deallocated. This function is intended to be called when GDB is
+ shutting down the Python interpreter to allow all Python objects to be
+ deallocated and cleaned up. */
+ static void
+ invalidate_all ()
+ {
+ gdbpy_enter enter_py;
+ for (gdbpy_tui_window_maker &f : m_window_maker_list)
+ f.m_constr.reset (nullptr);
+ }
+
private:
/* A constructor that is called to make a TUI window. */
gdbpy_ref<> m_constr;
+
+ /* A global list of all gdbpy_tui_window_maker objects. */
+ static intrusive_list<gdbpy_tui_window_maker> m_window_maker_list;
};
+/* See comment in class declaration above. */
+
+intrusive_list<gdbpy_tui_window_maker>
+ gdbpy_tui_window_maker::m_window_maker_list;
+
gdbpy_tui_window_maker::~gdbpy_tui_window_maker ()
{
- gdbpy_enter enter_py;
- m_constr.reset (nullptr);
+ /* Remove this gdbpy_tui_window_maker from the global list. */
+ m_window_maker_list.erase (m_window_maker_list.iterator_to (*this));
+
+ if (m_constr != nullptr)
+ {
+ gdbpy_enter enter_py;
+ m_constr.reset (nullptr);
+ }
}
tui_win_info *
@@ -332,6 +364,14 @@ gdbpy_tui_window_maker::operator() (const char *win_name)
std::unique_ptr<tui_py_window> window
(new tui_py_window (win_name, wrapper));
+ /* There's only two ways that m_constr can be reset back to nullptr,
+ first when the parent gdbpy_tui_window_maker object is deleted, in
+ which case it should be impossible to call this method, or second, as
+ a result of a gdbpy_tui_window_maker::invalidate_all call, but this is
+ only called when GDB's Python interpreter is being shut down, after
+ which, this method should not be called. */
+ gdb_assert (m_constr != nullptr);
+
gdbpy_ref<> user_window
(PyObject_CallFunctionObjArgs (m_constr.get (),
(PyObject *) wrapper.get (),
@@ -572,3 +612,11 @@ gdbpy_initialize_tui ()
return 0;
}
+
+/* Finalize this module. */
+
+void
+gdbpy_finalize_tui ()
+{
+ gdbpy_tui_window_maker::invalidate_all ();
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index c41a43b..8fb0979 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -550,6 +550,7 @@ int gdbpy_initialize_unwind (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_tui ()
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+void gdbpy_finalize_tui ();
int gdbpy_initialize_membuf ()
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_connection ()
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 54623f4..ed466cc 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1950,6 +1950,7 @@ finalize_python (void *ignore)
gdbpy_enter::finalize ();
gdbpy_finalize_micommands ();
+ gdbpy_finalize_tui ();
Py_Finalize ();
diff --git a/gdb/testsuite/gdb.python/tui-window-factory.exp b/gdb/testsuite/gdb.python/tui-window-factory.exp
index 99f9fbb..3e898d0 100644
--- a/gdb/testsuite/gdb.python/tui-window-factory.exp
+++ b/gdb/testsuite/gdb.python/tui-window-factory.exp
@@ -71,3 +71,35 @@ with_test_prefix "msg_3" {
Term::check_box_contents "check test_window box" 0 0 80 15 \
"TestWindow \\(msg_3\\)"
}
+
+# Restart GDB, setup a TUI window factory, and then check that the
+# Python object is deallocated when GDB exits.
+with_test_prefix "call __del__ at exit" {
+ clean_restart
+
+ gdb_test "source ${pyfile}" "Python script imported" \
+ "import python scripts"
+
+ gdb_test "python register_window_factory('msg_1')" \
+ "Entering TestWindowFactory\\.__init__: msg_1"
+
+ gdb_test "python register_window_factory('msg_2')" \
+ [multi_line \
+ "Entering TestWindowFactory\\.__init__: msg_2" \
+ "Entering TestWindowFactory\\.__del__: msg_1"]
+
+ set saw_window_factory_del 0
+ gdb_test_multiple "quit" "" {
+ -re "^quit\r\n" {
+ exp_continue
+ }
+ -re "^Entering TestWindowFactory.__del__: msg_2\r\n" {
+ incr saw_window_factory_del
+ exp_continue
+ }
+ eof {
+ gdb_assert { $saw_window_factory_del == 1 }
+ pass $gdb_test_name
+ }
+ }
+}