From 3e4a1ed5b56686a1a9773d5f46a0aee99df7d21b Mon Sep 17 00:00:00 2001 From: Matt Tucker Date: Mon, 19 Feb 2007 05:48:57 +0000 Subject: [PATCH] Optimized concurrency in packet writer, better job of cleanup on disconnect. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@7183 b35dd754-fafc-0310-a699-88a17e54d16e --- .../org/jivesoftware/smack/PacketReader.java | 14 ++- .../org/jivesoftware/smack/PacketWriter.java | 119 +++++------------- .../jivesoftware/smack/XMPPConnection.java | 18 ++- 3 files changed, 62 insertions(+), 89 deletions(-) diff --git a/source/org/jivesoftware/smack/PacketReader.java b/source/org/jivesoftware/smack/PacketReader.java index 33a40fb60..99a3f2718 100644 --- a/source/org/jivesoftware/smack/PacketReader.java +++ b/source/org/jivesoftware/smack/PacketReader.java @@ -50,7 +50,8 @@ class PacketReader { private XmlPullParser parser; private boolean done; private Collection collectors = new ConcurrentLinkedQueue(); - protected final Map listeners = new ConcurrentHashMap(); + protected final Map listeners = + new ConcurrentHashMap(); protected final Collection connectionListeners = new CopyOnWriteArrayList(); @@ -197,6 +198,15 @@ class PacketReader { } } + /** + * Cleans up all resources used by the packet reader. + */ + void cleanup() { + connectionListeners.clear(); + listeners.clear(); + collectors.clear(); + } + /** * Sends out a notification that there was an error with the connection * and closes the connection. @@ -824,7 +834,7 @@ class PacketReader { /** * A wrapper class to associate a packet collector with a listener. */ - protected static class ListenerWrapper { + private static class ListenerWrapper { private PacketListener packetListener; private PacketCollector packetCollector; diff --git a/source/org/jivesoftware/smack/PacketWriter.java b/source/org/jivesoftware/smack/PacketWriter.java index c3bf6e462..8ff3a19c0 100644 --- a/source/org/jivesoftware/smack/PacketWriter.java +++ b/source/org/jivesoftware/smack/PacketWriter.java @@ -25,10 +25,10 @@ import org.jivesoftware.smack.packet.Packet; import java.io.IOException; import java.io.Writer; -import java.util.ArrayList; -import java.util.List; import java.util.Queue; +import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentHashMap; /** * Writes packets to a XMPP server. Packets are sent using a dedicated thread. Packet @@ -43,11 +43,11 @@ class PacketWriter { private Thread keepAliveThread; private Writer writer; private XMPPConnection connection; - final private Queue queue; + private final Queue queue; private boolean done; - final protected List listeners = new ArrayList(); - private boolean listenersDeleted; + private final Map listeners = + new ConcurrentHashMap(); /** * Timestamp when the last stanza was sent to the server. This information is used @@ -56,15 +56,12 @@ class PacketWriter { private long lastActive = System.currentTimeMillis(); /** - * List of PacketInterceptor that will be notified when a new packet is about to be + * List of PacketInterceptors that will be notified when a new packet is about to be * sent to the server. These interceptors may modify the packet before it is being * actually sent to the server. */ - final private List interceptors = new ArrayList(); - /** - * Flag that indicates if an interceptor was deleted. This is an optimization flag. - */ - private boolean interceptorDeleted = false; + private final Map interceptors = + new ConcurrentHashMap(); /** * Creates a new packet writer with the specified connection. @@ -83,8 +80,6 @@ class PacketWriter { */ protected void init() { this.writer = connection.writer; - listenersDeleted = false; - interceptorDeleted = false; done = false; writerThread = new Thread() { @@ -130,9 +125,7 @@ class PacketWriter { * @param packetFilter the packet filter to use. */ public void addPacketListener(PacketListener packetListener, PacketFilter packetFilter) { - synchronized (listeners) { - listeners.add(new ListenerWrapper(packetListener, packetFilter)); - } + listeners.put(packetListener, new ListenerWrapper(packetListener, packetFilter)); } /** @@ -141,17 +134,7 @@ class PacketWriter { * @param packetListener the packet listener to remove. */ public void removePacketListener(PacketListener packetListener) { - synchronized (listeners) { - for (int i=0; i"); @@ -320,28 +302,13 @@ class PacketWriter { /** * Process listeners. + * + * @param packet the packet to process. */ private void processListeners(Packet packet) { - // Clean up null entries in the listeners list if the flag is set. List - // removes are done seperately so that the main notification process doesn't - // need to synchronize on the list. - synchronized (listeners) { - if (listenersDeleted) { - for (int i=listeners.size()-1; i>=0; i--) { - if (listeners.get(i) == null) { - listeners.remove(i); - } - } - listenersDeleted = false; - } - } // Notify the listeners of the new sent packet - int size = listeners.size(); - for (int i=0; i=0; i--) { - if (interceptors.get(i) == null) { - interceptors.remove(i); - } - } - interceptorDeleted = false; - } - } - // Notify the interceptors of the new packet to be sent - int size = interceptors.size(); - for (int i=0; i + * + * This method cleans up all resources used by the connection. Therefore, the roster, + * listeners and other stateful objects cannot be re-used by simply calling connect() + * on this connection again. This is unlike the behavior during unexpected disconnects + * (and subsequent connections). In that case, all state is preserved to allow for + * more seamless error recovery. */ public void disconnect() { disconnect(new Presence(Presence.Type.unavailable)); @@ -613,7 +619,13 @@ public class XMPPConnection { * again. A custom unavilable presence is useful for communicating offline presence * information such as "On vacation". Typically, just the status text of the presence * packet is set with online information, but most XMPP servers will deliver the full - * presence packet with whatever data is set. + * presence packet with whatever data is set.

+ * + * This method cleans up all resources used by the connection. Therefore, the roster, + * listeners and other stateful objects cannot be re-used by simply calling connect() + * on this connection again. This is unlike the behavior during unexpected disconnects + * (and subsequent connections). In that case, all state is preserved to allow for + * more seamless error recovery. * * @param unavailablePresence the presence packet to send during shutdown. */ @@ -624,7 +636,9 @@ public class XMPPConnection { this.wasAuthenticated = false; + packetWriter.cleanup(); packetWriter = null; + packetReader.cleanup(); packetReader = null; }