[ 
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)

Reply via email to