Michael Ernst created HTTPASYNC-117:
---------------------------------------

             Summary: Blocking thread when executing a request in a 
FutureCallback
                 Key: HTTPASYNC-117
                 URL: https://issues.apache.org/jira/browse/HTTPASYNC-117
             Project: HttpComponents HttpAsyncClient
          Issue Type: Bug
    Affects Versions: 4.1.2
         Environment: Environment Oracle JDK 8u121
            Reporter: Michael Ernst
            Priority: Critical
         Attachments: blocking-thread-example-application.zip

When executing a request within a {{FutureCallback}} (inner request) the 
invoking thread is blocked. The inner request will not be executed and 
processing the request terminates after the write event.
If the inner request is executed in another thread, the application works fine 
(see the thenApplyAsync note in the code).

This issue seems already be an issue for another user who reported this in a 
StackOverflow question: 
http://stackoverflow.com/questions/32031846/apaches-httpasyncclient-never-returns-after-execute

See the following log when it blocks:
{noformat}
IM-WEBAPP 2017-01-20 09:27:44,506 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.client.MainClientExec  - [exchange: 4] start execution
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.client.protocol.RequestAddCookies  - CookieSpec selected: 
default
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.client.protocol.RequestAuthCache  - Auth cache not set in the 
context
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.client.InternalHttpAsyncClient  - [exchange: 4] 
Request connection for 
{tls}->http://localhost:3128->https://permissions-api.apps.bosch-iot-cloud.com:443
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager  - Connection 
request: [route: 
{tls}->http://localhost:3128->https://permissions-api.apps.bosch-iot-cloud.com:443][total
 kept alive: 1; route allocated: 1 of 20; total allocated: 1 of 200]
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionImpl  - 
http-outgoing-0 
127.0.0.1:57860<->127.0.0.1:3128[ACTIVE][r:r][ACTIVE][r][NOT_HANDSHAKING][0][0][0][0]:
 Set timeout 0
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager  - Connection 
leased: [id: http-outgoing-0][route: 
{tls}->http://localhost:3128->https://permissions-api.apps.bosch-iot-cloud.com:443][total
 kept alive: 0; route allocated: 1 of 20; total allocated: 1 of 200]
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.client.InternalHttpAsyncClient  - [exchange: 4] 
Connection allocated: CPoolProxy{http-outgoing-0 [ACTIVE]}
IM-WEBAPP 2017-01-20 09:27:44,507 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionImpl  - 
http-outgoing-0 
127.0.0.1:57860<->127.0.0.1:3128[ACTIVE][r:r][ACTIVE][r][NOT_HANDSHAKING][0][0][0][0]:
 Set attribute http.nio.exchange-handler
IM-WEBAPP 2017-01-20 09:27:44,508 [I/O dispatcher 1] DEBUG 
org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionImpl  - 
http-outgoing-0 
127.0.0.1:57860<->127.0.0.1:3128[ACTIVE][rw:r][ACTIVE][rw][NOT_HANDSHAKING][0][0][0][0]:
 Event set [w]
{noformat}

The stack of the blocking thread:
{noformat}
Thread [I/O dispatcher 1] (Suspended)   
        sun.misc.Unsafe.park(boolean, long) line: not available [native method] 
        java.util.concurrent.locks.LockSupport.park(java.lang.Object) line: 175 
        java.util.concurrent.CompletableFuture$Signaller.block() line: 1693     
        
java.util.concurrent.ForkJoinPool.managedBlock(java.util.concurrent.ForkJoinPool$ManagedBlocker)
 line: 3323     
        java.util.concurrent.CompletableFuture<T>.waitingGet(boolean) line: 
1729        
        java.util.concurrent.CompletableFuture<T>.get() line: 1895      
        
BlockingThreadMainApplication.lambda$0(org.apache.http.impl.nio.client.CloseableHttpAsyncClient,
 java.io.InputStream) line: 60  
        
