Index: org/apache/commons/pool/impl/GenericObjectPool.java =================================================================== --- org/apache/commons/pool/impl/GenericObjectPool.java (revision 606179) +++ org/apache/commons/pool/impl/GenericObjectPool.java (working copy) @@ -226,6 +226,8 @@ * @since 1.4 */ public static final boolean DEFAULT_LIFO = true; + + public static final boolean DEFAULT_FAIRNESS = true; /** * The default maximum amount of time (in millis) the @@ -315,7 +317,7 @@ * @param config a non-null {@link GenericObjectPool.Config} describing my configuration */ public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) { - this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.minIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.softMinEvictableIdleTimeMillis, config.lifo); + this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.minIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.softMinEvictableIdleTimeMillis, config.lifo, config.fairness); } /** @@ -432,7 +434,7 @@ * @since Pool 1.3 */ public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis) { - this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, softMinEvictableIdleTimeMillis, DEFAULT_LIFO); + this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, softMinEvictableIdleTimeMillis, DEFAULT_LIFO, DEFAULT_FAIRNESS); } /** @@ -453,7 +455,7 @@ * @param lifo whether or not objects are returned in last-in-first-out order from the idle object pool (see {@link #setLifo}) * @since Pool 1.4 */ - public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis, boolean lifo) { + public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis, boolean lifo, boolean fairness) { _factory = factory; _maxActive = maxActive; _lifo = lifo; @@ -476,6 +478,8 @@ _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; _softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; _testWhileIdle = testWhileIdle; + _fairness = fairness; + _semaphore = new FairnessSemaphore(_maxActive); _pool = new CursorableLinkedList(); startEvictor(_timeBetweenEvictionRunsMillis); @@ -502,6 +506,7 @@ */ public synchronized void setMaxActive(int maxActive) { _maxActive = maxActive; + _semaphore.setSize(_maxActive); notifyAll(); } @@ -848,6 +853,10 @@ public synchronized void setLifo(boolean lifo) { this._lifo = lifo; } + + public synchronized void setFairness(boolean fairness) { + this._fairness = fairness; + } /** * Sets my configuration. @@ -869,6 +878,7 @@ setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis); setSoftMinEvictableIdleTimeMillis(conf.softMinEvictableIdleTimeMillis); setLifo(conf.lifo); + setFairness(conf.fairness); notifyAll(); } @@ -874,7 +884,19 @@ //-- ObjectPool methods ------------------------------------------ + boolean _fairness = true; + FairnessSemaphore _semaphore; + public Object borrowObject() throws Exception { + if (_fairness) { + _semaphore.acquire(_maxWait); + return _borrowObject(); + } else { + return _borrowObject(); + } + } + + public Object _borrowObject() throws Exception { long starttime = System.currentTimeMillis(); for(;;) { ObjectTimestampPair pair = null; @@ -992,6 +1014,9 @@ } finally { synchronized (this) { _numActive--; + if (_fairness) { + _semaphore.release(); + } notifyAll(); // _numActive has changed } } @@ -1052,6 +1077,10 @@ // swallowed } } + } finally { + if (_fairness) { + _semaphore.release(); + } } } @@ -1367,6 +1396,8 @@ * @see GenericObjectPool#setLifo */ public boolean lifo = GenericObjectPool.DEFAULT_LIFO; + + public boolean fairness = GenericObjectPool.DEFAULT_FAIRNESS; } Index: org/apache/commons/pool/impl/GenericObjectPoolFactory.java =================================================================== --- org/apache/commons/pool/impl/GenericObjectPoolFactory.java (revision 606179) +++ org/apache/commons/pool/impl/GenericObjectPoolFactory.java (working copy) @@ -51,7 +51,7 @@ * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, GenericObjectPool.Config) */ public GenericObjectPoolFactory(PoolableObjectFactory factory, GenericObjectPool.Config config) throws NullPointerException { - this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.minIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.softMinEvictableIdleTimeMillis, config.lifo); + this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.minIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.softMinEvictableIdleTimeMillis, config.lifo, config.fairness); } /** @@ -184,7 +184,7 @@ * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int, int, boolean, boolean, long, int, long, boolean, long) */ public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis) { - this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,minIdle,testOnBorrow,testOnReturn,timeBetweenEvictionRunsMillis,numTestsPerEvictionRun,minEvictableIdleTimeMillis,testWhileIdle,softMinEvictableIdleTimeMillis, GenericObjectPool.DEFAULT_LIFO); + this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,minIdle,testOnBorrow,testOnReturn,timeBetweenEvictionRunsMillis,numTestsPerEvictionRun,minEvictableIdleTimeMillis,testWhileIdle,softMinEvictableIdleTimeMillis, GenericObjectPool.DEFAULT_LIFO, GenericObjectPool.DEFAULT_FAIRNESS); } /** @@ -207,7 +207,7 @@ * @since Pool 1.4 * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int, int, boolean, boolean, long, int, long, boolean, long, boolean) */ - public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis, boolean lifo) { + public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis, boolean lifo, boolean fairness) { _maxIdle = maxIdle; _minIdle = minIdle; _maxActive = maxActive; @@ -221,6 +221,7 @@ _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; _softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; _lifo = lifo; + _fairness = fairness; _factory = factory; } @@ -225,7 +226,7 @@ } public ObjectPool createPool() { - return new GenericObjectPool(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_minIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle,_softMinEvictableIdleTimeMillis,_lifo); + return new GenericObjectPool(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_minIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle,_softMinEvictableIdleTimeMillis,_lifo, _fairness); } protected int _maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE; @@ -241,6 +242,7 @@ protected long _minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; protected long _softMinEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; protected boolean _lifo = GenericObjectPool.DEFAULT_LIFO; + protected boolean _fairness = GenericObjectPool.DEFAULT_FAIRNESS; protected PoolableObjectFactory _factory = null; Index: org/apache/commons/pool/impl/FairnessSemaphore.java =================================================================== --- org/apache/commons/pool/impl/FairnessSemaphore.java (revision 0) +++ org/apache/commons/pool/impl/FairnessSemaphore.java (revision 0) @@ -0,0 +1,114 @@ +package org.apache.commons.pool.impl; + +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Semaphore with fairness feature. + * @author Takayuki Kaneko + */ +public class FairnessSemaphore { + private ThreadLocal depth; + + private int currentCount; + + private int size; + + private List latches; + + public FairnessSemaphore(int size) { + depth = new ThreadLocal(); + latches = new LinkedList(); + currentCount = 0; + this.size = size; + } + + public void acquire() throws InterruptedException { + acquire(-1); + } + + public void acquire(long waitTime) throws InterruptedException { + if (depth.get() != null && ((Integer) depth.get()).intValue() > 0) { + depth.set(new Integer(((Integer) depth.get()).intValue() + 1)); + } else { + TimerLatch l = null; + synchronized (this) { + currentCount++; + if (currentCount > size) { + l = new TimerLatch(); + latches.add(l); + } + } + if (l != null) { + try { + l.checkAndWait(waitTime); + } catch (NoSuchElementException e) { + synchronized (this) { + currentCount--; + latches.remove(l); + } + throw e; + } catch (InterruptedException e) { + synchronized (this) { + currentCount--; + latches.remove(l); + } + throw e; + } + } + depth.set(new Integer(1)); + } + } + + + public void release() { + if (depth.get() == null || ((Integer) depth.get()).intValue() == 0) + throw new IllegalStateException(); + depth.set(new Integer(((Integer) depth.get()).intValue() - 1)); + if (((Integer) depth.get()).intValue() == 0) { + depth.remove(); + synchronized (this) { + currentCount--; + if (latches.size() > 0) { + ((TimerLatch) latches.get(0)).notifyLatch(); + latches.remove(0); + } + } + } + } + + public synchronized void setSize(int size) { + this.size = size; + } + + private static class TimerLatch { + private boolean notified = false; + private long startTime; + + public synchronized void checkAndWait(long waitTime) + throws InterruptedException { + startTime = System.currentTimeMillis(); + if (waitTime <= 0) { + while (!notified) { + wait(); + } + } else { + while (!notified) { + long remainTime = startTime + waitTime + - System.currentTimeMillis(); + if (remainTime <= 0) { + throw new NoSuchElementException( + "Timeout waiting for acuiring semaphore"); + } + wait(remainTime); + } + } + } + + public synchronized void notifyLatch() { + notified = true; + notify(); + } + } +}