package org.playorm.nio.impl.libs; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.playorm.nio.api.channels.RegisterableChannel; import org.playorm.nio.api.libs.ChannelsRunnable; /** */ public class LookAheadQueue implements BlockingQueue<Runnable> { //The list of sessions currently being run on a thread(ie. don't let another thread run a request of one of these sessions) private Set<RegisterableChannel> runningChannels = Collections.synchronizedSet(new HashSet<RegisterableChannel>()); private BlockingQueue<Runnable> queue; private Lock lock = new ReentrantLock(); private Condition hasAvailableRunnable = lock.newCondition(); private Condition queueNotFull = lock.newCondition(); public LookAheadQueue(BlockingQueue<Runnable> queue) { this.queue = queue; } public boolean offer(Runnable o) { throw new UnsupportedOperationException("not implemented yet"); } public boolean offer(Runnable o, long timeout, TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException("not implemented yet"); } public void put(Runnable o) throws InterruptedException { lock.lockInterruptibly(); try { while(queue.remainingCapacity() <= 0) { queueNotFull.await(); } queue.put(o); //now may need to signal hasAvailableRunnable ChannelsRunnable r = (ChannelsRunnable)o; if(!runningChannels.contains(r.getChannel())) hasAvailableRunnable.signal(); } finally { lock.unlock(); } } /** * @see java.util.concurrent.BlockingQueue#remainingCapacity() */ public int remainingCapacity() { return queue.remainingCapacity(); } public boolean add(Runnable o) { throw new UnsupportedOperationException("not implemented yet"); } /** * @see java.util.concurrent.BlockingQueue#drainTo(java.util.Collection) */ public int drainTo(Collection< ? super Runnable> c) { throw new UnsupportedOperationException("not supported yet"); } /** * @see java.util.concurrent.BlockingQueue#drainTo(java.util.Collection, int) */ public int drainTo(Collection< ? super Runnable> c, int maxElements) { throw new UnsupportedOperationException("not supported yet"); } /** * @see java.util.concurrent.BlockingQueue#poll(long, java.util.concurrent.TimeUnit) */ public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { lock.lockInterruptibly(); try { long nanos = unit.toNanos(timeout); while(true) { if(queue.remainingCapacity() > 0) { ChannelsRunnable availableRunnable = getAvailableRunnable(); if(availableRunnable != null) return availableRunnable; } if (nanos <= 0) return null; try { nanos = hasAvailableRunnable.awaitNanos(nanos); } catch (InterruptedException ie) { hasAvailableRunnable.signal(); // propagate to non-interrupted thread throw ie; } } } finally { lock.unlock(); } } public Runnable take() throws InterruptedException { lock.lockInterruptibly(); try { try { ChannelsRunnable r = null; while(r == null) { r = getAvailableRunnable(); if(r != null) break; hasAvailableRunnable.await(); } return r; } catch (InterruptedException ie) { hasAvailableRunnable.signal(); // propagate to non-interrupted thread throw ie; } } finally { lock.unlock(); } } /** * Returns an available runnable. A runnable is only available if another thread * is not running another runnable that has the same channel(ie. it is the same client * making the request.). this makes it very fair between clients so they all get time in * the threads equally * @return Available runnable or null if none is available */ private ChannelsRunnable getAvailableRunnable() { Iterator<Runnable> iterator = queue.iterator(); while(iterator.hasNext()) { ChannelsRunnable runnable = (ChannelsRunnable)iterator.next(); RegisterableChannel channel = runnable.getChannel(); if(!runningChannels.contains(channel)) { runningChannels.add(channel); return new QueueRunnable(runningChannels, runnable); } } return null; } private class QueueRunnable implements ChannelsRunnable { private Set<RegisterableChannel> runningChannels; private ChannelsRunnable runnable; public QueueRunnable(Set<RegisterableChannel> runningChannels, ChannelsRunnable runnable) { this.runningChannels = runningChannels; this.runnable = runnable; } public void run() { runnable.run(); lock.lock(); try { runningChannels.remove(getChannel()); hasAvailableRunnable.signal(); } finally { lock.unlock(); } } public RegisterableChannel getChannel() { return runnable.getChannel(); } } public Runnable poll() { return getAvailableRunnable(); } /** * @see java.util.Queue#remove() */ public Runnable remove() { throw new UnsupportedOperationException("not supported yet"); } /** * @see java.util.Queue#peek() */ public Runnable peek() { return queue.peek(); } /** * @see java.util.Queue#element() */ public Runnable element() { return queue.element(); } /** * @see java.util.Collection#size() */ public int size() { return queue.size(); } /** * @see java.util.Collection#isEmpty() */ public boolean isEmpty() { return queue.isEmpty(); } /** * @see java.util.Collection#contains(java.lang.Object) */ public boolean contains(Object o) { return queue.contains(o); } /** * @see java.util.Collection#iterator() */ public Iterator<Runnable> iterator() { return queue.iterator(); } /** * @see java.util.Collection#toArray() */ public Object[] toArray() { return queue.toArray(); } /** * @see java.util.Collection#toArray(T[]) */ public <T> T[] toArray(T[] a) { return queue.toArray(a); } /** * @see java.util.Collection#remove(java.lang.Object) */ public boolean remove(Object o) { return queue.remove(o); } /** * @see java.util.Collection#containsAll(java.util.Collection) */ public boolean containsAll(Collection< ? > c) { return queue.containsAll(c); } /** * @see java.util.Collection#addAll(java.util.Collection) */ public boolean addAll(Collection< ? extends Runnable> c) { return queue.addAll(c); } /** * @see java.util.Collection#removeAll(java.util.Collection) */ public boolean removeAll(Collection< ? > c) { return queue.removeAll(c); } /** * @see java.util.Collection#retainAll(java.util.Collection) */ public boolean retainAll(Collection< ? > c) { return queue.retainAll(c); } /** * @see java.util.Collection#clear() */ public void clear() { queue.clear(); } }