You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Codievilky August (Jira)" <ji...@apache.org> on 2020/12/16 09:13:00 UTC

[jira] [Updated] (POOL-391) GenericKeyedObjectPool is not thread safe when invoke method `borrowObject` and `destroy` simultaneously

     [ https://issues.apache.org/jira/browse/POOL-391?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Codievilky August updated POOL-391:
-----------------------------------
    Description: 
The method `brrowObject` is not thread safe with `destroy` or `clear` method.

The reason is when use GenericKeyedObjectPool#destroy method,it did not ensure the *Atomicity* of destroy object from the ObjectDeque。

This may cause in the GenericKeyedObjectPool#borrowObject method,may get the wrong number of GenericKeyedObjectPool.ObjectDeque#getCreateCount when need to create. And the  GenericKeyedObjectPool#borrowObject will block until timeout.

Here is the sample test code to recur the bug:
{code:java}
// code placeholder
public class CommonPoolMultiThreadTest {
  public static void main(String[] args) {
    GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
    config.setMaxTotalPerKey(1);
    config.setMinIdlePerKey(0);
    config.setMaxIdlePerKey(-1);
    config.setMaxTotal(-1);
    config.setMaxWaitMillis(TimeUnit.SECONDS.toMillis(5));
    GenericKeyedObjectPool<Integer, Integer> testPool = new GenericKeyedObjectPool<>(
        new KeyedPooledObjectFactory<Integer, Integer>() {
          @Override
          public PooledObject<Integer> makeObject(Integer key) throws Exception {
            System.out.println("start to create");
            return new DefaultPooledObject<>(10);
          }          @Override
          public void destroyObject(Integer key, PooledObject<Integer> p) throws Exception {
            System.out.println("start to destroy");
            Thread.sleep(2000);
          }          @Override
          public boolean validateObject(Integer key, PooledObject<Integer> p) {
            return true;
          }          @Override
          public void activateObject(Integer key, PooledObject<Integer> p) throws Exception {
            // do nothing
          }          @Override
          public void passivateObject(Integer key, PooledObject<Integer> p) throws Exception {
            // do nothing
          }
        }, config
    );
    int borrowKey = 10;
    Thread t = new Thread(() -> {
      try {
        while (true) {
          Integer integer = testPool.borrowObject(borrowKey);
          testPool.returnObject(borrowKey, integer);
          Thread.sleep(10);
        }
      } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
      }
    });
    Thread t2 = new Thread(() -> {
      try {
        while (true) {
          testPool.clear(borrowKey);
          Thread.sleep(10);
        }
      } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
      }
    });
    t.start();
    t2.start();
  }
}

{code}
 

  was:
The method `brrowObject` is not thread safe with `destroy` or `clear` method.

The reason is when use GenericKeyedObjectPool#destroy method,it did not ensure the *Atomicity* of destroy object from the ObjectDeque。

This may cause in the GenericKeyedObjectPool#borrowObject method,may get the wrong number of GenericKeyedObjectPool.ObjectDeque#getCreateCount when need to create. And the  GenericKeyedObjectPool#borrowObject will block until timeout.

Here is the sample test code to recur the bug:
{code:java}
// code placeholder
public class CommonPoolMultiThreadTest {
  public static void main(String[] args) {
    GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
    config.setMaxTotalPerKey(1);
    config.setMinIdlePerKey(0);
    config.setMaxIdlePerKey(-1);
    config.setMaxTotal(-1);
    config.setMaxWaitMillis(TimeUnit.SECONDS.toMillis(5));
    GenericKeyedObjectPool<Integer, Integer> testPool = new GenericKeyedObjectPool<>(
        new KeyedPooledObjectFactory<Integer, Integer>() {
          @Override
          public PooledObject<Integer> makeObject(Integer key) throws Exception {
            System.out.println("start to create");
            return new DefaultPooledObject<>(10);
          }          @Override
          public void destroyObject(Integer key, PooledObject<Integer> p) throws Exception {
            System.out.println("start to destroy");
            Thread.sleep(2000);
          }          @Override
          public boolean validateObject(Integer key, PooledObject<Integer> p) {
            return true;
          }          @Override
          public void activateObject(Integer key, PooledObject<Integer> p) throws Exception {
            // do nothing
          }          @Override
          public void passivateObject(Integer key, PooledObject<Integer> p) throws Exception {
            // do nothing
          }
        }, config
    );
    int borrowKey = 10;
    Thread t = new Thread(() -> {
      try {
        while (true) {
          testPool.borrowObject(borrowKey);
          Thread.sleep(10);
        }
      } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
      }
    });
    Thread t2 = new Thread(() -> {
      try {
        while (true) {
          testPool.clear(borrowKey);
          Thread.sleep(10);
        }
      } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
      }
    });
    t.start();
    t2.start();
  }
}

