Source File
writesched.go
Belonging Package
golang.org/x/net/http2
// 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.//go:build !(go1.27 && !http2legacy)package http2import// FrameWriteRequest is a request to write a frame.//// Deprecated: User-provided write schedulers are deprecated.type FrameWriteRequest struct {// write is the interface value that does the writing, once the// WriteScheduler has selected this frame to write. The write// functions are all defined in write.go.write writeFramer// stream is the stream on which this frame will be written.// nil for non-stream frames like PING and SETTINGS.// nil for RST_STREAM streams, which use the StreamError.StreamID field instead.stream *stream// done, if non-nil, must be a buffered channel with space for// 1 message and is sent the return value from write (or an// earlier error) when the frame has been written.done chan error}// StreamID returns the id of the stream this frame will be written to.// 0 is used for non-stream frames such as PING and SETTINGS.func ( FrameWriteRequest) () uint32 {if .stream == nil {if , := .write.(StreamError); {// (*serverConn).resetStream doesn't set// stream because it doesn't necessarily have// one. So special case this type of write// message.return .StreamID}return 0}return .stream.id}// isControl reports whether wr is a control frame for MaxQueuedControlFrames// purposes. That includes non-stream frames and RST_STREAM frames.func ( FrameWriteRequest) () bool {return .stream == nil}// DataSize returns the number of flow control bytes that must be consumed// to write this entire frame. This is 0 for non-DATA frames.func ( FrameWriteRequest) () int {if , := .write.(*writeData); {return len(.p)}return 0}// Consume consumes min(n, available) bytes from this frame, where available// is the number of flow control bytes available on the stream. Consume returns// 0, 1, or 2 frames, where the integer return value gives the number of frames// returned.//// If flow control prevents consuming any bytes, this returns (_, _, 0). If// the entire frame was consumed, this returns (wr, _, 1). Otherwise, this// returns (consumed, rest, 2), where 'consumed' contains the consumed bytes and// 'rest' contains the remaining bytes. The consumed bytes are deducted from the// underlying stream's flow control budget.func ( FrameWriteRequest) ( int32) (FrameWriteRequest, FrameWriteRequest, int) {var FrameWriteRequest// Non-DATA frames are always consumed whole., := .write.(*writeData)if ! || len(.p) == 0 {return , , 1}// Might need to split after applying limits.:= .stream.flow.available()if < {=}if .stream.sc.maxFrameSize < {= .stream.sc.maxFrameSize}if <= 0 {return , , 0}if len(.p) > int() {.stream.flow.take():= FrameWriteRequest{stream: .stream,write: &writeData{streamID: .streamID,p: .p[:],// Even if the original had endStream set, there// are bytes remaining because len(wd.p) > allowed,// so we know endStream is false.endStream: false,},// Our caller is blocking on the final DATA frame, not// this intermediate frame, so no need to wait.done: nil,}:= FrameWriteRequest{stream: .stream,write: &writeData{streamID: .streamID,p: .p[:],endStream: .endStream,},done: .done,}return , , 2}// The frame is consumed whole.// NB: This cast cannot overflow because allowed is <= math.MaxInt32..stream.flow.take(int32(len(.p)))return , , 1}// String is for debugging only.func ( FrameWriteRequest) () string {var stringif , := .write.(fmt.Stringer); {= .String()} else {= fmt.Sprintf("%T", .write)}return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", .StreamID(), .done != nil, )}// replyToWriter sends err to wr.done and panics if the send must block// This does nothing if wr.done is nil.func ( *FrameWriteRequest) ( error) {if .done == nil {return}select {case .done <- :default:panic(fmt.Sprintf("unbuffered done channel passed in for type %T", .write))}.write = nil // prevent use (assume it's tainted after wr.done send)}// writeQueue is used by implementations of WriteScheduler.//// Each writeQueue contains a queue of FrameWriteRequests, meant to store all// FrameWriteRequests associated with a given stream. This is implemented as a// two-stage queue: currQueue[currPos:] and nextQueue. Removing an item is done// by incrementing currPos of currQueue. Adding an item is done by appending it// to the nextQueue. If currQueue is empty when trying to remove an item, we// can swap currQueue and nextQueue to remedy the situation.// This two-stage queue is analogous to the use of two lists in Okasaki's// purely functional queue but without the overhead of reversing the list when// swapping stages.//// writeQueue also contains prev and next, this can be used by implementations// of WriteScheduler to construct data structures that represent the order of// writing between different streams (e.g. circular linked list).type writeQueue struct {currQueue []FrameWriteRequestnextQueue []FrameWriteRequestcurrPos intprev, next *writeQueue}func ( *writeQueue) () bool {return (len(.currQueue) - .currPos + len(.nextQueue)) == 0}func ( *writeQueue) ( FrameWriteRequest) {.nextQueue = append(.nextQueue, )}func ( *writeQueue) () FrameWriteRequest {if .empty() {panic("invalid use of queue")}if .currPos >= len(.currQueue) {.currQueue, .currPos, .nextQueue = .nextQueue, 0, .currQueue[:0]}:= .currQueue[.currPos].currQueue[.currPos] = FrameWriteRequest{}.currPos++return}func ( *writeQueue) () *FrameWriteRequest {if .currPos < len(.currQueue) {return &.currQueue[.currPos]}if len(.nextQueue) > 0 {return &.nextQueue[0]}return nil}// consume consumes up to n bytes from q.s[0]. If the frame is// entirely consumed, it is removed from the queue. If the frame// is partially consumed, the frame is kept with the consumed// bytes removed. Returns true iff any bytes were consumed.func ( *writeQueue) ( int32) (FrameWriteRequest, bool) {if .empty() {return FrameWriteRequest{}, false}, , := .peek().Consume()switch {case 0:return FrameWriteRequest{}, falsecase 1:.shift()case 2:*.peek() =}return , true}type writeQueuePool []*writeQueue// put inserts an unused writeQueue into the pool.func ( *writeQueuePool) ( *writeQueue) {for := range .currQueue {.currQueue[] = FrameWriteRequest{}}for := range .nextQueue {.nextQueue[] = FrameWriteRequest{}}.currQueue = .currQueue[:0].nextQueue = .nextQueue[:0].currPos = 0* = append(*, )}// get returns an empty writeQueue.func ( *writeQueuePool) () *writeQueue {:= len(*)if == 0 {return new(writeQueue)}:= - 1:= (*)[](*)[] = nil* = (*)[:]return}
The pages are generated with Golds v0.8.4. (GOOS=linux GOARCH=amd64)