[ 
https://issues.apache.org/jira/browse/HTTPCORE-589?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16904846#comment-16904846
 ] 

Linton Miller commented on HTTPCORE-589:
----------------------------------------

Suggested fix in

https://github.com/apache/httpcomponents-core/pull/137

> 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
>            Priority: Minor
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> 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