You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ma...@apache.org on 2009/05/12 20:07:44 UTC
svn commit: r774007 - in
/commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl:
GenericKeyedObjectPool.java GenericObjectPool.java
Author: markt
Date: Tue May 12 18:07:44 2009
New Revision: 774007
URL: http://svn.apache.org/viewvc?rev=774007&view=rev
Log:
Fix POOL-125
Ensure we don't call factory methods from inside a sync block to prevent deadlocks like DBCP-44
Modified:
commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java
commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericObjectPool.java
Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java?rev=774007&r1=774006&r2=774007&view=diff
==============================================================================
--- commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java (original)
+++ commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java Tue May 12 18:07:44 2009
@@ -17,8 +17,11 @@
package org.apache.commons.pool.impl;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -183,6 +186,11 @@
* non-<code>null</code> factory must be provided either as a constructor argument
* or via a call to {@link #setFactory setFactory} before the pool is used.
* </p>
+ * <p>
+ * Implementation note: To prevent possible deadlocks, care has been taken to
+ * ensure that no call to a factory method will occur within a synchronization
+ * block. See POOL-125 and DBCP-44 for more information.
+ * </p>
* @see GenericObjectPool
* @author Rodney Waldhoff
* @author Dirk Verbeeck
@@ -1058,24 +1066,21 @@
/**
* Clears the pool, removing all pooled instances.
*/
- public synchronized void clear() {
- for(Iterator entries = _poolMap.entrySet().iterator(); entries.hasNext(); ) {
- final Map.Entry entry = (Map.Entry)entries.next();
- final Object key = entry.getKey();
- final CursorableLinkedList list = ((ObjectQueue)(entry.getValue())).queue;
- for(Iterator it = list.iterator(); it.hasNext(); ) {
- try {
- _factory.destroyObject(key,((ObjectTimestampPair)(it.next())).value);
- } catch(Exception e) {
- // ignore error, keep destroying the rest
- }
+ public void clear() {
+ Map toDestroy = new HashMap();
+ synchronized (this) {
+ for (Iterator it = _poolMap.keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ ObjectQueue pool = (ObjectQueue)_poolMap.get(key);
+ toDestroy.put(key, pool.queue);
it.remove();
+ _poolList.remove(key);
+ _totalIdle = _totalIdle - pool.queue.size();
+ _totalInternalProcessing =
+ _totalInternalProcessing + pool.queue.size();
}
}
- _poolMap.clear();
- _poolList.clear();
- _totalIdle = 0;
- notifyAll();
+ destroy(toDestroy);
}
/**
@@ -1083,50 +1088,59 @@
* objects into a TreeMap and then iterates the first 15% for removal
* @since Pool 1.3
*/
- public synchronized void clearOldest() {
+ public void clearOldest() {
+ // Map of objects to destroy my key
+ final Map toDestroy = new HashMap();
+
// build sorted map of idle objects
final Map map = new TreeMap();
- for (Iterator keyiter = _poolMap.keySet().iterator(); keyiter.hasNext();) {
- final Object key = keyiter.next();
- final CursorableLinkedList list = ((ObjectQueue)_poolMap.get(key)).queue;
- for (Iterator it = list.iterator(); it.hasNext();) {
- // each item into the map uses the objectimestamppair object
- // as the key. It then gets sorted based on the timstamp field
- // each value in the map is the parent list it belongs in.
- map.put(it.next(), key);
+ synchronized (this) {
+ for (Iterator keyiter = _poolMap.keySet().iterator(); keyiter.hasNext();) {
+ final Object key = keyiter.next();
+ final CursorableLinkedList list = ((ObjectQueue)_poolMap.get(key)).queue;
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ // each item into the map uses the objectimestamppair object
+ // as the key. It then gets sorted based on the timstamp field
+ // each value in the map is the parent list it belongs in.
+ map.put(it.next(), key);
+ }
}
- }
- // Now iterate created map and kill the first 15% plus one to account for zero
- Set setPairKeys = map.entrySet();
- int itemsToRemove = ((int) (map.size() * 0.15)) + 1;
-
- Iterator iter = setPairKeys.iterator();
- while (iter.hasNext() && itemsToRemove > 0) {
- Map.Entry entry = (Map.Entry) iter.next();
- // kind of backwards on naming. In the map, each key is the objecttimestamppair
- // because it has the ordering with the timestamp value. Each value that the
- // key references is the key of the list it belongs to.
- Object key = entry.getValue();
- ObjectTimestampPair pairTimeStamp = (ObjectTimestampPair) entry.getKey();
- final CursorableLinkedList list =
- ((ObjectQueue)(_poolMap.get(key))).queue;
- list.remove(pairTimeStamp);
+ // Now iterate created map and kill the first 15% plus one to account for zero
+ Set setPairKeys = map.entrySet();
+ int itemsToRemove = ((int) (map.size() * 0.15)) + 1;
+
+ Iterator iter = setPairKeys.iterator();
+ while (iter.hasNext() && itemsToRemove > 0) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ // kind of backwards on naming. In the map, each key is the objecttimestamppair
+ // because it has the ordering with the timestamp value. Each value that the
+ // key references is the key of the list it belongs to.
+ Object key = entry.getValue();
+ ObjectTimestampPair pairTimeStamp = (ObjectTimestampPair) entry.getKey();
+ final CursorableLinkedList list =
+ ((ObjectQueue)(_poolMap.get(key))).queue;
+ list.remove(pairTimeStamp);
- try {
- _factory.destroyObject(key, pairTimeStamp.value);
- } catch (Exception e) {
- // ignore error, keep destroying the rest
- }
- // if that was the last object for that key, drop that pool
- if (list.isEmpty()) {
- _poolMap.remove(key);
- _poolList.remove(key);
+ if (toDestroy.containsKey(key)) {
+ ((List)toDestroy.get(key)).add(pairTimeStamp);
+ } else {
+ List listForKey = new ArrayList();
+ listForKey.add(pairTimeStamp);
+ toDestroy.put(key, listForKey);
+ }
+ // if that was the last object for that key, drop that pool
+ if (list.isEmpty()) {
+ _poolMap.remove(key);
+ _poolList.remove(key);
+ }
+ _totalIdle--;
+ _totalInternalProcessing++;
+ itemsToRemove--;
}
- _totalIdle--;
- itemsToRemove--;
+
}
- notifyAll();
+ destroy(toDestroy);
}
/**
@@ -1134,24 +1148,48 @@
*
* @param key the key to clear
*/
- public synchronized void clear(Object key) {
- final ObjectQueue pool = (ObjectQueue)(_poolMap.remove(key));
- if(null == pool) {
- return;
- } else {
- _poolList.remove(key);
- for(Iterator it = pool.queue.iterator(); it.hasNext(); ) {
+ public void clear(Object key) {
+ Map toDestroy = new HashMap();
+
+ final ObjectQueue pool;
+ synchronized (this) {
+ pool = (ObjectQueue)(_poolMap.remove(key));
+ if (pool == null) {
+ return;
+ } else {
+ _poolList.remove(key);
+ }
+ _totalIdle = _totalIdle - pool.queue.size();
+ _totalInternalProcessing =
+ _totalInternalProcessing + pool.queue.size();
+ toDestroy.put(key, pool.queue);
+ }
+ destroy(toDestroy);
+ }
+
+ /*
+ * Assuming Map<Object,Collection<ObjectTimestampPair>>, destroy all
+ * ObjectTimestampPair.value
+ */
+ private void destroy(Map m) {
+ for (Iterator keys = m.keySet().iterator(); keys.hasNext();) {
+ Object key = keys.next();
+ Collection c = (Collection) m.get(key);
+ for (Iterator it = c.iterator(); it.hasNext();) {
try {
- _factory.destroyObject(key,((ObjectTimestampPair)(it.next())).value);
+ _factory.destroyObject(
+ key,((ObjectTimestampPair)(it.next())).value);
} catch(Exception e) {
// ignore error, keep destroying the rest
+ } finally {
+ synchronized(this) {
+ _totalInternalProcessing--;
+ notifyAll();
+ }
}
- it.remove();
- _totalIdle--;
}
+
}
-
- notifyAll();
}
/**
@@ -1374,14 +1412,30 @@
}
}
- public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException {
- assertOpen();
- if(0 < getNumActive()) {
- throw new IllegalStateException("Objects are already active");
- } else {
- clear();
- _factory = factory;
+ public void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException {
+ Map toDestroy = new HashMap();
+ synchronized (this) {
+ assertOpen();
+ if(0 < getNumActive()) {
+ throw new IllegalStateException("Objects are already active");
+ } else {
+ for (Iterator it = _poolMap.keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ ObjectQueue pool = (ObjectQueue)_poolMap.get(key);
+ if (pool != null) {
+ toDestroy.put(key, pool.queue);
+ it.remove();
+ _poolList.remove(key);
+ _totalIdle = _totalIdle - pool.queue.size();
+ _totalInternalProcessing =
+ _totalInternalProcessing + pool.queue.size();
+ }
+ }
+ destroy(toDestroy);
+ _factory = factory;
+ }
}
+ destroy(toDestroy);
}
/**
@@ -1398,57 +1452,38 @@
*
* @throws Exception when there is a problem evicting idle objects.
*/
- public synchronized void evict() throws Exception {
+ public void evict() throws Exception {
// Initialize key to last key value
Object key = null;
- if (_evictionKeyCursor != null &&
- _evictionKeyCursor._lastReturned != null) {
- key = _evictionKeyCursor._lastReturned.value();
+ synchronized (this) {
+ if (_evictionKeyCursor != null &&
+ _evictionKeyCursor._lastReturned != null) {
+ key = _evictionKeyCursor._lastReturned.value();
+ }
}
for (int i=0,m=getNumTests(); i<m; i++) {
- // make sure pool map is not empty; otherwise do nothing
- if (_poolMap == null || _poolMap.size() == 0) {
- continue;
- }
-
- // if we don't have a key cursor, then create one
- if (null == _evictionKeyCursor) {
- resetEvictionKeyCursor();
- key = null;
- }
-
- // if we don't have an object cursor, create one
- if (null == _evictionCursor) {
- // if the _evictionKeyCursor has a next value, use this key
- if (_evictionKeyCursor.hasNext()) {
- key = _evictionKeyCursor.next();
- resetEvictionObjectCursor(key);
- } else {
- // Reset the key cursor and try again
- resetEvictionKeyCursor();
- if (_evictionKeyCursor != null) {
- if (_evictionKeyCursor.hasNext()) {
- key = _evictionKeyCursor.next();
- resetEvictionObjectCursor(key);
- }
- }
+ final ObjectTimestampPair pair;
+ synchronized (this) {
+ // make sure pool map is not empty; otherwise do nothing
+ if (_poolMap == null || _poolMap.size() == 0) {
+ continue;
}
- }
- if (_evictionCursor == null) {
- continue; // should never happen; do nothing
- }
+ // if we don't have a key cursor, then create one
+ if (null == _evictionKeyCursor) {
+ resetEvictionKeyCursor();
+ key = null;
+ }
- // If eviction cursor is exhausted, try to move
- // to the next key and reset
- if((_lifo && !_evictionCursor.hasPrevious()) ||
- (!_lifo && !_evictionCursor.hasNext())) {
- if (_evictionKeyCursor != null) {
+ // if we don't have an object cursor, create one
+ if (null == _evictionCursor) {
+ // if the _evictionKeyCursor has a next value, use this key
if (_evictionKeyCursor.hasNext()) {
key = _evictionKeyCursor.next();
resetEvictionObjectCursor(key);
- } else { // Need to reset Key cursor
+ } else {
+ // Reset the key cursor and try again
resetEvictionKeyCursor();
if (_evictionKeyCursor != null) {
if (_evictionKeyCursor.hasNext()) {
@@ -1457,19 +1492,47 @@
}
}
}
+ }
+
+ if (_evictionCursor == null) {
+ continue; // should never happen; do nothing
+ }
+
+ // If eviction cursor is exhausted, try to move
+ // to the next key and reset
+ if((_lifo && !_evictionCursor.hasPrevious()) ||
+ (!_lifo && !_evictionCursor.hasNext())) {
+ if (_evictionKeyCursor != null) {
+ if (_evictionKeyCursor.hasNext()) {
+ key = _evictionKeyCursor.next();
+ resetEvictionObjectCursor(key);
+ } else { // Need to reset Key cursor
+ resetEvictionKeyCursor();
+ if (_evictionKeyCursor != null) {
+ if (_evictionKeyCursor.hasNext()) {
+ key = _evictionKeyCursor.next();
+ resetEvictionObjectCursor(key);
+ }
+ }
+ }
+ }
}
- }
- if((_lifo && !_evictionCursor.hasPrevious()) ||
- (!_lifo && !_evictionCursor.hasNext())) {
- continue; // reset failed, do nothing
+ if((_lifo && !_evictionCursor.hasPrevious()) ||
+ (!_lifo && !_evictionCursor.hasNext())) {
+ continue; // reset failed, do nothing
+ }
+
+ // if LIFO and the _evictionCursor has a previous object,
+ // or FIFO and _evictionCursor has a next object, test it
+ pair = _lifo ?
+ (ObjectTimestampPair) _evictionCursor.previous() :
+ (ObjectTimestampPair) _evictionCursor.next();
+ _evictionCursor.remove();
+ _totalIdle--;
+ _totalInternalProcessing++;
}
- // if LIFO and the _evictionCursor has a previous object,
- // or FIFO and _evictionCursor has a next object, test it
- ObjectTimestampPair pair = _lifo ?
- (ObjectTimestampPair) _evictionCursor.previous() :
- (ObjectTimestampPair) _evictionCursor.next();
boolean removeObject=false;
if((_minEvictableIdleTimeMillis > 0) &&
(System.currentTimeMillis() - pair.tstamp >
@@ -1496,11 +1559,13 @@
}
}
}
+
if(removeObject) {
try {
- _evictionCursor.remove();
- _totalIdle--;
_factory.destroyObject(key, pair.value);
+ } catch(Exception e) {
+ // ignored
+ } finally {
// Do not remove the key from the _poolList or _poolmap,
// even if the list stored in the _poolMap for this key is
// empty when minIdle > 0.
@@ -1508,18 +1573,29 @@
// Otherwise if it was the last object for that key,
// drop that pool
if (_minIdle == 0) {
- ObjectQueue objectQueue =
- (ObjectQueue)_poolMap.get(key);
- if (objectQueue != null &&
- objectQueue.queue.isEmpty()) {
- _poolMap.remove(key);
- _poolList.remove(key);
+ synchronized (this) {
+ ObjectQueue objectQueue =
+ (ObjectQueue)_poolMap.get(key);
+ if (objectQueue != null &&
+ objectQueue.queue.isEmpty()) {
+ _poolMap.remove(key);
+ _poolList.remove(key);
+ }
}
}
- } catch(Exception e) {
- // ignored
}
}
+ synchronized (this) {
+ if(!removeObject) {
+ _evictionCursor.add(pair);
+ _totalIdle++;
+ if (_lifo) {
+ // Skip over the element we just added back
+ _evictionCursor.previous();
+ }
+ }
+ _totalInternalProcessing--;
+ }
}
}
Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericObjectPool.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericObjectPool.java?rev=774007&r1=774006&r2=774007&view=diff
==============================================================================
--- commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericObjectPool.java (original)
+++ commons/proper/pool/trunk/src/java/org/apache/commons/pool/impl/GenericObjectPool.java Tue May 12 18:07:44 2009
@@ -17,7 +17,10 @@
package org.apache.commons.pool.impl;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
import java.util.NoSuchElementException;
import java.util.TimerTask;
@@ -168,6 +171,10 @@
* GenericObjectPool is not usable without a {@link PoolableObjectFactory}. A
* non-<code>null</code> factory must be provided either as a constructor argument
* or via a call to {@link #setFactory} before the pool is used.
+ * <p>
+ * Implementation note: To prevent possible deadlocks, care has been taken to
+ * ensure that no call to a factory method will occur within a synchronization
+ * block. See POOL-125 and DBCP-44 for more information.
*
* @see GenericKeyedObjectPool
* @author Rodney Waldhoff
@@ -1037,17 +1044,34 @@
/**
* Clears any objects sitting idle in the pool.
*/
- public synchronized void clear() {
- for(Iterator it = _pool.iterator(); it.hasNext(); ) {
+ public void clear() {
+ List toDestroy = new ArrayList();
+
+ synchronized(this) {
+ toDestroy.addAll(_pool);
+ _numInternalProcessing = _numInternalProcessing + _pool._size;
+ _pool.clear();
+ }
+ destroy(toDestroy);
+ }
+
+ /*
+ * Private method to destroy all the objects in a collection. Assumes
+ * objects in the collection are instances of ObjectTimestampPair
+ */
+ private void destroy(Collection c) {
+ for (Iterator it = c.iterator(); it.hasNext();) {
try {
_factory.destroyObject(((ObjectTimestampPair)(it.next())).value);
} catch(Exception e) {
// ignore error, keep destroying the rest
+ } finally {
+ synchronized(this) {
+ _numInternalProcessing--;
+ notifyAll();
+ }
}
- it.remove();
}
- _pool.clear();
- notifyAll(); // num sleeping has changed
}
/**
@@ -1164,14 +1188,20 @@
* @param factory the {@link PoolableObjectFactory} used to create new instances.
* @throws IllegalStateException when the factory cannot be set at this time
*/
- public synchronized void setFactory(PoolableObjectFactory factory) throws IllegalStateException {
- assertOpen();
- if(0 < getNumActive()) {
- throw new IllegalStateException("Objects are already active");
- } else {
- clear();
+ public void setFactory(PoolableObjectFactory factory) throws IllegalStateException {
+ List toDestroy = new ArrayList();
+ synchronized (this) {
+ assertOpen();
+ if(0 < getNumActive()) {
+ throw new IllegalStateException("Objects are already active");
+ } else {
+ toDestroy.addAll(_pool);
+ _numInternalProcessing = _numInternalProcessing + _pool._size;
+ _pool.clear();
+ }
_factory = factory;
}
+ destroy(toDestroy);
}
/**
@@ -1187,61 +1217,83 @@
*
* @throws Exception if the pool is closed or eviction fails.
*/
- public synchronized void evict() throws Exception {
+ public void evict() throws Exception {
assertOpen();
- if(!_pool.isEmpty()) {
+ synchronized (this) {
+ if(_pool.isEmpty()) {
+ return;
+ }
if (null == _evictionCursor) {
_evictionCursor = (_pool.cursor(_lifo ? _pool.size() : 0));
}
- for (int i=0,m=getNumTests();i<m;i++) {
+ }
+
+ for (int i=0,m=getNumTests();i<m;i++) {
+ final ObjectTimestampPair pair;
+ synchronized (this) {
if ((_lifo && !_evictionCursor.hasPrevious()) ||
!_lifo && !_evictionCursor.hasNext()) {
_evictionCursor.close();
_evictionCursor = _pool.cursor(_lifo ? _pool.size() : 0);
}
- boolean removeObject = false;
- final ObjectTimestampPair pair = _lifo ?
- (ObjectTimestampPair) _evictionCursor.previous() :
- (ObjectTimestampPair) _evictionCursor.next();
- final long idleTimeMilis = System.currentTimeMillis() - pair.tstamp;
- if ((_minEvictableIdleTimeMillis > 0)
- && (idleTimeMilis > _minEvictableIdleTimeMillis)) {
- removeObject = true;
- } else if ((_softMinEvictableIdleTimeMillis > 0)
- && (idleTimeMilis > _softMinEvictableIdleTimeMillis)
- && (getNumIdle() > getMinIdle())) {
- removeObject = true;
+
+ pair = _lifo ?
+ (ObjectTimestampPair) _evictionCursor.previous() :
+ (ObjectTimestampPair) _evictionCursor.next();
+
+ _evictionCursor.remove();
+ _numInternalProcessing++;
+ }
+
+ boolean removeObject = false;
+ final long idleTimeMilis = System.currentTimeMillis() - pair.tstamp;
+ if ((getMinEvictableIdleTimeMillis() > 0)
+ && (idleTimeMilis > getMinEvictableIdleTimeMillis())) {
+ removeObject = true;
+ } else if ((getSoftMinEvictableIdleTimeMillis() > 0)
+ && (idleTimeMilis > getSoftMinEvictableIdleTimeMillis())
+ && ((getNumIdle() + 1)> getMinIdle())) { // +1 accounts for object we are processing
+ removeObject = true;
+ }
+ if(getTestWhileIdle() && !removeObject) {
+ boolean active = false;
+ try {
+ _factory.activateObject(pair.value);
+ active = true;
+ } catch(Exception e) {
+ removeObject=true;
}
- if(_testWhileIdle && !removeObject) {
- boolean active = false;
- try {
- _factory.activateObject(pair.value);
- active = true;
- } catch(Exception e) {
+ if(active) {
+ if(!_factory.validateObject(pair.value)) {
removeObject=true;
- }
- if(active) {
- if(!_factory.validateObject(pair.value)) {
+ } else {
+ try {
+ _factory.passivateObject(pair.value);
+ } catch(Exception e) {
removeObject=true;
- } else {
- try {
- _factory.passivateObject(pair.value);
- } catch(Exception e) {
- removeObject=true;
- }
}
}
}
- if(removeObject) {
- try {
- _evictionCursor.remove();
- _factory.destroyObject(pair.value);
- } catch(Exception e) {
- // ignored
+ }
+
+ if (removeObject) {
+ try {
+ _factory.destroyObject(pair.value);
+ } catch(Exception e) {
+ // ignored
+ }
+ }
+ synchronized (this) {
+ if(!removeObject) {
+ _evictionCursor.add(pair);
+ if (_lifo) {
+ // Skip over the element we just added back
+ _evictionCursor.previous();
}
}
+ _numInternalProcessing--;
}
- } // if !empty
+ }
}
/**