// Describes the importance of obtaining a particular piece.
type piecePriority byte
-func (pp *piecePriority) Raise(maybe piecePriority) {
+func (pp *piecePriority) Raise(maybe piecePriority) bool {
if maybe > *pp {
*pp = maybe
+ return true
}
+ return false
}
// Priority for use in PriorityBitmap
func (p *Piece) torrentEndOffset() int64 {
return p.torrentBeginOffset() + int64(p.length())
}
+
+func (p *Piece) SetPriority(prio piecePriority) {
+ p.t.cl.mu.Lock()
+ defer p.t.cl.mu.Unlock()
+ p.priority = prio
+ p.t.updatePiecePriority(p.index)
+}
+
+func (p *Piece) uncachedPriority() (ret piecePriority) {
+ if p.t.pieceComplete(p.index) {
+ return PiecePriorityNone
+ }
+ for _, f := range p.files {
+ ret.Raise(f.prio)
+ }
+ if p.t.readerNowPieces.Contains(p.index) {
+ ret.Raise(PiecePriorityNow)
+ }
+ // if t.readerNowPieces.Contains(piece - 1) {
+ // return PiecePriorityNext
+ // }
+ if p.t.readerReadaheadPieces.Contains(p.index) {
+ ret.Raise(PiecePriorityReadahead)
+ }
+ ret.Raise(p.priority)
+ return
+}
func (t *Torrent) DownloadPieces(begin, end int) {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- t.pendPieceRange(begin, end)
+ for i := begin; i < end; i++ {
+ if t.pieces[i].priority.Raise(PiecePriorityNormal) {
+ t.updatePiecePriority(i)
+ }
+ }
}
func (t *Torrent) CancelPieces(begin, end int) {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- t.unpendPieceRange(begin, end)
+ for i := begin; i < end; i++ {
+ p := &t.pieces[i]
+ if p.priority == PiecePriorityNone {
+ continue
+ }
+ p.priority = PiecePriorityNone
+ t.updatePiecePriority(i)
+ }
}
func (t *Torrent) initFiles() {
}
// Marks the entire torrent for download. Requires the info first, see
-// GotInfo.
+// GotInfo. Sets piece priorities for historical reasons.
func (t *Torrent) DownloadAll() {
- t.cl.mu.Lock()
- defer t.cl.mu.Unlock()
- t.pendPieceRange(0, t.numPieces())
+ t.DownloadPieces(0, t.numPieces())
}
func (t *Torrent) String() string {
readerNowPieces bitmap.Bitmap
readerReadaheadPieces bitmap.Bitmap
- // The indexes of pieces we want with normal priority, that aren't
- // currently available.
+ // A cache of pieces we need to get. Calculated from various piece and
+ // file priorities and completion states elsewhere.
pendingPieces prioritybitmap.PriorityBitmap
// A cache of completed piece indices.
completedPieces bitmap.Bitmap
if t.pendingPieces.Contains(index) {
return true
}
+ // log.Printf("piece %d not pending", index)
return !t.forReaderOffsetPieces(func(begin, end int) bool {
return index < begin || index >= end
})
}
func (t *Torrent) piecePriorityChanged(piece int) {
+ // log.Printf("piece %d priority changed", piece)
for c := range t.conns {
if c.updatePiecePriority(piece) {
+ // log.Print("conn piece priority changed")
c.updateRequests()
}
}
func (t *Torrent) updatePiecePriority(piece int) {
p := &t.pieces[piece]
- newPrio := t.piecePriorityUncached(piece)
- if newPrio == p.priority {
- return
+ newPrio := p.uncachedPriority()
+ // log.Printf("torrent %p: piece %d: uncached priority: %v", t, piece, newPrio)
+ if newPrio == PiecePriorityNone {
+ if !t.pendingPieces.Remove(piece) {
+ return
+ }
+ } else {
+ if !t.pendingPieces.Set(piece, newPrio.BitmapPriority()) {
+ return
+ }
}
- p.priority = newPrio
t.piecePriorityChanged(piece)
}
}
func (t *Torrent) piecePriority(piece int) piecePriority {
- if !t.haveInfo() {
- return PiecePriorityNone
- }
- return t.pieces[piece].priority
-}
-
-func (t *Torrent) piecePriorityUncached(piece int) (ret piecePriority) {
- for _, f := range t.pieces[piece].files {
- ret.Raise(f.prio)
- }
- if t.readerNowPieces.Contains(piece) {
- ret.Raise(PiecePriorityNow)
- }
- // if t.readerNowPieces.Contains(piece - 1) {
- // return PiecePriorityNext
- // }
- if t.readerReadaheadPieces.Contains(piece) {
- ret.Raise(PiecePriorityReadahead)
- }
- if t.pendingPieces.Contains(piece) {
- ret.Raise(PiecePriorityNormal)
- }
- if t.pieceComplete(piece) {
+ prio, ok := t.pendingPieces.GetPriority(piece)
+ if !ok {
return PiecePriorityNone
}
- return
-}
-
-func (t *Torrent) pendPiece(piece int) {
- if t.pendingPieces.Contains(piece) {
- return
- }
- if t.havePiece(piece) {
- return
- }
- t.pendingPieces.Set(piece, PiecePriorityNormal.BitmapPriority())
- t.updatePiecePriority(piece)
-}
-
-func (t *Torrent) unpendPieces(unpend bitmap.Bitmap) {
- unpend.IterTyped(func(piece int) (more bool) {
- t.pendingPieces.Remove(piece)
- t.updatePiecePriority(piece)
- return true
- })
-}
-
-func (t *Torrent) pendPieceRange(begin, end int) {
- for i := begin; i < end; i++ {
- t.pendPiece(i)
- }
-}
-
-func (t *Torrent) unpendPieceRange(begin, end int) {
- var bm bitmap.Bitmap
- bm.AddRange(begin, end)
- t.unpendPieces(bm)
+ return piecePriority(-prio)
}
func (t *Torrent) pendRequest(req request) {
changed := t.completedPieces.Get(piece) != pcu.Complete || p.storageCompletionOk != pcu.Ok
p.storageCompletionOk = pcu.Ok
t.completedPieces.Set(piece, pcu.Complete)
+ // log.Printf("piece %d uncached completion: %v", piece, pcu.Complete)
+ // log.Printf("piece %d changed: %v", piece, changed)
if changed {
t.pieceCompletionChanged(piece)
}
if !t.haveInfo() {
return true
}
- if t.pendingPieces.Len() != 0 {
- return true
- }
- // Read as "not all complete".
- return !t.readerPieces().IterTyped(func(piece int) bool {
- return t.pieceComplete(piece)
- })
+ return t.pendingPieces.Len() != 0
}
func appendMissingStrings(old, new []string) (ret []string) {
t.pendAllChunkSpecs(piece)
}
if !t.wantPieceIndex(piece) {
+ // log.Printf("piece %d incomplete and unwanted", piece)
return
}
// We could drop any connections that we told we have a piece that we