Moves swapState up, so it gets executed no matter what. (#2649)
* Moves swapState up, so it gets executed no matter what. * Adds an explanation.
This commit is contained in:
@ -1030,6 +1030,9 @@ func (t *http2Server) deleteStream(s *Stream, eosReceived bool) {
|
||||
// closeStream clears the footprint of a stream when the stream is not needed
|
||||
// any more.
|
||||
func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) {
|
||||
// Mark the stream as done
|
||||
oldState := s.swapState(streamDone)
|
||||
|
||||
// In case stream sending and receiving are invoked in separate
|
||||
// goroutines (e.g., bi-directional streaming), cancel needs to be
|
||||
// called to interrupt the potential blocking on other goroutines.
|
||||
@ -1051,8 +1054,19 @@ func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, hd
|
||||
return
|
||||
}
|
||||
|
||||
// We do the check here, because of the following scenario:
|
||||
// 1. closeStream is called first with a trailer. A trailer item with a piggybacked cleanup item
|
||||
// is put to control buffer.
|
||||
// 2. Loopy writer is waiting on a stream quota. It will never get it because client errored at
|
||||
// some point. So loopy can't act on trailer
|
||||
// 3. Client sends a RST_STREAM due to the error. Then closeStream is called without a trailer as
|
||||
// the result of the received RST_STREAM.
|
||||
// If we do this check at the beginning of the closeStream, then we won't put a cleanup item in
|
||||
// response to received RST_STREAM into the control buffer and outStream in loopy writer will
|
||||
// never get cleaned up.
|
||||
|
||||
// If the stream is already done, don't send the trailer.
|
||||
if s.swapState(streamDone) == streamDone {
|
||||
if oldState == streamDone {
|
||||
return
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user