package org.limewire.nio.timeout; import java.util.ArrayList; import java.util.List; import java.util.PriorityQueue; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Keeps track of {@link Timeoutable Timeoutables}. */ public class TimeoutController { private static final Log LOG = LogFactory.getLog(TimeoutController.class); private final PriorityQueue<Timeout> items = new PriorityQueue<Timeout>(20); // used to store timed out items to notify outside of lock. private final List<Timeout> timedout = new ArrayList<Timeout>(100); public synchronized int getNumPendingTimeouts() { return items.size(); } /** Adds a <code>Timeoutable</code> to be timed out at timeout + now. */ public synchronized void addTimeout(Timeoutable t, long now, long timeout) { if(LOG.isDebugEnabled()) LOG.debug("Adding timeoutable: " + t + ", now: " + now + ", timeout: " + timeout); items.offer(new Timeout(t, now, timeout)); } /** * Processes all the items that can be timed out. */ public void processTimeouts(long now) { synchronized(this) { while(!items.isEmpty()) { Timeout t = items.peek(); if(t != null && now >= t.expireTime) { if(LOG.isDebugEnabled()) LOG.debug("Timing out: " + t + ", expired: " + t.expireTime + ", now: " + now + ", length: " + t.timeoutLength); timedout.add(t); } else { if(t != null && LOG.isDebugEnabled()) LOG.debug("Breaking -- next timeout at: " + t.expireTime + ", now: " + now); break; } items.poll(); // remove it -- we only peeked before. } } for(int i = 0; i < timedout.size(); i++) { Timeout t = timedout.get(i); t.timeoutable.notifyTimeout(now, t.expireTime, t.timeoutLength); } timedout.clear(); } /** Gets the time the next <code>Timeoutable</code> expires or -1 for never. */ public synchronized long getNextExpireTime() { if(items.isEmpty()) return -1; else return items.peek().expireTime; } /** Keep an <code>expireTime</code> and <code>timeoutable</code> together as one happy couple. */ private static class Timeout implements Comparable<Timeout> { private final long expireTime; private final Timeoutable timeoutable; private final long timeoutLength; Timeout(Timeoutable timeoutable, long now, long timeout) { if (timeout > 0 && Long.MAX_VALUE - timeout < now) this.expireTime = Long.MAX_VALUE; else this.expireTime = now + timeout; this.timeoutLength = timeout; this.timeoutable = timeoutable; } /** * Makes items that expire sooner considered 'larger' so the max BinaryHeap is * sorted correctly. */ public int compareTo(Timeout b) { return expireTime > b.expireTime ? 1 : expireTime < b.expireTime ? -1 : 0; } @Override public String toString() { return "TimeoutWrapper for: " + timeoutable; } } }