mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 14:02:06 +01:00
SMACK-278: Call socket.close() in XMPPConnection.shutdown() before
closing the reader and writer streams. This basically prevents the deadlock reported in SMACK-278. Additionally, we need to tell the PacketReader and PacketWriter that the socket got closed and that subsequent caused Exceptions can be ignored. This is done via the socketClosed boolean in XMPP Connection, that is queried by PacketReader and PacketWriter in case an Exception is encountered. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@13390 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
3c2eaba840
commit
e1bb5a6123
3 changed files with 57 additions and 10 deletions
|
@ -26,6 +26,7 @@ import org.jivesoftware.smack.sasl.SASLMechanism.Challenge;
|
||||||
import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
|
import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
|
||||||
import org.jivesoftware.smack.sasl.SASLMechanism.Success;
|
import org.jivesoftware.smack.sasl.SASLMechanism.Success;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
|
|
||||||
import org.xmlpull.mxp1.MXParser;
|
import org.xmlpull.mxp1.MXParser;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
@ -326,7 +327,9 @@ class PacketReader {
|
||||||
} while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread);
|
} while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
if (!done) {
|
// The exception can be ignored if the the connection is 'done'
|
||||||
|
// or if the it was caused because the socket got closed
|
||||||
|
if (!(done || connection.isSocketClosed())) {
|
||||||
// Close the connection and notify connection listeners of the
|
// Close the connection and notify connection listeners of the
|
||||||
// error.
|
// error.
|
||||||
notifyConnectionError(e);
|
notifyConnectionError(e);
|
||||||
|
|
|
@ -149,6 +149,9 @@ class PacketWriter {
|
||||||
synchronized (queue) {
|
synchronized (queue) {
|
||||||
queue.notifyAll();
|
queue.notifyAll();
|
||||||
}
|
}
|
||||||
|
// Interrupt the keep alive thread if one was created
|
||||||
|
if (keepAliveThread != null)
|
||||||
|
keepAliveThread.interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -232,10 +235,16 @@ class PacketWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException ioe){
|
catch (IOException ioe) {
|
||||||
if (!done) {
|
// The exception can be ignored if the the connection is 'done'
|
||||||
|
// or if the it was caused because the socket got closed
|
||||||
|
if (!(done || connection.isSocketClosed())) {
|
||||||
done = true;
|
done = true;
|
||||||
connection.packetReader.notifyConnectionError(ioe);
|
// packetReader could be set to null by an concurrent disconnect() call.
|
||||||
|
// Therefore Prevent NPE exceptions by checking packetReader.
|
||||||
|
if (connection.packetReader != null) {
|
||||||
|
connection.packetReader.notifyConnectionError(ioe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,10 @@ public class XMPPConnection extends Connection {
|
||||||
String connectionID = null;
|
String connectionID = null;
|
||||||
private String user = null;
|
private String user = null;
|
||||||
private boolean connected = false;
|
private boolean connected = false;
|
||||||
|
// socketClosed is used concurrent
|
||||||
|
// by XMPPConnection, PacketReader, PacketWriter
|
||||||
|
private volatile boolean socketClosed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag that indicates if the user is currently authenticated with the server.
|
* Flag that indicates if the user is currently authenticated with the server.
|
||||||
*/
|
*/
|
||||||
|
@ -358,6 +362,10 @@ public class XMPPConnection extends Connection {
|
||||||
return isUsingTLS();
|
return isUsingTLS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSocketClosed() {
|
||||||
|
return socketClosed;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAuthenticated() {
|
public boolean isAuthenticated() {
|
||||||
return authenticated;
|
return authenticated;
|
||||||
}
|
}
|
||||||
|
@ -377,14 +385,20 @@ public class XMPPConnection extends Connection {
|
||||||
*/
|
*/
|
||||||
protected void shutdown(Presence unavailablePresence) {
|
protected void shutdown(Presence unavailablePresence) {
|
||||||
// Set presence to offline.
|
// Set presence to offline.
|
||||||
packetWriter.sendPacket(unavailablePresence);
|
if (packetWriter != null) {
|
||||||
|
packetWriter.sendPacket(unavailablePresence);
|
||||||
|
}
|
||||||
|
|
||||||
this.setWasAuthenticated(authenticated);
|
this.setWasAuthenticated(authenticated);
|
||||||
authenticated = false;
|
authenticated = false;
|
||||||
connected = false;
|
|
||||||
|
|
||||||
packetReader.shutdown();
|
if (packetReader != null) {
|
||||||
packetWriter.shutdown();
|
packetReader.shutdown();
|
||||||
|
}
|
||||||
|
if (packetWriter != null) {
|
||||||
|
packetWriter.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
// Wait 150 ms for processes to clean-up, then shutdown.
|
// Wait 150 ms for processes to clean-up, then shutdown.
|
||||||
try {
|
try {
|
||||||
Thread.sleep(150);
|
Thread.sleep(150);
|
||||||
|
@ -393,9 +407,25 @@ public class XMPPConnection extends Connection {
|
||||||
// Ignore.
|
// Ignore.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set socketClosed to true. This will cause the PacketReader
|
||||||
|
// and PacketWriter to ingore any Exceptions that are thrown
|
||||||
|
// because of a read/write from/to a closed stream.
|
||||||
|
// It is *important* that this is done before socket.close()!
|
||||||
|
socketClosed = true;
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// In most cases the close() should be successful, so set
|
||||||
|
// connected to false here.
|
||||||
|
connected = false;
|
||||||
|
|
||||||
// Close down the readers and writers.
|
// Close down the readers and writers.
|
||||||
if (reader != null) {
|
if (reader != null) {
|
||||||
try {
|
try {
|
||||||
|
// Should already be closed by the previous
|
||||||
|
// socket.close(). But just in case do it explicitly.
|
||||||
reader.close();
|
reader.close();
|
||||||
}
|
}
|
||||||
catch (Throwable ignore) { /* ignore */ }
|
catch (Throwable ignore) { /* ignore */ }
|
||||||
|
@ -403,13 +433,17 @@ public class XMPPConnection extends Connection {
|
||||||
}
|
}
|
||||||
if (writer != null) {
|
if (writer != null) {
|
||||||
try {
|
try {
|
||||||
|
// Should already be closed by the previous
|
||||||
|
// socket.close(). But just in case do it explicitly.
|
||||||
writer.close();
|
writer.close();
|
||||||
}
|
}
|
||||||
catch (Throwable ignore) { /* ignore */ }
|
catch (Throwable ignore) { /* ignore */ }
|
||||||
writer = null;
|
writer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure that the socket is really closed
|
||||||
try {
|
try {
|
||||||
|
// Does nothing if the socket is already closed
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
@ -524,6 +558,7 @@ public class XMPPConnection extends Connection {
|
||||||
throw new XMPPException(errorMessage, new XMPPError(
|
throw new XMPPException(errorMessage, new XMPPError(
|
||||||
XMPPError.Condition.remote_server_error, errorMessage), ioe);
|
XMPPError.Condition.remote_server_error, errorMessage), ioe);
|
||||||
}
|
}
|
||||||
|
socketClosed = false;
|
||||||
initConnection();
|
initConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue