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:
Can Guler
2019-02-20 18:03:46 -08:00
committed by GitHub
parent 29c406a5bd
commit 587cec03e6

View File

@ -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
}