-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x/net/http2: when StrictMaxConcurrentStreams enabled ClientConn.ReserveNewRequest() causes stalls in request processing #70809
Comments
cc @neild @tombergan |
I believe the deadlock happens in func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStream)) (err error) { Following the reproducer above: first request takes select {
case cc.reqHeaderMu <- struct{}{}:
...
}
cc.mu.Lock()
...
cc.decrStreamReservationsLocked()
if err := cc.awaitOpenSlotForStreamLocked(cs); err != nil { Looking further we can see the request hanging on // awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams.
// Must hold cc.mu.
func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {
for {
if cc.closed && cc.nextStreamID == 1 && cc.streamsReserved == 0 {
// This is the very first request sent to this connection.
// Return a fatal error which aborts the retry loop.
return errClientConnNotEstablished
}
cc.lastActive = cc.t.now()
if cc.closed || !cc.canTakeNewRequestLocked() {
return errClientConnUnusable
}
cc.lastIdle = time.Time{}
if cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) {
return nil
}
cc.pendingRequests++
cc.cond.Wait()
cc.pendingRequests--
select {
case <-cs.abort:
return cs.abortErr
default:
}
}
} Since select {
case cc.reqHeaderMu <- struct{}{}: Client connection state confirms it:
Looks like this behaviour was there before. It was exposed by that change golang/net@f35fec9#diff-e9bd9b4a514c2960ad85405e4a827d83c2decaaac70e9e0158a59db2d05807a7R1686. |
Thanks @Choraden your help is much appreciated. The following patch seems to fix the issue.
The awaitOpenSlotForStreamLocked() function should not look into reserved (waiting) requests as they have no pressence in connection yet. |
…bled Fix regression introduced in CL 617655. The awaitOpenSlotForStreamLocked() should not take into consideration reserved requests as they have no presence in the connection. Fixes golang/go#70809 Change-Id: Ia23968189cbdb44dae860a1de9d32700115b28b4
…bled Fix regression introduced in CL 617655. The awaitOpenSlotForStreamLocked() should not take into consideration reserved requests as they have no presence in the connection. Fixes golang/go#70809 Change-Id: Ia23968189cbdb44dae860a1de9d32700115b28b4
Go version
go1.23.3
Output of
go env
in your module/workspace:What did you do?
Please find a minimal reproducer below:
What did you see happen?
Running it without reserve flag works as expected
Running it with reserve flag set hangs
I nailed down this bug to change: https://go-review.googlesource.com/c/net/+/617655
What did you expect to see?
I expect it to work as in x/net version 0.30.0.
The text was updated successfully, but these errors were encountered: