[
https://issues.apache.org/jira/browse/POOL-419?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
john ms updated POOL-419:
-------------------------
Description:
We've notice unexpected counting that is return from getNumActive .
and it means that allObjects and idleObjects are out of sync.
Calling returnObject and invalidateObject on the same pooled object from two
different threads is causing getNumActive to return negative value.
Why two threads are working on the same object at the same time and the real
running use case is much complex to described and not relevant, I think.
Bellow is a simple program to demo the behavior
{*}Expected result{*}:
getNumActive=0
{*}Actual result{*}:
getNumActive=\{negative number}
If it help debugging the RC, I've notice that returnObject is not synchronized
over pooled object and verify it's status before using it, like it's done in
invalidateObject.
{*}Workaround{*}:
Synchronize the pooled object before calling returnObject/invalidateObject
Java 17
{code:java}
public class PoolObject extends BasePooledObjectFactory<PoolObject> {
@Override
public PoolObject create() {
return new PoolObject();
}
@Override
public PooledObject<PoolObject> wrap(PoolObject poolObject) {
return new DefaultPooledObject<>(poolObject);
}
@Override
public void passivateObject(PooledObject<PoolObject> pooledObject) {
}
}{code}
{code:java}
public static void main (String [] args) throws Exception {
ExecutorService executor = Executors.newCachedThreadPool();
GenericObjectPoolConfig<PoolObject> poolConfig = new
GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(200000);
poolConfig.setMaxIdle(200000);
poolConfig.setMinIdle(200000);
ObjectPool<PoolObject> objectPool = new GenericObjectPool<>(new
PoolObject(), poolConfig);
for (int i=0; i<1000000; i++){
PoolObject poolObject= objectPool.borrowObject();
FutureTask invalidateObject = new FutureTask<>(() -> {
Thread.sleep(RandomUtils.nextInt(0,10));
//synchronized (poolObject) { // Workaround bug
objectPool.invalidateObject(poolObject);
//}
return true;
});
executor.execute(invalidateObject);
FutureTask returnObject = new FutureTask<>(() -> {
Thread.sleep(RandomUtils.nextInt(0,10));
//synchronized (poolObject) { // Workaround bug
objectPool.returnObject(poolObject);
//}
return true;
});
executor.execute(returnObject);
}
Thread.sleep(2000);
executor.shutdown();
System.out.println("getNumActive=" + objectPool.getNumActive());
}{code}
was:
We've notice unexpected counting that is return from getNumActive .
and it means that allObjects and idleObjects are out of sync.
Calling returnObject and invalidateObject on the same pooled object from two
different threads is causing getNumActive to return negative value.
Why two threads are working on the same object at the same time and the real
running use case is much complex to described and not relevant, I think.
Bellow is a simple program to demo the behavior
{*}Expected result{*}:
getNumActive=0
{*}Actual result{*}:
getNumActive=\{negative number}
If it help debugging the RC, I've notice that returnObject is not synchronized
over pooled object and verify it's status before using it, like it's done in
invalidateObject.
{*}Workaround{*}:
Synchronize the pooled object before calling returnObject/invalidateObject
> GenericObjectPoolConfig getNumActive return negative value
> ----------------------------------------------------------
>
> Key: POOL-419
> URL: https://issues.apache.org/jira/browse/POOL-419
> Project: Commons Pool
> Issue Type: Bug
> Affects Versions: 2.12.0
> Environment:
> Java 17
> {code:java}
> public class PoolObject extends BasePooledObjectFactory<PoolObject> {
> @Override
> public PoolObject create() {
> return new PoolObject();
> }
> @Override
> public PooledObject<PoolObject> wrap(PoolObject poolObject) {
> return new DefaultPooledObject<>(poolObject);
> }
> @Override
> public void passivateObject(PooledObject<PoolObject> pooledObject) {
> }
> }{code}
>
> {code:java}
> public static void main (String [] args) throws Exception {
> ExecutorService executor = Executors.newCachedThreadPool();
> GenericObjectPoolConfig<PoolObject> poolConfig = new
> GenericObjectPoolConfig<>();
> poolConfig.setMaxTotal(200000);
> poolConfig.setMaxIdle(200000);
> poolConfig.setMinIdle(200000);
> ObjectPool<PoolObject> objectPool = new GenericObjectPool<>(new
> PoolObject(), poolConfig);
> for (int i=0; i<1000000; i++){
> PoolObject poolObject= objectPool.borrowObject();
> FutureTask invalidateObject = new FutureTask<>(() -> {
> Thread.sleep(RandomUtils.nextInt(0,10));
> //synchronized (poolObject) { // Workaround bug
> objectPool.invalidateObject(poolObject);
> //}
> return true;
> });
> executor.execute(invalidateObject);
> FutureTask returnObject = new FutureTask<>(() -> {
> Thread.sleep(RandomUtils.nextInt(0,10));
> //synchronized (poolObject) { // Workaround bug
> objectPool.returnObject(poolObject);
> //}
> return true;
> });
> executor.execute(returnObject);
> }
> Thread.sleep(2000);
> executor.shutdown();
> System.out.println("getNumActive=" + objectPool.getNumActive());
> }{code}
>
>
>
> Reporter: john ms
> Priority: Major
>
> We've notice unexpected counting that is return from getNumActive .
> and it means that allObjects and idleObjects are out of sync.
> Calling returnObject and invalidateObject on the same pooled object from two
> different threads is causing getNumActive to return negative value.
> Why two threads are working on the same object at the same time and the real
> running use case is much complex to described and not relevant, I think.
> Bellow is a simple program to demo the behavior
> {*}Expected result{*}:
> getNumActive=0
>
> {*}Actual result{*}:
> getNumActive=\{negative number}
> If it help debugging the RC, I've notice that returnObject is not
> synchronized over pooled object and verify it's status before using it, like
> it's done in invalidateObject.
> {*}Workaround{*}:
> Synchronize the pooled object before calling returnObject/invalidateObject
>
> Java 17
> {code:java}
> public class PoolObject extends BasePooledObjectFactory<PoolObject> {
> @Override
> public PoolObject create() {
> return new PoolObject();
> }
> @Override
> public PooledObject<PoolObject> wrap(PoolObject poolObject) {
> return new DefaultPooledObject<>(poolObject);
> }
> @Override
> public void passivateObject(PooledObject<PoolObject> pooledObject) {
> }
> }{code}
>
> {code:java}
> public static void main (String [] args) throws Exception {
> ExecutorService executor = Executors.newCachedThreadPool();
> GenericObjectPoolConfig<PoolObject> poolConfig = new
> GenericObjectPoolConfig<>();
> poolConfig.setMaxTotal(200000);
> poolConfig.setMaxIdle(200000);
> poolConfig.setMinIdle(200000);
> ObjectPool<PoolObject> objectPool = new GenericObjectPool<>(new
> PoolObject(), poolConfig);
> for (int i=0; i<1000000; i++){
> PoolObject poolObject= objectPool.borrowObject();
> FutureTask invalidateObject = new FutureTask<>(() -> {
> Thread.sleep(RandomUtils.nextInt(0,10));
> //synchronized (poolObject) { // Workaround bug
> objectPool.invalidateObject(poolObject);
> //}
> return true;
> });
> executor.execute(invalidateObject);
> FutureTask returnObject = new FutureTask<>(() -> {
> Thread.sleep(RandomUtils.nextInt(0,10));
> //synchronized (poolObject) { // Workaround bug
> objectPool.returnObject(poolObject);
> //}
> return true;
> });
> executor.execute(returnObject);
> }
> Thread.sleep(2000);
> executor.shutdown();
> System.out.println("getNumActive=" + objectPool.getNumActive());
> }{code}
>
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)