BlockingThreadMainApplication$$Lambda$4.1112414583.apply(java.lang.Object) 
line: not available  
        
java.util.concurrent.CompletableFuture<T>.uniApply(java.util.concurrent.CompletableFuture<S>,
 java.util.function.Function<? super S,? extends T>, 
java.util.concurrent.CompletableFuture.UniApply<S,T>) line: 602       
        java.util.concurrent.CompletableFuture$UniApply<T,V>.tryFire(int) line: 
577     
        java.util.concurrent.CompletableFuture<T>.postComplete() line: 474      
        java.util.concurrent.CompletableFuture<T>.complete(T) line: 1962        
        BlockingThreadMainApplication$1.completed(org.apache.http.HttpResponse) 
line: 83        
        BlockingThreadMainApplication$1.completed(java.lang.Object) line: 1     
        org.apache.http.concurrent.BasicFuture<T>.completed(T) line: 119        
        
org.apache.http.impl.nio.client.DefaultClientExchangeHandlerImpl<T>.responseCompleted()
 line: 177       
        
org.apache.http.nio.protocol.HttpAsyncRequestExecutor.processResponse(org.apache.http.nio.NHttpClientConnection,
 org.apache.http.nio.protocol.HttpAsyncRequestExecutor$State, 
org.apache.http.nio.protocol.HttpAsyncClientExchangeHandler) line: 436    
        
org.apache.http.nio.protocol.HttpAsyncRequestExecutor.inputReady(org.apache.http.nio.NHttpClientConnection,
 org.apache.http.nio.ContentDecoder) line: 326       
        
org.apache.http.impl.nio.client.InternalRequestExecutor.inputReady(org.apache.http.nio.NHttpClientConnection,
 org.apache.http.nio.ContentDecoder) line: 83      
        
org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionImpl(org.apache.http.impl.nio.DefaultNHttpClientConnection).consumeInput(org.apache.http.nio.NHttpClientEventHandler)
 line: 265       
        
org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(org.apache.http.impl.nio.DefaultNHttpClientConnection)
 line: 81 
        
org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(java.lang.Object)
 line: 39      
        
org.apache.http.impl.nio.client.InternalIODispatch(org.apache.http.impl.nio.reactor.AbstractIODispatch<T>).inputReady(org.apache.http.nio.reactor.IOSession)
 line: 121  
        
org.apache.http.impl.nio.reactor.BaseIOReactor.readable(java.nio.channels.SelectionKey)
 line: 162       
        
org.apache.http.impl.nio.reactor.BaseIOReactor(org.apache.http.impl.nio.reactor.AbstractIOReactor).processEvent(java.nio.channels.SelectionKey)
 line: 337       
        
org.apache.http.impl.nio.reactor.BaseIOReactor(org.apache.http.impl.nio.reactor.AbstractIOReactor).processEvents(java.util.Set<java.nio.channels.SelectionKey>)
 line: 315       
        
org.apache.http.impl.nio.reactor.BaseIOReactor(org.apache.http.impl.nio.reactor.AbstractIOReactor).execute()
 line: 276  
        
org.apache.http.impl.nio.reactor.BaseIOReactor.execute(org.apache.http.nio.reactor.IOEventDispatch)
 line: 104   
        
org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run() 
line: 588    
        java.lang.Thread.run() line: 745        
{noformat}

Be aware that this issue could sometimes be a Heisenbug and the application 
terminates successfully in debugging. When running normally it fails on my 
machine with 100%.

You can simply reproduce this issue with the following code or use the attached 
maven project:

{code:java}
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;

public class BlockingThreadMainApplication
{
   private static final int POOL_PER_ROUTE = 20;
   private static final int POOL_MAX = 200;
   private static final int CONNECT_TIMEOUT = 10000;
   private static final int SOCKET_TIMEOUT = 60000;
   private static final String PUBLIC_KEYS_URL = 
"https://permissions-api.apps.bosch-iot-cloud.com/2/rest/publickeys";;

