package net.i2p.router.util; import java.io.Serializable; import java.util.Comparator; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import net.i2p.I2PAppContext; import net.i2p.util.Log; /** * Priority Blocking Queue using methods in the entries, * as defined in PQEntry, to store priority and sequence number, * ensuring FIFO order within a priority. * * Input: add(), offer(), and put() are overridden to add a sequence number. * * @since 0.9.3 */ public class PriBlockingQueue<E extends PQEntry> extends PriorityBlockingQueue<E> { private static final long serialVersionUID = 1L; protected transient final I2PAppContext _context; protected transient final Log _log; protected final String _name; private final AtomicLong _seqNum = new AtomicLong(); private final String STAT_FULL; protected static final long[] RATES = {5*60*1000, 60*60*1000}; protected static final int BACKLOG_SIZE = 256; protected static final int MAX_SIZE = 512; /** * Bounded queue with a hardcoded failsafe max size, * except when using put(), which is unbounded. */ public PriBlockingQueue(I2PAppContext ctx, String name, int initialCapacity) { super(initialCapacity, new PriorityComparator<E>()); _context = ctx; _log = ctx.logManager().getLog(PriorityBlockingQueue.class); _name = name; STAT_FULL = ("pbq." + name + ".full").intern(); ctx.statManager().createRateStat(STAT_FULL, "queue full", "Router", RATES); } /** * OpenJDK add(o) calls offer(o), so use offer(o) to avoid dup stamping. * Returns false if full * @deprecated use offer(o) */ @Deprecated @Override public boolean add(E o) { timestamp(o); if (size() >= MAX_SIZE) { _context.statManager().addRateData(STAT_FULL, 1); return false; } return super.add(o); } /** * Returns false if full */ @Override public boolean offer(E o) { timestamp(o); if (size() >= MAX_SIZE) { _context.statManager().addRateData(STAT_FULL, 1); return false; } return super.offer(o); } /** * OpenJDK offer(o, timeout, unit) calls offer(o), so use offer(o) to avoid dup stamping. * Non blocking. Returns false if full. * @param timeout ignored * @param unit ignored * @deprecated use offer(o) */ @Deprecated @Override public boolean offer(E o, long timeout, TimeUnit unit) { timestamp(o); if (size() >= MAX_SIZE) { _context.statManager().addRateData(STAT_FULL, 1); return false; } return super.offer(o, timeout, unit); } /** * OpenJDK put(o) calls offer(o), so use offer(o) to avoid dup stamping. * Non blocking. Does not add if full. * @deprecated use offer(o) */ @Deprecated @Override public void put(E o) { timestamp(o); super.put(o); } /** * Is the queue too big? */ public boolean isBacklogged() { return size() >= BACKLOG_SIZE; } /////// private below here protected void timestamp(E o) { o.setSeqNum(_seqNum.incrementAndGet()); } /** * highest priority first, then lowest sequence number first */ private static class PriorityComparator<E extends PQEntry> implements Comparator<E>, Serializable { public int compare(E l, E r) { int d = r.getPriority() - l.getPriority(); if (d != 0) return d; long ld = l.getSeqNum() - r.getSeqNum(); return ld > 0 ? 1 : -1; } } }