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