ProtocolLib/ProtocolLib/src/main/java/com/comphenix/protocol/async/PacketProcessingQueue.java

173 lines
5.4 KiB
Java

/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.async;
import java.util.Collection;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.Semaphore;
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.PrioritizedListener;
import com.google.common.collect.MinMaxPriorityQueue;
/**
* Handles the processing of every packet type.
*
* @author Kristian
*/
class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncListenerHandler> {
// Initial number of elements
public static final int INITIAL_CAPACITY = 64;
/**
* Default maximum number of packets to process concurrently.
*/
public static final int DEFAULT_MAXIMUM_CONCURRENCY = 32;
/**
* Default maximum number of packets to queue for processing.
*/
public static final int DEFAULT_QUEUE_LIMIT = 1024 * 60;
/**
* Number of packets we're processing concurrently.
*/
private final int maximumConcurrency;
private Semaphore concurrentProcessing;
// Queued packets for being processed
private Queue<PacketEventHolder> processingQueue;
// Packets for sending
private PacketSendingQueue sendingQueue;
public PacketProcessingQueue(PacketSendingQueue sendingQueue) {
this(sendingQueue, INITIAL_CAPACITY, DEFAULT_QUEUE_LIMIT, DEFAULT_MAXIMUM_CONCURRENCY);
}
public PacketProcessingQueue(PacketSendingQueue sendingQueue, int initialSize, int maximumSize, int maximumConcurrency) {
super();
this.processingQueue = Synchronization.queue(MinMaxPriorityQueue.
expectedSize(initialSize).
maximumSize(maximumSize).
<PacketEventHolder>create(), null);
this.maximumConcurrency = maximumConcurrency;
this.concurrentProcessing = new Semaphore(maximumConcurrency);
this.sendingQueue = sendingQueue;
}
/**
* Enqueue a packet for processing by the asynchronous listeners.
* @param packet - packet to process.
* @param onMainThread - whether or not this is occuring on the main thread.
* @return TRUE if we sucessfully queued the packet, FALSE if the queue ran out if space.
*/
public boolean enqueue(PacketEvent packet, boolean onMainThread) {
try {
processingQueue.add(new PacketEventHolder(packet));
// Begin processing packets
signalBeginProcessing(onMainThread);
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Number of packet events in the queue.
* @return The number of packet events in the queue.
*/
public int size() {
return processingQueue.size();
}
/**
* Called by the current method and each thread to signal that a packet might be ready for processing.
* @param onMainThread - whether or not this is occuring on the main thread.
*/
public void signalBeginProcessing(boolean onMainThread) {
while (concurrentProcessing.tryAcquire()) {
PacketEventHolder holder = processingQueue.poll();
// Any packet queued?
if (holder != null) {
PacketEvent packet = holder.getEvent();
AsyncMarker marker = packet.getAsyncMarker();
Collection<PrioritizedListener<AsyncListenerHandler>> list = getListener(packet.getPacketID());
marker.incrementProcessingDelay();
// Yes, removing the marker will cause the chain to stop
if (list != null) {
Iterator<PrioritizedListener<AsyncListenerHandler>> iterator = list.iterator();
if (iterator.hasNext()) {
marker.setListenerTraversal(iterator);
iterator.next().getListener().enqueuePacket(packet);
continue;
}
}
// The packet has no further listeners. Just send it.
if (marker.decrementProcessingDelay() == 0)
sendingQueue.signalPacketUpdate(packet, onMainThread);
signalProcessingDone();
} else {
// No more queued packets.
signalProcessingDone();
return;
}
}
}
/**
* Called when a packet has been processed.
*/
public void signalProcessingDone() {
concurrentProcessing.release();
}
/**
* Retrieve the maximum number of packets to process at any given time.
* @return Number of simultaneous packet to process.
*/
public int getMaximumConcurrency() {
return maximumConcurrency;
}
public void cleanupAll() {
// Cancel all the threads and every listener
for (PrioritizedListener<AsyncListenerHandler> handler : values()) {
if (handler != null) {
handler.getListener().cancel();
}
}
// Remove the rest, just in case
clearListeners();
}
}