Linton Miller created HTTPCORE-589:
--------------------------------------

             Summary: LaxConnPool may not allocate available connection
                 Key: HTTPCORE-589
                 URL: https://issues.apache.org/jira/browse/HTTPCORE-589
             Project: HttpComponents HttpCore
          Issue Type: Bug
          Components: HttpCore
    Affects Versions: 5.0-beta8
            Reporter: Linton Miller


The LaxConnPool can fail to allocate available connections from the pool in 
rare circumstances.

The issue happens when 2 threads execute concurrently; one requesting a lease 
while another releases a pool entry.

Thread 1
future = lease
  getAvailableEntry = null
  allocatePoolEntry = false
  <about to add a LeaseRequest to pending queue>
Thread 2
release
  removeLeased
  available.add
  servicePendingRequest
    while (pending.poll() != null)  does nothing because pending queue is empty
  <release call completed, with an entry now in the available list>
Thread 1
  pending.add(new LeaseRequest())
  <lease call completed, with an unsatisfied future>
future.get()

And now Thread 1 is blocked until there is another release call, even though 
there is actually an available connection in the pool.

The problem arises because there is a window between checking for an available 
pool entry and registering on the pending request queue. In that window, the 
statte of the pool can change.

Recreating this problem is test is extremely difficult, because the window for 
failure is really very small. I've only be able to observe the error in running 
code by introducing synchronization or delay (as small as 3ms, but still 
needed) into the pool code just before the lease pending.add, so as to ensure 
the necessary timing condition between threads exist. My test class:
{code:java}
import org.apache.hc.core5.io.ModalCloseable;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.pool.PoolEntry;
import org.apache.hc.core5.pool.LaxConnPool;
import org.apache.hc.core5.pool.PoolEntry;
import org.apache.hc.core5.util.TimeValue;

import java.util.concurrent.Future;


public class PoolPendTest {

  private static class TestConn implements ModalCloseable {
    public void close() {
    }
    public void close(CloseMode mode) {
    }
  }

  private static LaxConnPool<String,TestConn> pool;

  private static void dbg(String msg) {
    System.out.println(Thread.currentThread().getName()+": 
"+System.currentTimeMillis()+": "+msg);
  }

  public static void main(String[] args) {
    final int poolSize = 1;
    pool = new FixPendPool<>(poolSize);
    // Create a pool entry purely to prime class loading
    new PoolEntry<>("", TimeValue.NEG_ONE_MILLISECONDS);
    
    for (int i=0;i<poolSize*2;i++) {
      new Thread(String.format("req-%04d", i)) {
        @Override
        public void run() {
          dbg("Started");
          Future<PoolEntry<String,TestConn>> req = pool.lease("somehost",null);
          PoolEntry<String,TestConn> entry = null;
          if (req.isDone()) {
            try {
              entry = req.get();
              if (!entry.hasConnection()) {
                entry.assignConnection(new TestConn());
                pool.release(entry, true);
                dbg("Released pool entry "+System.identityHashCode(entry));
              }
            }
            catch (Exception shouldNotHappen) {
              dbg("Should not happen!");
              shouldNotHappen.printStackTrace();
            }
          }
          try {
            dbg("Assigned pool entry "+System.identityHashCode(req.get()));
          }
          catch (Exception fail) {
            dbg("Getting pool entry failed");
            fail.printStackTrace();
          }
        }
      }.start();
    }
  }
}
{code}
When I modify the LaxConnPool code to add a 3ms delay just before adding to the 
pend queue:
{code:java}
    try { Thread.sleep(3); } catch (InterruptedException onward) {}
    pending.add(new LeaseRequest<>(state, requestTimeout, future));
{code}
then the test class blocks with an infinite wait where the pool contains a 
returned connection, but it's never assigned to the second thread.



--
This message was sent by Atlassian JIRA
(v7.6.14#76016)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to