Use an atomic reference array instead of ConcurrentHashMap for listeners

This commit is contained in:
Kristian S. Stangeland 2013-03-12 02:33:35 +01:00
parent e3cfa45607
commit ed9b61fd11
3 changed files with 31 additions and 17 deletions

View File

@ -23,6 +23,7 @@ import java.util.PriorityQueue;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap; import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.PrioritizedListener; import com.comphenix.protocol.injector.PrioritizedListener;
@ -66,7 +67,7 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
} }
public PacketProcessingQueue(PlayerSendingHandler sendingHandler, int initialSize, int maximumSize, int maximumConcurrency) { public PacketProcessingQueue(PlayerSendingHandler sendingHandler, int initialSize, int maximumSize, int maximumConcurrency) {
super(); super(Packets.MAXIMUM_PACKET_ID);
try { try {
this.processingQueue = Synchronization.queue(MinMaxPriorityQueue. this.processingQueue = Synchronization.queue(MinMaxPriorityQueue.

View File

@ -23,6 +23,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReferenceArray;
import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.injector.PrioritizedListener; import com.comphenix.protocol.injector.PrioritizedListener;
@ -35,8 +36,13 @@ import com.google.common.collect.Iterables;
*/ */
public abstract class AbstractConcurrentListenerMultimap<TListener> { public abstract class AbstractConcurrentListenerMultimap<TListener> {
// The core of our map // The core of our map
private ConcurrentMap<Integer, SortedCopyOnWriteArray<PrioritizedListener<TListener>>> listeners = private AtomicReferenceArray<SortedCopyOnWriteArray<PrioritizedListener<TListener>>> arrayListeners;
new ConcurrentHashMap<Integer, SortedCopyOnWriteArray<PrioritizedListener<TListener>>>(); private ConcurrentMap<Integer, SortedCopyOnWriteArray<PrioritizedListener<TListener>>> mapListeners;
public AbstractConcurrentListenerMultimap(int maximumPacketID) {
arrayListeners = new AtomicReferenceArray<SortedCopyOnWriteArray<PrioritizedListener<TListener>>>(maximumPacketID + 1);
mapListeners = new ConcurrentHashMap<Integer, SortedCopyOnWriteArray<PrioritizedListener<TListener>>>();
}
/** /**
* Adds a listener to its requested list of packet recievers. * Adds a listener to its requested list of packet recievers.
@ -53,7 +59,7 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
// Add the listener to a specific packet notifcation list // Add the listener to a specific packet notifcation list
private void addListener(Integer packetID, PrioritizedListener<TListener> listener) { private void addListener(Integer packetID, PrioritizedListener<TListener> listener) {
SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = listeners.get(packetID); SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = arrayListeners.get(packetID);
// We don't want to create this for every lookup // We don't want to create this for every lookup
if (list == null) { if (list == null) {
@ -61,12 +67,12 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
// which is a essential feature for our purposes. // which is a essential feature for our purposes.
final SortedCopyOnWriteArray<PrioritizedListener<TListener>> value = new SortedCopyOnWriteArray<PrioritizedListener<TListener>>(); final SortedCopyOnWriteArray<PrioritizedListener<TListener>> value = new SortedCopyOnWriteArray<PrioritizedListener<TListener>>();
list = listeners.putIfAbsent(packetID, value); // We may end up creating multiple multisets, but we'll agree on which to use
if (arrayListeners.compareAndSet(packetID, null, value)) {
// We may end up creating multiple multisets, but we'll agree mapListeners.put(packetID, value);
// on the one to use.
if (list == null) {
list = value; list = value;
} else {
list = arrayListeners.get(packetID);
} }
} }
@ -85,8 +91,7 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
// Again, not terribly efficient. But adding or removing listeners should be a rare event. // Again, not terribly efficient. But adding or removing listeners should be a rare event.
for (Integer packetID : whitelist.getWhitelist()) { for (Integer packetID : whitelist.getWhitelist()) {
SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = arrayListeners.get(packetID);
SortedCopyOnWriteArray<PrioritizedListener<TListener>> list = listeners.get(packetID);
// Remove any listeners // Remove any listeners
if (list != null) { if (list != null) {
@ -96,7 +101,8 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
list.remove(new PrioritizedListener<TListener>(listener, whitelist.getPriority())); list.remove(new PrioritizedListener<TListener>(listener, whitelist.getPriority()));
if (list.size() == 0) { if (list.size() == 0) {
listeners.remove(packetID); arrayListeners.set(packetID, null);
mapListeners.remove(packetID);
removedPackets.add(packetID); removedPackets.add(packetID);
} }
} }
@ -116,7 +122,7 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
* @return Registered listeners. * @return Registered listeners.
*/ */
public Collection<PrioritizedListener<TListener>> getListener(int packetID) { public Collection<PrioritizedListener<TListener>> getListener(int packetID) {
return listeners.get(packetID); return arrayListeners.get(packetID);
} }
/** /**
@ -124,7 +130,7 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
* @return Every listener. * @return Every listener.
*/ */
public Iterable<PrioritizedListener<TListener>> values() { public Iterable<PrioritizedListener<TListener>> values() {
return Iterables.concat(listeners.values()); return Iterables.concat(mapListeners.values());
} }
/** /**
@ -132,13 +138,15 @@ public abstract class AbstractConcurrentListenerMultimap<TListener> {
* @return Registered packet ID. * @return Registered packet ID.
*/ */
public Set<Integer> keySet() { public Set<Integer> keySet() {
return listeners.keySet(); return mapListeners.keySet();
} }
/** /**
* Remove all packet listeners. * Remove all packet listeners.
*/ */
protected void clearListeners() { protected void clearListeners() {
listeners.clear(); arrayListeners = new AtomicReferenceArray<
SortedCopyOnWriteArray<PrioritizedListener<TListener>>>(arrayListeners.length());
mapListeners.clear();
} }
} }

View File

@ -19,6 +19,7 @@ package com.comphenix.protocol.injector;
import java.util.Collection; import java.util.Collection;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap; import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.ListenerPriority;
@ -31,6 +32,10 @@ import com.comphenix.protocol.events.PacketListener;
* @author Kristian * @author Kristian
*/ */
public final class SortedPacketListenerList extends AbstractConcurrentListenerMultimap<PacketListener> { public final class SortedPacketListenerList extends AbstractConcurrentListenerMultimap<PacketListener> {
public SortedPacketListenerList() {
super(Packets.MAXIMUM_PACKET_ID);
}
/** /**
* Invokes the given packet event for every registered listener. * Invokes the given packet event for every registered listener.
* @param reporter - the error reporter that will be used to inform about listener exceptions. * @param reporter - the error reporter that will be used to inform about listener exceptions.