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 }