From a653603facde16a475f299428647efb04971e10a Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Mon, 6 Sep 2021 22:06:20 +0200
Subject: [PATCH] UI: Avoid the segfault on MainWindow destruction

We destroy the log console before the final flush, take that into
account => disable handle message for the ConsoleLogSinkDispatcher if
the corresponding LogConsole was destroyed.

This is a quick and dirty solution, but I don't know any other way for
now!
---
 src/Lib/Log.hh         |  7 -------
 src/UI/LogConsole.cc   | 45 +++++++++++++++++++++++++++++++++++++-----
 src/UI/LogConsole.hh   | 35 ++++++++++++++++++++++++++++----
 src/VivyApplication.cc |  6 ++++--
 4 files changed, 75 insertions(+), 18 deletions(-)

diff --git a/src/Lib/Log.hh b/src/Lib/Log.hh
index 063187a9..428fb358 100644
--- a/src/Lib/Log.hh
+++ b/src/Lib/Log.hh
@@ -197,13 +197,6 @@ public:
         return dispatch;
     }
 
-    template <Derived<LogSinkDispatcher> DispatcherType, typename... Args>
-    DispatcherType *newUnmanagedDispatcher(Args &&...args) noexcept
-    {
-        DispatcherType *dispatch = new DispatcherType(std::forward<Args>(args)...);
-        return dispatch;
-    }
-
     std::shared_ptr<Logger> newLogger(const StringType auto &category) noexcept
     {
         std::shared_ptr<Logger> logger = std::make_shared<Logger>(this, category);
diff --git a/src/UI/LogConsole.cc b/src/UI/LogConsole.cc
index 7428a1a6..18363b67 100644
--- a/src/UI/LogConsole.cc
+++ b/src/UI/LogConsole.cc
@@ -2,9 +2,29 @@
 
 using namespace Vivy;
 
-ConsoleLogSinkDispatcher::ConsoleLogSinkDispatcher(QWidget *parent) noexcept
+ConsoleLogSinkDispatcher::ConsoleLogSinkDispatcher() noexcept
     : LogSinkDispatcher(std::string_view{ "console" })
-    , QListWidget(parent)
+{
+}
+
+void
+ConsoleLogSinkDispatcher::handleLogMessage(const std::string_view strv,
+                                           const LogMessage &msg) noexcept
+{
+    if (console)
+        console->handleLogMessage(strv, msg);
+}
+
+void
+ConsoleLogSinkDispatcher::attachLogConsole(LogConsole *widget) noexcept
+{
+    console = widget;
+    if (widget)
+        widget->setDispatcher(shared_from_this());
+}
+
+LogConsole::LogConsole(QWidget *parent) noexcept
+    : QListWidget(parent)
 {
     setSortingEnabled(false);
     setSelectionMode(QAbstractItemView::NoSelection);
@@ -13,8 +33,24 @@ ConsoleLogSinkDispatcher::ConsoleLogSinkDispatcher(QWidget *parent) noexcept
     setStyleSheet(style);
 }
 
+LogConsole::~LogConsole() noexcept
+{
+    if (auto parentDispatcher = dispatcher.lock()) {
+        qDebug() << "Reset parent LogConsole pointer";
+        parentDispatcher->attachLogConsole(nullptr);
+    } else {
+        qCritical() << "Failed to reset LogConsole from parent";
+    }
+}
+
 void
-ConsoleLogSinkDispatcher::setMessageCountLimit(int limit)
+LogConsole::setDispatcher(std::shared_ptr<ConsoleLogSinkDispatcher> ptr) noexcept
+{
+    dispatcher = std::weak_ptr<ConsoleLogSinkDispatcher>(ptr);
+}
+
+void
+LogConsole::setMessageCountLimit(int limit)
 {
     if (limit <= 0)
         throw std::logic_error("Can't pass a negative count!");
@@ -22,8 +58,7 @@ ConsoleLogSinkDispatcher::setMessageCountLimit(int limit)
 }
 
 void
-ConsoleLogSinkDispatcher::handleLogMessage(const std::string_view cat,
-                                           const LogMessage &msg) noexcept
+LogConsole::handleLogMessage(const std::string_view cat, const LogMessage &msg) noexcept
 {
     while (count() >= messageLimit) {
         QListWidgetItem *itemWidget = item(0);
diff --git a/src/UI/LogConsole.hh b/src/UI/LogConsole.hh
index 2525f10e..a388e330 100644
--- a/src/UI/LogConsole.hh
+++ b/src/UI/LogConsole.hh
@@ -5,15 +5,42 @@
 
 namespace Vivy
 {
-class ConsoleLogSinkDispatcher final : public LogSinkDispatcher, public QListWidget {
-    VIVY_UNMOVABLE_OBJECT(ConsoleLogSinkDispatcher)
+class LogConsole;
+class ConsoleLogSinkDispatcher;
+
+class LogConsole final : public QListWidget {
+    VIVY_UNMOVABLE_OBJECT(LogConsole)
 
     int messageLimit{ 1'000 };
+    std::weak_ptr<ConsoleLogSinkDispatcher> dispatcher;
 
 public:
-    explicit ConsoleLogSinkDispatcher(QWidget *parent = nullptr) noexcept;
-    void handleLogMessage(const std::string_view, const LogMessage &) noexcept override;
+    LogConsole(QWidget *parent = nullptr) noexcept;
+    ~LogConsole() noexcept override;
 
     void setMessageCountLimit(int limit);
+    void handleLogMessage(const std::string_view, const LogMessage &) noexcept;
+
+    // Hacky boi to set the `LogConsole *` in the dispatcher to nullptr when
+    // the `LogConsole` is destroyed.
+    void setDispatcher(std::shared_ptr<ConsoleLogSinkDispatcher> ptr) noexcept;
+};
+
+// Log console dispatcher, this is a proxy for the LogConsole which is the real
+// widget, we don't directly set the dispatcher being the widget because the
+// QDockWidget will demete its child on destruction, but the dispatcher is
+// already managed by the std::shared_ptr...
+class ConsoleLogSinkDispatcher final
+    : public LogSinkDispatcher,
+      public std::enable_shared_from_this<ConsoleLogSinkDispatcher> {
+    VIVY_UNMOVABLE_OBJECT(ConsoleLogSinkDispatcher)
+
+    LogConsole *console{ nullptr };
+
+public:
+    explicit ConsoleLogSinkDispatcher() noexcept;
+    void handleLogMessage(const std::string_view, const LogMessage &) noexcept override;
+
+    void attachLogConsole(LogConsole *const) noexcept;
 };
 }
diff --git a/src/VivyApplication.cc b/src/VivyApplication.cc
index de244c0a..f511fe9d 100644
--- a/src/VivyApplication.cc
+++ b/src/VivyApplication.cc
@@ -84,13 +84,15 @@ VivyApplication::exec() noexcept
         // Show the main window, also set up the log console
         mainWindowPtr               = std::make_shared<MainWindow>();
         QDockWidget *logConsoleDock = new QDockWidget("Console", mainWindowPtr.get());
+        LogConsole *logConsole      = new LogConsole(mainWindowPtr.get());
         std::shared_ptr<ConsoleLogSinkDispatcher> consoleLogSinkDispatcher =
-            logSink->newDispatcher<ConsoleLogSinkDispatcher>(nullptr);
+            logSink->newDispatcher<ConsoleLogSinkDispatcher>();
 
         DockWidgetTitleBar::addToDock(logConsoleDock);
+        consoleLogSinkDispatcher->attachLogConsole(logConsole);
         logConsoleDock->setAllowedAreas(Qt::BottomDockWidgetArea);
         logConsoleDock->setFeatures(QDockWidget::DockWidgetMovable);
-        logConsoleDock->setWidget(consoleLogSinkDispatcher.get());
+        logConsoleDock->setWidget(logConsole);
         mainWindowPtr->addDockWidget(Qt::BottomDockWidgetArea, logConsoleDock);
         mainWindowPtr->show();
 
-- 
GitLab