{code}
 


> GenericKeyedObjectPool is not thread safe when invoke method `borrowObject` and `destroy`  simultaneously
> ---------------------------------------------------------------------------------------------------------
>
>                 Key: POOL-391
>                 URL: https://issues.apache.org/jira/browse/POOL-391
>             Project: Commons Pool
>          Issue Type: Bug
>    Affects Versions: 2.4.2, 2.5.0, 2.6.0, 2.7.0, 2.8.0, 2.9.0
>            Reporter: Codievilky August
>            Priority: Blocker
>   Original Estimate: 24h
>  Remaining Estimate: 24h
>
> The method `brrowObject` is not thread safe with `destroy` or `clear` method.
> The reason is when use GenericKeyedObjectPool#destroy method,it did not ensure the *Atomicity* of destroy object from the ObjectDeque。
> This may cause in the GenericKeyedObjectPool#borrowObject method,may get the wrong number of GenericKeyedObjectPool.ObjectDeque#getCreateCount when need to create. And the  GenericKeyedObjectPool#borrowObject will block until timeout.
> Here is the sample test code to recur the bug:
> {code:java}
> // code placeholder
> public class CommonPoolMultiThreadTest {
>   public static void main(String[] args) {
>     GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
>     config.setMaxTotalPerKey(1);
>     config.setMinIdlePerKey(0);
>     config.setMaxIdlePerKey(-1);
>     config.setMaxTotal(-1);
>     config.setMaxWaitMillis(TimeUnit.SECONDS.toMillis(5));
>     GenericKeyedObjectPool<Integer, Integer> testPool = new GenericKeyedObjectPool<>(
>         new KeyedPooledObjectFactory<Integer, Integer>() {
>           @Override
>           public PooledObject<Integer> makeObject(Integer key) throws Exception {
>             System.out.println("start to create");
>             return new DefaultPooledObject<>(10);
>           }          @Override
>           public void destroyObject(Integer key, PooledObject<Integer> p) throws Exception {
>             System.out.println("start to destroy");
>             Thread.sleep(2000);
>           }          @Override
>           public boolean validateObject(Integer key, PooledObject<Integer> p) {
>             return true;
>           }          @Override
>           public void activateObject(Integer key, PooledObject<Integer> p) throws Exception {
>             // do nothing
>           }          @Override
>           public void passivateObject(Integer key, PooledObject<Integer> p) throws Exception {
>             // do nothing
>           }
>         }, config
>     );
>     int borrowKey = 10;
>     Thread t = new Thread(() -> {
>       try {
>         while (true) {
>           Integer integer = testPool.borrowObject(borrowKey);
>           testPool.returnObject(borrowKey, integer);
>           Thread.sleep(10);
>         }
>       } catch (Exception e) {
>         e.printStackTrace();
>         System.exit(1);
>       }
>     });
>     Thread t2 = new Thread(() -> {
>       try {
>         while (true) {
>           testPool.clear(borrowKey);
>           Thread.sleep(10);
>         }
>       } catch (Exception e) {
>         e.printStackTrace();
>         System.exit(1);
>       }
>     });
>     t.start();
>     t2.start();
>   }
> }
> {code}
>  



--
This message was sent by Atlassian Jira
(v8.3.4#803005)