From 95d60410186ed8311014d605ad02171c1fa54de3 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 4 Feb 2012 19:53:03 +0100 Subject: [PATCH] Fix a busy loop with the connection queue If connecting to a server failed without needing any time for DNS, the connect timer would busy loop over the networks, because a network re-inserted itself into the queue and the timer would try the network again. Fix this by moving the connection queue to a separate instance of std::list when the timer fires. From then on, we just iterate through that list while networks which want to try again add themselves to the "real" connection queue instead. We only have to make sure that any networks that are left in the old connection queue after the timer is done get prepended to the "real" connection queue. Signed-off-by: Uli Schlachter --- src/znc.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/znc.cpp b/src/znc.cpp index f403d5aa..3bf58a27 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -1827,27 +1827,33 @@ public: protected: virtual void RunJob() { - list& ConnectionQueue = CZNC::Get().GetConnectionQueue(); + list ConnectionQueue; + list& RealConnectionQueue = CZNC::Get().GetConnectionQueue(); - /* We store the end of the queue, so CIRCNetwork::Connect() can add - * itself back to the queue and we wont end up in an infinite loop. */ - list::iterator end = ConnectionQueue.end(); - list::iterator it; + // Problem: If a network can't connect right now because e.g. it + // is throttled, it will re-insert itself into the connection + // queue. However, we must only give each network a single + // chance during this timer run. + // + // Solution: We move the connection queue to our local list at + // the beginning and work from that. + ConnectionQueue.swap(RealConnectionQueue); - for (it = ConnectionQueue.begin(); it != end;) { - CIRCNetwork *pNetwork = *it; - - /* We must erase the network from the queue before we try to connect - * because it may try to add the network to the queue (which would - * fail if we were already in the queue) */ - it = ConnectionQueue.erase(it); + while (!ConnectionQueue.empty()) { + CIRCNetwork *pNetwork = ConnectionQueue.front(); + ConnectionQueue.pop_front(); if (pNetwork->Connect()) { break; } } - if (ConnectionQueue.empty()) { + /* Now re-insert anything that is left in our local list into + * the real connection queue. + */ + RealConnectionQueue.splice(RealConnectionQueue.begin(), ConnectionQueue); + + if (RealConnectionQueue.empty()) { DEBUG("ConnectQueueTimer done"); CZNC::Get().DisableConnectQueue(); }