core: Fix quitting from networks on shutdown
[quassel.git] / src / core / coresession.cpp
index 8d08d5b..c38c3d6 100644 (file)
@@ -158,53 +158,30 @@ void CoreSession::shutdown()
 {
     saveSessionState();
 
-    /* Why partially duplicate CoreNetwork destructor?  When each CoreNetwork quits in the
-     * destructor, disconnections are processed in sequence for each object.  For many IRC servers
-     * on a slow network, this could significantly delay core shutdown [msecs wait * network count].
-     *
-     * Here, CoreSession first calls disconnect on all networks, letting them all start
-     * disconnecting before beginning to sequentially wait for each network.  Ideally, after the
-     * first network is disconnected, the other networks will have already closed.  Worst-case may
-     * still wait [msecs wait time * num. of networks], but the risk should be much lower.
-     *
-     * CoreNetwork should still do cleanup in its own destructor in case a network is deleted
-     * outside of deleting the whole CoreSession.
-     *
-     * If this proves to be problematic in the future, there's an alternative Qt signal-based system
-     * implemented in another pull request that guarentees a maximum amount of time to disconnect,
-     * but at the cost of more complex code.
-     *
-     * See https://github.com/quassel/quassel/pull/203
-     */
-
-    foreach(CoreNetwork *net, _networks.values()) {
-        // Request each network properly disconnect, but don't count as user-requested disconnect
-        if (net->socketConnected()) {
-            // Only try if the socket's fully connected (not initializing or disconnecting).
-            // Force an immediate disconnect, jumping the command queue.  Ensures the proper QUIT is
-            // shown even if other messages are queued.
-            net->disconnectFromIrc(false, QString(), false, true);
+    // Request disconnect from all connected networks in parallel, and wait until every network
+    // has emitted the disconnected() signal before deleting the session itself
+    for (CoreNetwork *net : _networks.values()) {
+        if (net->socketState() != QAbstractSocket::UnconnectedState) {
+            _networksPendingDisconnect.insert(net->networkId());
+            connect(net, SIGNAL(disconnected(NetworkId)), this, SLOT(onNetworkDisconnected(NetworkId)));
+            net->shutdown();
         }
     }
 
-    // Process the putCmd events that trigger the quit.  Without this, shutting down the core
-    // results in abrubtly closing the socket rather than sending the QUIT as expected.
-    QCoreApplication::processEvents();
-
-    foreach(CoreNetwork *net, _networks.values()) {
-        // Wait briefly for each network to disconnect.  Sometimes it takes a little while to send.
-        if (!net->forceDisconnect()) {
-            qWarning() << "Timed out quitting network" << net->networkName() <<
-                          "(user ID " << net->userId() << ")";
-        }
-        // Delete the network now that it's closed
-        delete net;
+    if (_networksPendingDisconnect.isEmpty()) {
+        // Nothing to do, suicide so the core can shut down
+        deleteLater();
     }
+}
 
-    _networks.clear();
 
-    // Suicide
-    deleteLater();
+void CoreSession::onNetworkDisconnected(NetworkId networkId)
+{
+    _networksPendingDisconnect.remove(networkId);
+    if (_networksPendingDisconnect.isEmpty()) {
+        // We're done, suicide so the core can shut down
+        deleteLater();
+    }
 }