- more := true
- interested := false
- for _, endGameIter := range []bool{false, true} {
- for _, piece := range pieceOrder {
- tp := p.t.piece(piece.index)
- tp.iterUndirtiedChunks(func(cs chunkIndexType) {
- req := cs + tp.requestIndexOffset()
- if !piece.endGame && !endGameIter && p.t.pendingRequests[req] > 0 {
- return
- }
- interested = true
- more = p.setInterested(true)
- if !more {
- return
- }
- if maxRequests(p.actualRequestState.Requests.GetCardinality()) >= p.nominalMaxRequests() {
- return
- }
- if p.peerChoking && !p.peerAllowedFast.Contains(bitmap.BitIndex(piece.index)) {
+ // Could either of the lastRequested be Zero? That's what checking an existing peer is for.
+ leftLast := leftRequestState.when
+ rightLast := rightRequestState.when
+ if leftLast.IsZero() || rightLast.IsZero() {
+ panic("expected non-zero last requested times")
+ }
+ // We want the most-recently requested on the left. Clients like Transmission serve requests
+ // in received order, so the most recently-requested is the one that has the longest until
+ // it will be served and therefore is the best candidate to cancel.
+ ml = ml.CmpInt64(rightLast.Sub(leftLast).Nanoseconds())
+ }
+ ml = ml.Int(
+ leftPiece.Availability,
+ rightPiece.Availability)
+ if priority == PiecePriorityReadahead {
+ // TODO: For readahead in particular, it would be even better to consider distance from the
+ // reader position so that reads earlier in a torrent don't starve reads later in the
+ // torrent. This would probably require reconsideration of how readahead priority works.
+ ml = ml.Int(leftPieceIndex, rightPieceIndex)
+ } else {
+ ml = ml.Int(t.pieceRequestOrder[leftPieceIndex], t.pieceRequestOrder[rightPieceIndex])
+ }
+ return ml.Less()
+}
+
+func (p *desiredPeerRequests) Swap(i, j int) {
+ p.requestIndexes[i], p.requestIndexes[j] = p.requestIndexes[j], p.requestIndexes[i]
+}
+
+func (p *desiredPeerRequests) Push(x interface{}) {
+ p.requestIndexes = append(p.requestIndexes, x.(RequestIndex))
+}
+
+func (p *desiredPeerRequests) Pop() interface{} {
+ last := len(p.requestIndexes) - 1
+ x := p.requestIndexes[last]
+ p.requestIndexes = p.requestIndexes[:last]
+ return x
+}
+
+type desiredRequestState struct {
+ Requests desiredPeerRequests
+ Interested bool
+}
+
+func (p *Peer) getDesiredRequestState() (desired desiredRequestState) {
+ t := p.t
+ if !t.haveInfo() {
+ return
+ }
+ if t.closed.IsSet() {
+ return
+ }
+ input := t.getRequestStrategyInput()
+ requestHeap := desiredPeerRequests{
+ peer: p,
+ pieceStates: t.requestPieceStates,
+ requestIndexes: t.requestIndexes,
+ }
+ // Caller-provided allocation for roaring bitmap iteration.
+ var it typedRoaring.Iterator[RequestIndex]
+ requestStrategy.GetRequestablePieces(
+ input,
+ t.getPieceRequestOrder(),
+ func(ih InfoHash, pieceIndex int, pieceExtra requestStrategy.PieceRequestOrderState) {
+ if ih != t.infoHash {
+ return
+ }
+ if !p.peerHasPiece(pieceIndex) {
+ return
+ }
+ requestHeap.pieceStates[pieceIndex] = pieceExtra
+ allowedFast := p.peerAllowedFast.Contains(pieceIndex)
+ t.iterUndirtiedRequestIndexesInPiece(&it, pieceIndex, func(r requestStrategy.RequestIndex) {
+ if !allowedFast {
+ // We must signal interest to request this. TODO: We could set interested if the
+ // peers pieces (minus the allowed fast set) overlap with our missing pieces if
+ // there are any readers, or any pending pieces.
+ desired.Interested = true
+ // We can make or will allow sustaining a request here if we're not choked, or
+ // have made the request previously (presumably while unchoked), and haven't had
+ // the peer respond yet (and the request was retained because we are using the
+ // fast extension).
+ if p.peerChoking && !p.requestState.Requests.Contains(r) {
+ // We can't request this right now.