From ed9b61fd11e2b2857f36ed44b7d435a1fe68bd5c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 12 Mar 2013 02:33:35 +0100 Subject: [PATCH] Use an atomic reference array instead of ConcurrentHashMap for listeners --- .../protocol/async/PacketProcessingQueue.java | 3 +- .../AbstractConcurrentListenerMultimap.java | 40 +++++++++++-------- .../injector/SortedPacketListenerList.java | 5 +++ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java b/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java index 800124f2..3671ba0e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java @@ -23,6 +23,7 @@ import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.Semaphore; +import com.comphenix.protocol.Packets; import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.injector.PrioritizedListener; @@ -66,7 +67,7 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap { // The core of our map - private ConcurrentMap>> listeners = - new ConcurrentHashMap>>(); + private AtomicReferenceArray>> arrayListeners; + private ConcurrentMap>> mapListeners; + + public AbstractConcurrentListenerMultimap(int maximumPacketID) { + arrayListeners = new AtomicReferenceArray>>(maximumPacketID + 1); + mapListeners = new ConcurrentHashMap>>(); + } /** * Adds a listener to its requested list of packet recievers. @@ -53,20 +59,20 @@ public abstract class AbstractConcurrentListenerMultimap { // Add the listener to a specific packet notifcation list private void addListener(Integer packetID, PrioritizedListener listener) { - SortedCopyOnWriteArray> list = listeners.get(packetID); + SortedCopyOnWriteArray> list = arrayListeners.get(packetID); // We don't want to create this for every lookup if (list == null) { // It would be nice if we could use a PriorityBlockingQueue, but it doesn't preseve iterator order, // which is a essential feature for our purposes. final SortedCopyOnWriteArray> value = new SortedCopyOnWriteArray>(); - - list = listeners.putIfAbsent(packetID, value); - - // We may end up creating multiple multisets, but we'll agree - // on the one to use. - if (list == null) { + + // We may end up creating multiple multisets, but we'll agree on which to use + if (arrayListeners.compareAndSet(packetID, null, value)) { + mapListeners.put(packetID, value); list = value; + } else { + list = arrayListeners.get(packetID); } } @@ -85,8 +91,7 @@ public abstract class AbstractConcurrentListenerMultimap { // Again, not terribly efficient. But adding or removing listeners should be a rare event. for (Integer packetID : whitelist.getWhitelist()) { - - SortedCopyOnWriteArray> list = listeners.get(packetID); + SortedCopyOnWriteArray> list = arrayListeners.get(packetID); // Remove any listeners if (list != null) { @@ -96,7 +101,8 @@ public abstract class AbstractConcurrentListenerMultimap { list.remove(new PrioritizedListener(listener, whitelist.getPriority())); if (list.size() == 0) { - listeners.remove(packetID); + arrayListeners.set(packetID, null); + mapListeners.remove(packetID); removedPackets.add(packetID); } } @@ -116,7 +122,7 @@ public abstract class AbstractConcurrentListenerMultimap { * @return Registered listeners. */ public Collection> getListener(int packetID) { - return listeners.get(packetID); + return arrayListeners.get(packetID); } /** @@ -124,7 +130,7 @@ public abstract class AbstractConcurrentListenerMultimap { * @return Every listener. */ public Iterable> values() { - return Iterables.concat(listeners.values()); + return Iterables.concat(mapListeners.values()); } /** @@ -132,13 +138,15 @@ public abstract class AbstractConcurrentListenerMultimap { * @return Registered packet ID. */ public Set keySet() { - return listeners.keySet(); + return mapListeners.keySet(); } /** * Remove all packet listeners. */ protected void clearListeners() { - listeners.clear(); + arrayListeners = new AtomicReferenceArray< + SortedCopyOnWriteArray>>(arrayListeners.length()); + mapListeners.clear(); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java index 53fe6366..e1e3d912 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/SortedPacketListenerList.java @@ -19,6 +19,7 @@ package com.comphenix.protocol.injector; import java.util.Collection; +import com.comphenix.protocol.Packets; import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap; import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.ListenerPriority; @@ -31,6 +32,10 @@ import com.comphenix.protocol.events.PacketListener; * @author Kristian */ public final class SortedPacketListenerList extends AbstractConcurrentListenerMultimap { + public SortedPacketListenerList() { + super(Packets.MAXIMUM_PACKET_ID); + } + /** * Invokes the given packet event for every registered listener. * @param reporter - the error reporter that will be used to inform about listener exceptions.