From f6fa494666b0cee5cb33706d9a33b5f9d20b206c Mon Sep 17 00:00:00 2001
From: Nicolas Werner <nicolas.werner@hotmail.de>
Date: Wed, 24 Jun 2020 14:50:11 +0200
Subject: [PATCH] Make connections across threads queued in any case

---
 src/ChatPage.cpp               |  22 ++++--
 src/timeline/TimelineModel.cpp | 134 +++++++++++++++++++--------------
 2 files changed, 94 insertions(+), 62 deletions(-)

diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 3b8af33a5..0b2909276 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -545,8 +545,12 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
                                   emit notificationsRetrieved(std::move(res));
                           });
         });
-        connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync);
-        connect(this, &ChatPage::syncTags, communitiesList_, &CommunitiesList::syncTags);
+        connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync, Qt::QueuedConnection);
+        connect(this,
+                &ChatPage::syncTags,
+                communitiesList_,
+                &CommunitiesList::syncTags,
+                Qt::QueuedConnection);
         connect(
           this, &ChatPage::syncTopBar, this, [this](const std::map<QString, RoomInfo> &updates) {
                   if (updates.find(currentRoom()) != updates.end())
@@ -561,11 +565,15 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
                 user_info_widget_->setDisplayName(name);
         });
 
-        connect(this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync);
-        connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync);
-        connect(this, &ChatPage::tryDelayedSyncCb, this, [this]() {
-                QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync);
-        });
+        connect(
+          this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection);
+        connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync, Qt::QueuedConnection);
+        connect(
+          this,
+          &ChatPage::tryDelayedSyncCb,
+          this,
+          [this]() { QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync); },
+          Qt::QueuedConnection);
 
         connect(this, &ChatPage::dropToLoginPageCb, this, &ChatPage::dropToLoginPage);
 
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 16e4f207e..504c6dcf7 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -144,72 +144,96 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
   , room_id_(room_id)
   , manager_(manager)
 {
+        connect(this,
+                &TimelineModel::oldMessagesRetrieved,
+                this,
+                &TimelineModel::addBackwardsEvents,
+                Qt::QueuedConnection);
         connect(
-          this, &TimelineModel::oldMessagesRetrieved, this, &TimelineModel::addBackwardsEvents);
-        connect(this, &TimelineModel::messageFailed, this, [this](QString txn_id) {
-                nhlog::ui()->error("Failed to send {}, retrying", txn_id.toStdString());
-
-                QTimer::singleShot(5000, this, [this]() { emit nextPendingMessage(); });
-        });
-        connect(this, &TimelineModel::messageSent, this, [this](QString txn_id, QString event_id) {
-                pending.removeOne(txn_id);
+          this,
+          &TimelineModel::messageFailed,
+          this,
+          [this](QString txn_id) {
+                  nhlog::ui()->error("Failed to send {}, retrying", txn_id.toStdString());
 
-                auto ev = events.value(txn_id);
-
-                if (auto reaction =
-                      std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(&ev)) {
-                        QString reactedTo =
-                          QString::fromStdString(reaction->content.relates_to.event_id);
-                        auto &rModel = reactions[reactedTo];
-                        rModel.removeReaction(*reaction);
-                        auto rCopy     = *reaction;
-                        rCopy.event_id = event_id.toStdString();
-                        rModel.addReaction(room_id_.toStdString(), rCopy);
-                }
-
-                int idx = idToIndex(txn_id);
-                if (idx < 0) {
-                        // transaction already received via sync
-                        return;
-                }
-                eventOrder[idx] = event_id;
-                ev              = std::visit(
-                  [event_id](const auto &e) -> mtx::events::collections::TimelineEvents {
-                          auto eventCopy     = e;
-                          eventCopy.event_id = event_id.toStdString();
-                          return eventCopy;
-                  },
-                  ev);
+                  QTimer::singleShot(5000, this, [this]() { emit nextPendingMessage(); });
+          },
+          Qt::QueuedConnection);
+        connect(
+          this,
+          &TimelineModel::messageSent,
+          this,
+          [this](QString txn_id, QString event_id) {
+                  pending.removeOne(txn_id);
+
+                  auto ev = events.value(txn_id);
+
+                  if (auto reaction =
+                        std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(&ev)) {
+                          QString reactedTo =
+                            QString::fromStdString(reaction->content.relates_to.event_id);
+                          auto &rModel = reactions[reactedTo];
+                          rModel.removeReaction(*reaction);
+                          auto rCopy     = *reaction;
+                          rCopy.event_id = event_id.toStdString();
+                          rModel.addReaction(room_id_.toStdString(), rCopy);
+                  }
 
-                events.remove(txn_id);
-                events.insert(event_id, ev);
+                  int idx = idToIndex(txn_id);
+                  if (idx < 0) {
+                          // transaction already received via sync
+                          return;
+                  }
+                  eventOrder[idx] = event_id;
+                  ev              = std::visit(
+                    [event_id](const auto &e) -> mtx::events::collections::TimelineEvents {
+                            auto eventCopy     = e;
+                            eventCopy.event_id = event_id.toStdString();
+                            return eventCopy;
+                    },
+                    ev);
 
-                // mark our messages as read
-                readEvent(event_id.toStdString());
+                  events.remove(txn_id);
+                  events.insert(event_id, ev);
 
-                emit dataChanged(index(idx, 0), index(idx, 0));
+                  // mark our messages as read
+                  readEvent(event_id.toStdString());
 
-                if (pending.size() > 0)
-                        emit nextPendingMessage();
-        });
-        connect(this, &TimelineModel::redactionFailed, this, [](const QString &msg) {
-                emit ChatPage::instance()->showNotification(msg);
-        });
+                  emit dataChanged(index(idx, 0), index(idx, 0));
 
+                  if (pending.size() > 0)
+                          emit nextPendingMessage();
+          },
+          Qt::QueuedConnection);
         connect(
-          this, &TimelineModel::nextPendingMessage, this, &TimelineModel::processOnePendingMessage);
-        connect(this, &TimelineModel::newMessageToSend, this, &TimelineModel::addPendingMessage);
+          this,
+          &TimelineModel::redactionFailed,
+          this,
+          [](const QString &msg) { emit ChatPage::instance()->showNotification(msg); },
+          Qt::QueuedConnection);
 
         connect(this,
-                &TimelineModel::eventFetched,
+                &TimelineModel::nextPendingMessage,
                 this,
-                [this](QString requestingEvent, mtx::events::collections::TimelineEvents event) {
-                        events.insert(QString::fromStdString(mtx::accessors::event_id(event)),
-                                      event);
-                        auto idx = idToIndex(requestingEvent);
-                        if (idx >= 0)
-                                emit dataChanged(index(idx, 0), index(idx, 0));
-                });
+                &TimelineModel::processOnePendingMessage,
+                Qt::QueuedConnection);
+        connect(this,
+                &TimelineModel::newMessageToSend,
+                this,
+                &TimelineModel::addPendingMessage,
+                Qt::QueuedConnection);
+
+        connect(
+          this,
+          &TimelineModel::eventFetched,
+          this,
+          [this](QString requestingEvent, mtx::events::collections::TimelineEvents event) {
+                  events.insert(QString::fromStdString(mtx::accessors::event_id(event)), event);
+                  auto idx = idToIndex(requestingEvent);
+                  if (idx >= 0)
+                          emit dataChanged(index(idx, 0), index(idx, 0));
+          },
+          Qt::QueuedConnection);
 }
 
 QHash<int, QByteArray>
-- 
GitLab