   private static HttpHost proxy = null;

   private int requestCounter = 0;

   public static void main(final String[] args) throws InterruptedException, 
ExecutionException, IOException
   {
      // include this lines if you are behind a proxy server
      // proxy = new HttpHost("localhost", 3128);

      final BlockingThreadMainApplication application = new 
BlockingThreadMainApplication();

      try (CloseableHttpAsyncClient asyncClient = 
application.createAsynClient())
      {

         // these request work like they should
         application.createRequest(asyncClient).get();
         application.createRequest(asyncClient).get();

         System.out.println("Execute request with inner request");
         final Object object = 
application.createRequestWithInnerRequest(asyncClient).get();
         // will never be invoked
         System.out.println("Yeaha, application completed. Resolved last 
response " + object.getClass().getName());
      }
   }

   private CompletableFuture<?> createRequestWithInnerRequest(final 
CloseableHttpAsyncClient asyncClient)
      throws InterruptedException, ExecutionException
   {
      // it works like a charm when using thenApplyAsync so it is executed in 
another thread
      return createRequest(asyncClient).thenApply(i ->
      {
         // execute another request and get its result
         try
         {
            System.out.println("executing request in thenApply");
            createRequest(asyncClient).get(); // never comes back
            System.out.println("executed request in thenApply");
         }
         catch (InterruptedException | ExecutionException e)
         {
            throw new IllegalStateException(e);
         }
         return i;
      });
   }

   public CompletableFuture<InputStream> createRequest(final 
CloseableHttpAsyncClient asyncClient)
   {
      final int requestId = ++requestCounter;
      final CompletableFuture<InputStream> toBeCompleted = new 
CompletableFuture<>();
      final FutureCallback<HttpResponse> callback = new 
FutureCallback<HttpResponse>()
      {
         @Override
         public void completed(final HttpResponse response)
         {
            System.out.println("completed " + requestId);
            try
            {
               toBeCompleted.complete(response.getEntity().getContent());
            }
            catch (UnsupportedOperationException | IOException e)
            {
               System.out.println("failed to get InputStream from response " + 
requestId);
               toBeCompleted.completeExceptionally(e);
            }
         }

         @Override
         public void cancelled()
         {
            System.out.println("cancelled " + requestId);
            toBeCompleted.cancel(true);
         }

         @Override
         public void failed(final Exception e)
         {
            System.out.println("failed " + requestId);
            toBeCompleted.completeExceptionally(e);
         }
      };

      asyncClient.execute(getGetRequest(), callback);
      return toBeCompleted;
   }

   private CloseableHttpAsyncClient createAsynClient()
   {
      // Create common default configuration
      final RequestConfig clientConfig = 
RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT)
         
.setSocketTimeout(SOCKET_TIMEOUT).setConnectionRequestTimeout(SOCKET_TIMEOUT).setProxy(proxy).build();

      DefaultConnectingIOReactor ioreactor;
      PoolingNHttpClientConnectionManager asyncConnectionManager;
      try
      {
         ioreactor = new DefaultConnectingIOReactor();
         asyncConnectionManager = new 
PoolingNHttpClientConnectionManager(ioreactor);
         asyncConnectionManager.setMaxTotal(POOL_MAX);
         asyncConnectionManager.setDefaultMaxPerRoute(POOL_PER_ROUTE);
      }
      catch (final IOReactorException e)
      {
         throw new RuntimeException(e);
      }

      final CloseableHttpAsyncClient client = 
HttpAsyncClientBuilder.create().setDefaultRequestConfig(clientConfig)
         .setConnectionManager(asyncConnectionManager).build();
      client.start();
      return client;
   }

   private HttpGet getGetRequest()
   {
      return new HttpGet(PUBLIC_KEYS_URL);
   }
}
{code}

I don't think it is related to HTTPASYNC-35 or HTTPASYNC-34 who sounds similiar.

Just one last note: Why is 4.1.2 released but not in this Jira project?



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

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

Reply via email to