Phil Steitz created POOL-386:
--------------------------------

             Summary: Closing a pool can cause Evictor in another pool to be 
cancelled
                 Key: POOL-386
                 URL: https://issues.apache.org/jira/browse/POOL-386
             Project: Commons Pool
          Issue Type: Task
    Affects Versions: 2.8.0, 2.7.0, 2.6.2, 2.6.1
            Reporter: Phil Steitz
             Fix For: 2.8.1


The fix for POOL-337 introduced a race condition that can cause the shared 
EvictionTimer to be cancelled when a pool is closed but another pool still has 
an active Evictor.  The EvictionTimer cancel method used to cancel an eviction 
task owned by a client pool uses this test to determine whether or not to 
shutdown its executor:
{code:java}
 if (executor != null && executor.getQueue().isEmpty()){code}
The executor may report an empty queue if it is executing a task.  This will 
cause the executor to be shut down and scheduling of new tasks to stop.

The unit test below illustrates the problem.
{code:java}
 
public void testEvictionTimerMultiplePools() throws InterruptedException {
        final AtomicIntegerFactory factory = new AtomicIntegerFactory();
        factory.setValidateLatency(50);
        final GenericObjectPool<AtomicInteger> evictingPool = new 
GenericObjectPool<>(factory);
        evictingPool.setTimeBetweenEvictionRunsMillis(100);
        evictingPool.setNumTestsPerEvictionRun(5);
        evictingPool.setTestWhileIdle(true);
        evictingPool.setMinEvictableIdleTimeMillis(50);
        for (int i = 0; i < 10; i++) {
            try {
                evictingPool.addObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


        for (int i = 0; i < 1000; i++) {
            GenericObjectPool<AtomicInteger> nonEvictingPool = new 
GenericObjectPool<>(factory);
            nonEvictingPool.close();
        }


        Thread.sleep(1000);
        Assert.assertEquals(0, evictingPool.getNumIdle());
        evictingPool.close();
    }

{code}
Proposed fix:

Change the test guarding executor shutdown to  
{code:java}
 executor.getQueue().isEmpty() && executor.getActiveCount() == 0
{code}
This still exposes a low-probability race - a task completes after the isEmpty 
test and is requeued before the activecount test - but this is very unlikely.




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

Reply via email to