mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
In certain shutdown cases (from the client and/or server), the http2
Server can Push stream-specific frames on closed streams. This caused
memory leaks in the random write scheduler.
As a conservative fix for backporting, just clear the map element
whenever its queue value is empty. The map entry is re-created as
needed anyway. This isn't perfectly ideal (it adds a map+delete and
free queue put+get) in the case where a stream is open & actively
writing, but it's an easy fix for now. A future CL can optimize all
this code. It looks like there are some other good optimization
opportunities in related code anyway. But I'd rather that happen on
master and not be done in a backported change.
Updates golang/go#34636 (needs bundle to std before fixed)
Updates golang/go#33812
Change-Id: I21508ba2ebc361e8b8532d0d1cebf882e82c473c
Reviewed-on: https://go-review.googlesource.com/c/net/+/198462
Reviewed-by: Bryan C. Mills <bcmills@google.com>
(cherry picked from commit d98b1b4438)
Reviewed-on: https://go-review.googlesource.com/c/net/+/198617
Reviewed-by: Andrew Bonventre <andybons@golang.org>
Run-TryBot: Andrew Bonventre <andybons@golang.org>
78 lines
2.0 KiB
Go
78 lines
2.0 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package http2
|
|
|
|
import "math"
|
|
|
|
// NewRandomWriteScheduler constructs a WriteScheduler that ignores HTTP/2
|
|
// priorities. Control frames like SETTINGS and PING are written before DATA
|
|
// frames, but if no control frames are queued and multiple streams have queued
|
|
// HEADERS or DATA frames, Pop selects a ready stream arbitrarily.
|
|
func NewRandomWriteScheduler() WriteScheduler {
|
|
return &randomWriteScheduler{sq: make(map[uint32]*writeQueue)}
|
|
}
|
|
|
|
type randomWriteScheduler struct {
|
|
// zero are frames not associated with a specific stream.
|
|
zero writeQueue
|
|
|
|
// sq contains the stream-specific queues, keyed by stream ID.
|
|
// When a stream is idle, closed, or emptied, it's deleted
|
|
// from the map.
|
|
sq map[uint32]*writeQueue
|
|
|
|
// pool of empty queues for reuse.
|
|
queuePool writeQueuePool
|
|
}
|
|
|
|
func (ws *randomWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) {
|
|
// no-op: idle streams are not tracked
|
|
}
|
|
|
|
func (ws *randomWriteScheduler) CloseStream(streamID uint32) {
|
|
q, ok := ws.sq[streamID]
|
|
if !ok {
|
|
return
|
|
}
|
|
delete(ws.sq, streamID)
|
|
ws.queuePool.put(q)
|
|
}
|
|
|
|
func (ws *randomWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) {
|
|
// no-op: priorities are ignored
|
|
}
|
|
|
|
func (ws *randomWriteScheduler) Push(wr FrameWriteRequest) {
|
|
id := wr.StreamID()
|
|
if id == 0 {
|
|
ws.zero.push(wr)
|
|
return
|
|
}
|
|
q, ok := ws.sq[id]
|
|
if !ok {
|
|
q = ws.queuePool.get()
|
|
ws.sq[id] = q
|
|
}
|
|
q.push(wr)
|
|
}
|
|
|
|
func (ws *randomWriteScheduler) Pop() (FrameWriteRequest, bool) {
|
|
// Control frames first.
|
|
if !ws.zero.empty() {
|
|
return ws.zero.shift(), true
|
|
}
|
|
// Iterate over all non-idle streams until finding one that can be consumed.
|
|
for streamID, q := range ws.sq {
|
|
if wr, ok := q.consume(math.MaxInt32); ok {
|
|
if q.empty() {
|
|
delete(ws.sq, streamID)
|
|
ws.queuePool.put(q)
|
|
}
|
|
return wr, true
|
|
}
|
|
}
|
|
return FrameWriteRequest{}, false
|
|
}
|