#include "defs.h"
#include "arch-utils.h"
#include "python-internal.h"
+#include "gdbsupport/intrusive_list.h"
#ifdef TUI
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 ();
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)
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 *
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 (),
return 0;
}
+
+/* Finalize this module. */
+
+void
+gdbpy_finalize_tui ()
+{
+ gdbpy_tui_window_maker::invalidate_all ();
+}
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
+ }
+ }
+}