From 418f497d996892ec16a31d3e282c4996e8677b3a Mon Sep 17 00:00:00 2001 From: Shane Synan Date: Mon, 5 Sep 2016 14:07:51 -0500 Subject: [PATCH 1/1] Process events when destroying CoreNetwork During shutdown, process events in CoreNetwork, and wait for the socket to disconnect. This fixes the QUIT command not getting sent to IRC networks. Examples [Unreal 3.2] > Before <-- dcircuit_dev (quasseldev@hostmask.IP) has quit (Input/output error) > After <-- dcircuit_dev (quasseldev@hostmask.IP) has quit (Quit: My Message!) [Freenode] > Before <-- dcircuit_dev (~quasselde@hostmask) has quit (Remote host closed the connection) > After <-- dcircuit_dev (~quasselde@hostmask) has quit (Quit: My Message!) Where "My Message!" is specified in Configure Quassel -> IRC -> Identities -> Advanced -> Quit Reason Note: Freenode hides quit messages from clients that disconnect soon after connecting. Stay connected ~10 minutes before testing QUIT. (cherry picked from commit 59ed0127591f946a68a6ee7f30b23deb37d26821) --- src/core/corenetwork.cpp | 30 ++++++++++++++++++++++++++++-- src/core/corenetwork.h | 11 +++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 65850c3e..ac69044d 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -90,13 +90,39 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) CoreNetwork::~CoreNetwork() { - if (connectionState() != Disconnected && connectionState() != Network::Reconnecting) - disconnectFromIrc(false); // clean up, but this does not count as requested disconnect! + // Request a proper disconnect, but don't count as user-requested disconnect + if (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. + disconnectFromIrc(false, QString(), false, true); + // 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(); + // Wait briefly for each network to disconnect. Sometimes it takes a little while to send. + if (!forceDisconnect()) { + qWarning() << "Timed out quitting network" << networkName() << + "(user ID " << userId() << ")"; + } + } disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up delete _userInputHandler; } +bool CoreNetwork::forceDisconnect(int msecs) +{ + if (socket.state() == QAbstractSocket::UnconnectedState) { + // Socket already disconnected. + return true; + } + // Request a socket-level disconnect if not already happened + socket.disconnectFromHost(); + // Return the result of waiting for disconnect; true if successful, otherwise false + return socket.waitForDisconnected(msecs); +} + + QString CoreNetwork::channelDecode(const QString &bufferName, const QByteArray &string) const { if (!bufferName.isEmpty()) { diff --git a/src/core/corenetwork.h b/src/core/corenetwork.h index 51eb31a8..5a41b983 100644 --- a/src/core/corenetwork.h +++ b/src/core/corenetwork.h @@ -127,6 +127,17 @@ public slots: void disconnectFromIrc(bool requested = true, const QString &reason = QString(), bool withReconnect = false, bool forceImmediate = false); + /** + * Forcibly close the IRC server socket, waiting for it to close. + * + * Call CoreNetwork::disconnectFromIrc() first, allow the event loop to run, then if you need to + * be sure the network's disconencted (e.g. clean-up), call this. + * + * @param msecs Maximum time to wait for socket to close, in milliseconds. + * @return True if socket closes successfully; false if error occurs or timeout reached + */ + bool forceDisconnect(int msecs = 1000); + void userInput(BufferInfo bufferInfo, QString msg); /** -- 2.20.1