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 <psychon@znc.in>
This commit is contained in:
Uli Schlachter
2012-02-04 19:53:03 +01:00
parent 4d1e97ed47
commit 95d6041018
+19 -13
View File
@@ -1827,27 +1827,33 @@ public:
protected:
virtual void RunJob() {
list<CIRCNetwork*>& ConnectionQueue = CZNC::Get().GetConnectionQueue();
list<CIRCNetwork*> ConnectionQueue;
list<CIRCNetwork*>& 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<CIRCNetwork*>::iterator end = ConnectionQueue.end();
list<CIRCNetwork*>::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();
}