package com.limegroup.gnutella.connection; import com.limegroup.gnutella.messages.Message; /** * A priority queue for messages. Subclasses override the add, * removeNextInternal, and size template methods to implement different * prioritization policies. NOT THREAD SAFE.<p> * * This class is designed for speed; hence the somewhat awkward use of * resetCycle/extractMax instead of a simple iterator. Likewise, this class has * a resetDropped() method instead of returning a (Message, int) pair in * removeNext(); */ public abstract class AbstractMessageQueue implements MessageQueue { /** The number of messages per cycle, and number left for this cycle. * INVARIANT: 0<=_leftInCycle<=cycleSize */ private final int _cycleSize; private int _leftInCycle; /** The oldest message to return, in milliseconds. */ private int _timeout; /** The number of messages dropped since the last call to resetDropped(). */ protected int _dropped; /** * @param cycle the number of messages to return per cycle, i.e., between * calls to resetCycle. This is used to tweak the ratios of various * message types. * @param timeout the max time to keep queued messages, in milliseconds. * Set this to Integer.MAX_VALUE to avoid timeouts. */ protected AbstractMessageQueue(int cycle, int timeout) throws IllegalArgumentException { if (timeout<=0) throw new IllegalArgumentException("Timeout too small: "+cycle); if (cycle<=0) throw new IllegalArgumentException("Cycle too small: "+cycle); this._cycleSize=cycle; this._leftInCycle=cycle; this._timeout=timeout; } /** * Adds m to this. Message may be dropped in the process; find out how many * by calling resetDropped(). */ public void add(Message m) { Message dropmsg = addInternal(m); if (dropmsg != null) { _dropped++; dropmsg.recordDrop(); } } /** * Add m to this, returns any message that had to dropped to make room in * a queue. */ protected abstract Message addInternal(Message m); /** * Removes and returns the next message to send from this during this cycle. * Returns null if there are no more messages to send in this cycle. The * returned message is guaranteed be younger than TIMEOUT milliseconds. * Messages may be dropped in the process; find out how many by calling * resetDropped(). (Subclasses should implement the removeNextInternal * method and be sure to update the _dropped field if necessary.) * @return the next message, or null if none */ public Message removeNext() { if (_leftInCycle==0) return null; //Nothing left for cycle. long expireTime=System.currentTimeMillis()-_timeout; while (true) { Message m=removeNextInternal(); if (m==null) return null; //Nothing left, give up. if (m.getCreationTime()<expireTime) { _dropped++; m.recordDrop(); continue; //Too old. Keep searching. } _leftInCycle--; return m; //Normal case. } } /** Same as removeNext, but ignores message age and cycle. * @return the next message to send, or null if this is empty */ protected abstract Message removeNextInternal(); /** Resets the cycle counter used to control removeNext(). */ public void resetCycle() { this._leftInCycle=_cycleSize; } /** Returns the number of dropped messages since the last call to * resetDropped(). */ public final int resetDropped() { int ret=_dropped; _dropped=0; return ret; } /** Returns the number of queued messages. */ public abstract int size(); /** Returns true if this has any queued messages. */ public boolean isEmpty() { return size()==0; } //No unit tests; this code is covered by tests in ManagedConnection. //(Actually most of this code used to be in ManagedConnection.) }