Attempt to improve the /LIST timeout for large lists
authorMatt Schatz <genius3000@g3k.solutions>
Sun, 1 Jul 2018 05:16:03 +0000 (23:16 -0600)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sun, 15 Jul 2018 22:15:39 +0000 (00:15 +0200)
Prior to this, the timer was simply running for 10 seconds from the
start of the query; which could timeout even while data was being received.
Large channel lists (i.e. Freenode) would over-run into the status buffer.

Now we utilize QBasicTimer to create a timer that can be easily
restarted everytime we receive a RPL_LIST. Inital timeout is 5 seconds
and 5 seconds after each RPL_LIST. We also now remove the timer after
receiving a RPL_LISTEND instead of letting it tick.

src/core/coreirclisthelper.cpp
src/core/coreirclisthelper.h

index 9bdf4c4..1c4bfde 100644 (file)
@@ -23,6 +23,8 @@
 #include "corenetwork.h"
 #include "coreuserinputhandler.h"
 
+constexpr auto kTimeoutMs = 5000;
+
 INIT_SYNCABLE_OBJECT(CoreIrcListHelper)
 QVariantList CoreIrcListHelper::requestChannelList(const NetworkId &netId, const QStringList &channelFilters)
 {
@@ -45,6 +47,9 @@ bool CoreIrcListHelper::addChannel(const NetworkId &netId, const QString &channe
         return false;
 
     _channelLists[netId] << ChannelDescription(channelName, userCount, topic);
+    if (_queryTimeoutByNetId.contains(netId))
+        _queryTimeoutByNetId[netId]->start(kTimeoutMs, this);
+
     return true;
 }
 
@@ -55,7 +60,12 @@ bool CoreIrcListHelper::dispatchQuery(const NetworkId &netId, const QString &que
     if (network) {
         _channelLists[netId] = QList<ChannelDescription>();
         network->userInputHandler()->handleList(BufferInfo(), query);
-        _queryTimeout[startTimer(10000)] = netId;
+
+        auto timer = std::make_shared<QBasicTimer>();
+        timer->start(kTimeoutMs, this);
+        _queryTimeoutByNetId[netId] = timer;
+        _queryTimeoutByTimerId[timer->timerId()] = netId;
+
         return true;
     }
     else {
@@ -66,6 +76,12 @@ bool CoreIrcListHelper::dispatchQuery(const NetworkId &netId, const QString &que
 
 bool CoreIrcListHelper::endOfChannelList(const NetworkId &netId)
 {
+    if (_queryTimeoutByNetId.contains(netId)) {
+        // If we recieved an actual RPL_LISTEND, remove the timer
+        int timerId = _queryTimeoutByNetId.take(netId)->timerId();
+        _queryTimeoutByTimerId.remove(timerId);
+    }
+
     if (_queuedQuery.contains(netId)) {
         // we're no longer interessted in the current data. drop it and issue a new request.
         return dispatchQuery(netId, _queuedQuery.take(netId));
@@ -92,8 +108,14 @@ bool CoreIrcListHelper::endOfChannelList(const NetworkId &netId)
 
 void CoreIrcListHelper::timerEvent(QTimerEvent *event)
 {
-    int timerId = event->timerId();
-    killTimer(timerId);
-    NetworkId netId = _queryTimeout.take(timerId);
+    if (!_queryTimeoutByTimerId.contains(event->timerId())) {
+        IrcListHelper::timerEvent(event);
+        return;
+    }
+
+    NetworkId netId = _queryTimeoutByTimerId.take(event->timerId());
+    _queryTimeoutByNetId.remove(netId);
+
+    event->accept();
     endOfChannelList(netId);
 }
index 9247c8d..8f1081c 100644 (file)
 #ifndef COREIRCLISTHELPER_H
 #define COREIRCLISTHELPER_H
 
+#include <memory>
+
 #include "irclisthelper.h"
 
 #include "coresession.h"
 
+class QBasicTimer;
 class QTimerEvent;
 
 class CoreIrcListHelper : public IrcListHelper
@@ -58,7 +61,8 @@ private:
     QHash<NetworkId, QString> _queuedQuery;
     QHash<NetworkId, QList<ChannelDescription> > _channelLists;
     QHash<NetworkId, QVariantList> _finishedChannelLists;
-    QHash<int, NetworkId> _queryTimeout;
+    QHash<int, NetworkId> _queryTimeoutByTimerId;
+    QHash<NetworkId, std::shared_ptr<QBasicTimer>> _queryTimeoutByNetId;
 };