github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/http2/writesched.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http2
     6  
     7  import "fmt"
     8  
     9  // frameWriteMsg is a request to write a frame.
    10  type frameWriteMsg struct {
    11  	// write is the interface value that does the writing, once the
    12  	// writeScheduler (below) has decided to select this frame
    13  	// to write. The write functions are all defined in write.go.
    14  	write writeFramer
    15  
    16  	stream *stream // used for prioritization. nil for non-stream frames.
    17  
    18  	// done, if non-nil, must be a buffered channel with space for
    19  	// 1 message and is sent the return value from write (or an
    20  	// earlier error) when the frame has been written.
    21  	done chan error
    22  }
    23  
    24  // for debugging only:
    25  func (wm frameWriteMsg) String() string {
    26  	var streamID uint32
    27  	if wm.stream != nil {
    28  		streamID = wm.stream.id
    29  	}
    30  	var des string
    31  	if s, ok := wm.write.(fmt.Stringer); ok {
    32  		des = s.String()
    33  	} else {
    34  		des = fmt.Sprintf("%T", wm.write)
    35  	}
    36  	return fmt.Sprintf("[frameWriteMsg stream=%d, ch=%v, type: %v]", streamID, wm.done != nil, des)
    37  }
    38  
    39  // writeScheduler tracks pending frames to write, priorities, and decides
    40  // the next one to use. It is not thread-safe.
    41  type writeScheduler struct {
    42  	// zero are frames not associated with a specific stream.
    43  	// They're sent before any stream-specific freams.
    44  	zero writeQueue
    45  
    46  	// maxFrameSize is the maximum size of a DATA frame
    47  	// we'll write. Must be non-zero and between 16K-16M.
    48  	maxFrameSize uint32
    49  
    50  	// sq contains the stream-specific queues, keyed by stream ID.
    51  	// when a stream is idle, it's deleted from the map.
    52  	sq map[uint32]*writeQueue
    53  
    54  	// canSend is a slice of memory that's reused between frame
    55  	// scheduling decisions to hold the list of writeQueues (from sq)
    56  	// which have enough flow control data to send. After canSend is
    57  	// built, the best is selected.
    58  	canSend []*writeQueue
    59  
    60  	// pool of empty queues for reuse.
    61  	queuePool []*writeQueue
    62  }
    63  
    64  func (ws *writeScheduler) putEmptyQueue(q *writeQueue) {
    65  	if len(q.s) != 0 {
    66  		panic("queue must be empty")
    67  	}
    68  	ws.queuePool = append(ws.queuePool, q)
    69  }
    70  
    71  func (ws *writeScheduler) getEmptyQueue() *writeQueue {
    72  	ln := len(ws.queuePool)
    73  	if ln == 0 {
    74  		return new(writeQueue)
    75  	}
    76  	q := ws.queuePool[ln-1]
    77  	ws.queuePool = ws.queuePool[:ln-1]
    78  	return q
    79  }
    80  
    81  func (ws *writeScheduler) empty() bool { return ws.zero.empty() && len(ws.sq) == 0 }
    82  
    83  func (ws *writeScheduler) add(wm frameWriteMsg) {
    84  	st := wm.stream
    85  	if st == nil {
    86  		ws.zero.push(wm)
    87  	} else {
    88  		ws.streamQueue(st.id).push(wm)
    89  	}
    90  }
    91  
    92  func (ws *writeScheduler) streamQueue(streamID uint32) *writeQueue {
    93  	if q, ok := ws.sq[streamID]; ok {
    94  		return q
    95  	}
    96  	if ws.sq == nil {
    97  		ws.sq = make(map[uint32]*writeQueue)
    98  	}
    99  	q := ws.getEmptyQueue()
   100  	ws.sq[streamID] = q
   101  	return q
   102  }
   103  
   104  // take returns the most important frame to write and removes it from the scheduler.
   105  // It is illegal to call this if the scheduler is empty or if there are no connection-level
   106  // flow control bytes available.
   107  func (ws *writeScheduler) take() (wm frameWriteMsg, ok bool) {
   108  	if ws.maxFrameSize == 0 {
   109  		panic("internal error: ws.maxFrameSize not initialized or invalid")
   110  	}
   111  
   112  	// If there any frames not associated with streams, prefer those first.
   113  	// These are usually SETTINGS, etc.
   114  	if !ws.zero.empty() {
   115  		return ws.zero.shift(), true
   116  	}
   117  	if len(ws.sq) == 0 {
   118  		return
   119  	}
   120  
   121  	// Next, prioritize frames on streams that aren't DATA frames (no cost).
   122  	for id, q := range ws.sq {
   123  		if q.firstIsNoCost() {
   124  			return ws.takeFrom(id, q)
   125  		}
   126  	}
   127  
   128  	// Now, all that remains are DATA frames with non-zero bytes to
   129  	// send. So pick the best one.
   130  	if len(ws.canSend) != 0 {
   131  		panic("should be empty")
   132  	}
   133  	for _, q := range ws.sq {
   134  		if n := ws.streamWritableBytes(q); n > 0 {
   135  			ws.canSend = append(ws.canSend, q)
   136  		}
   137  	}
   138  	if len(ws.canSend) == 0 {
   139  		return
   140  	}
   141  	defer ws.zeroCanSend()
   142  
   143  	// TODO: find the best queue
   144  	q := ws.canSend[0]
   145  
   146  	return ws.takeFrom(q.streamID(), q)
   147  }
   148  
   149  // zeroCanSend is defered from take.
   150  func (ws *writeScheduler) zeroCanSend() {
   151  	for i := range ws.canSend {
   152  		ws.canSend[i] = nil
   153  	}
   154  	ws.canSend = ws.canSend[:0]
   155  }
   156  
   157  // streamWritableBytes returns the number of DATA bytes we could write
   158  // from the given queue's stream, if this stream/queue were
   159  // selected. It is an error to call this if q's head isn't a
   160  // *writeData.
   161  func (ws *writeScheduler) streamWritableBytes(q *writeQueue) int32 {
   162  	wm := q.head()
   163  	ret := wm.stream.flow.available() // max we can write
   164  	if ret == 0 {
   165  		return 0
   166  	}
   167  	if int32(ws.maxFrameSize) < ret {
   168  		ret = int32(ws.maxFrameSize)
   169  	}
   170  	if ret == 0 {
   171  		panic("internal error: ws.maxFrameSize not initialized or invalid")
   172  	}
   173  	wd := wm.write.(*writeData)
   174  	if len(wd.p) < int(ret) {
   175  		ret = int32(len(wd.p))
   176  	}
   177  	return ret
   178  }
   179  
   180  func (ws *writeScheduler) takeFrom(id uint32, q *writeQueue) (wm frameWriteMsg, ok bool) {
   181  	wm = q.head()
   182  	// If the first item in this queue costs flow control tokens
   183  	// and we don't have enough, write as much as we can.
   184  	if wd, ok := wm.write.(*writeData); ok && len(wd.p) > 0 {
   185  		allowed := wm.stream.flow.available() // max we can write
   186  		if allowed == 0 {
   187  			// No quota available. Caller can try the next stream.
   188  			return frameWriteMsg{}, false
   189  		}
   190  		if int32(ws.maxFrameSize) < allowed {
   191  			allowed = int32(ws.maxFrameSize)
   192  		}
   193  		// TODO: further restrict the allowed size, because even if
   194  		// the peer says it's okay to write 16MB data frames, we might
   195  		// want to write smaller ones to properly weight competing
   196  		// streams' priorities.
   197  
   198  		if len(wd.p) > int(allowed) {
   199  			wm.stream.flow.take(allowed)
   200  			chunk := wd.p[:allowed]
   201  			wd.p = wd.p[allowed:]
   202  			// Make up a new write message of a valid size, rather
   203  			// than shifting one off the queue.
   204  			return frameWriteMsg{
   205  				stream: wm.stream,
   206  				write: &writeData{
   207  					streamID: wd.streamID,
   208  					p:        chunk,
   209  					// even if the original had endStream set, there
   210  					// arebytes remaining because len(wd.p) > allowed,
   211  					// so we know endStream is false:
   212  					endStream: false,
   213  				},
   214  				// our caller is blocking on the final DATA frame, not
   215  				// these intermediates, so no need to wait:
   216  				done: nil,
   217  			}, true
   218  		}
   219  		wm.stream.flow.take(int32(len(wd.p)))
   220  	}
   221  
   222  	q.shift()
   223  	if q.empty() {
   224  		ws.putEmptyQueue(q)
   225  		delete(ws.sq, id)
   226  	}
   227  	return wm, true
   228  }
   229  
   230  func (ws *writeScheduler) forgetStream(id uint32) {
   231  	q, ok := ws.sq[id]
   232  	if !ok {
   233  		return
   234  	}
   235  	delete(ws.sq, id)
   236  
   237  	// But keep it for others later.
   238  	for i := range q.s {
   239  		q.s[i] = frameWriteMsg{}
   240  	}
   241  	q.s = q.s[:0]
   242  	ws.putEmptyQueue(q)
   243  }
   244  
   245  type writeQueue struct {
   246  	s []frameWriteMsg
   247  }
   248  
   249  // streamID returns the stream ID for a non-empty stream-specific queue.
   250  func (q *writeQueue) streamID() uint32 { return q.s[0].stream.id }
   251  
   252  func (q *writeQueue) empty() bool { return len(q.s) == 0 }
   253  
   254  func (q *writeQueue) push(wm frameWriteMsg) {
   255  	q.s = append(q.s, wm)
   256  }
   257  
   258  // head returns the next item that would be removed by shift.
   259  func (q *writeQueue) head() frameWriteMsg {
   260  	if len(q.s) == 0 {
   261  		panic("invalid use of queue")
   262  	}
   263  	return q.s[0]
   264  }
   265  
   266  func (q *writeQueue) shift() frameWriteMsg {
   267  	if len(q.s) == 0 {
   268  		panic("invalid use of queue")
   269  	}
   270  	wm := q.s[0]
   271  	// TODO: less copy-happy queue.
   272  	copy(q.s, q.s[1:])
   273  	q.s[len(q.s)-1] = frameWriteMsg{}
   274  	q.s = q.s[:len(q.s)-1]
   275  	return wm
   276  }
   277  
   278  func (q *writeQueue) firstIsNoCost() bool {
   279  	if df, ok := q.s[0].write.(*writeData); ok {
   280  		return len(df.p) == 0
   281  	}
   282  	return true
   283  }