Uploaded image for project: 'Commons Pool'
  1. Commons Pool
  2. POOL-391

GenericKeyedObjectPool is not thread safe when invoke method `borrowObject` and `destroy` simultaneously

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Blocker
    • Resolution: Fixed
    • 2.4.2, 2.5.0, 2.6.0, 2.7.0, 2.8.0, 2.9.0
    • 2.12.0
    • None

    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 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();
        }
      }
      
      

       

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              codievilky Codievilky August
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - 24h
                  24h
                  Remaining:
                  Remaining Estimate - 24h
                  24h
                  Logged:
                  Time Spent - Not Specified
                  Not Specified