RFR: 8371903: HttpClient: improve handling of HTTP/2 GOAWAY frames with error code [v10]

EunHyunsu duke at openjdk.org
Wed Jan 7 12:47:52 UTC 2026


On Wed, 7 Jan 2026 10:25:09 GMT, Daniel Jeliński <djelinski at openjdk.org> wrote:

>> EunHyunsu has updated the pull request incrementally with one additional commit since the last revision:
>> 
>>   8371903: Use stateLock to synchronize stream iteration and connection closure
>
> Well with af092399cd5d188fdec4061d33528f6dc0f3a7f0 the `GoAwayWithErrorTest` deadlocks sometimes, with the following stack traces:
> Thread 1:
> 
>               "java.base/java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:323)",
>               "java.net.http/jdk.internal.net.http.Http2Connection.tryReserveForPoolCheckout(Http2Connection.java:1604)",
>               "java.net.http/jdk.internal.net.http.Http2ClientImpl.getConnectionFor(Http2ClientImpl.java:109)",
>               "java.net.http/jdk.internal.net.http.ExchangeImpl.attemptHttp2Exchange(ExchangeImpl.java:143)",
>               "java.net.http/jdk.internal.net.http.ExchangeImpl.get(ExchangeImpl.java:122)",
>               "java.net.http/jdk.internal.net.http.Exchange.establishExchange(Exchange.java:409)",
>               "java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl(Exchange.java:624)",
>               "java.net.http/jdk.internal.net.http.Exchange.responseAsync(Exchange.java:447)",
>               "java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:483)",
>               "java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsync0$0(MultiExchange.java:402)",
> 
> Thread 2:
> 
>               "java.base/java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:323)",
>               "java.net.http/jdk.internal.net.http.Http2ClientImpl.removeFromPool(Http2ClientImpl.java:225)",
>               "java.net.http/jdk.internal.net.http.Http2Connection$Terminator.doTerminate(Http2Connection.java:2135)",
>               "java.net.http/jdk.internal.net.http.Http2Connection$Terminator.terminate(Http2Connection.java:2099)",
>               "java.net.http/jdk.internal.net.http.Http2Connection.close(Http2Connection.java:899)",
>               "java.net.http/jdk.internal.net.http.Http2Connection.handleGoAwayWithError(Http2Connection.java:1481)",
>               "java.net.http/jdk.internal.net.http.Http2Connection.handleGoAway(Http2Connection.java:1411)",
> 
> connectionPoolLock and stateLock are acquired in a different order, leading to the deadlock.

@djelinski 
I addressed the deadlock by moving `close(cause)` outside of stateLock, keeping the stream iteration synchronized while avoiding holding the lock during `close()`.

The change works in local testing, but I don’t have a way to validate this under CI conditions. I’m also unsure whether this is the best solution, so I’d appreciate any guidance or alternative suggestions.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/28632#issuecomment-3718696751


More information about the net-dev mailing list