if err != nil {
return
}
- // Queue all pieces for hashing. This is done sequentially to avoid
- // spamming goroutines.
- for _, p := range t.Pieces {
- p.QueuedForHash = true
- }
- go func() {
- for i := range t.Pieces {
- cl.verifyPiece(t, pp.Integer(i))
+ // If the client intends to upload, it needs to know what state pieces are
+ // in.
+ if !cl.noUpload {
+ // Queue all pieces for hashing. This is done sequentially to avoid
+ // spamming goroutines.
+ for _, p := range t.Pieces {
+ p.QueuedForHash = true
}
- }()
+ go func() {
+ for i := range t.Pieces {
+ cl.verifyPiece(t, pp.Integer(i))
+ }
+ }()
+ }
cl.downloadStrategy.TorrentStarted(t)
return
func (cl *Client) allTorrentsCompleted() bool {
for _, t := range cl.torrents {
- if !t.haveAllPieces() {
+ if !t.haveInfo() {
+ return false
+ }
+ for e := t.IncompletePiecesByBytesLeft.Front(); e != nil; e = e.Next() {
+ i := e.Value.(int)
+ if t.Pieces[i].Complete() {
+ continue
+ }
+ // If the piece isn't complete, make sure it's not because it's
+ // never been hashed.
+ cl.queueFirstHash(t, i)
return false
}
}
if !t.haveInfo() {
return
}
- me.downloadStrategy.FillRequests(t, c)
+ for _, p := range me.downloadStrategy.FillRequests(t, c) {
+ // Make sure the state of pieces that would have been requested is
+ // known.
+ me.queueFirstHash(t, p)
+ }
//me.assertRequestHeat()
if len(c.Requests) == 0 && !c.PeerChoked {
c.SetInterested(false)
)
type DownloadStrategy interface {
- FillRequests(*torrent, *connection)
+ // Tops up the outgoing pending requests. Returns the indices of pieces
+ // that would be requested. This is used to determine if pieces require
+ // hashing so the completed state is known.
+ FillRequests(*torrent, *connection) (pieces []int)
TorrentStarted(*torrent)
TorrentStopped(*torrent)
DeleteRequest(*torrent, request)
func (me *DefaultDownloadStrategy) WriteStatus(w io.Writer) {}
-func (s *DefaultDownloadStrategy) FillRequests(t *torrent, c *connection) {
+func (s *DefaultDownloadStrategy) FillRequests(t *torrent, c *connection) (pieces []int) {
if c.Interested {
if c.PeerChoked {
return
for _, heatThreshold := range []int{1, 4, 15, 60} {
for e := t.IncompletePiecesByBytesLeft.Front(); e != nil; e = e.Next() {
pieceIndex := pp.Integer(e.Value.(int))
+ piece := t.Pieces[pieceIndex]
+ if !piece.EverHashed {
+ pieces = append(pieces, int(pieceIndex))
+ return
+ }
for chunkSpec := range t.Pieces[pieceIndex].PendingChunkSpecs {
r := request{pieceIndex, chunkSpec}
if th[r] >= heatThreshold {
}
}
}
+ return
}
func (s *DefaultDownloadStrategy) TorrentStarted(t *torrent) {
c *connection
t *torrent
s *responsiveDownloadStrategy
+
+ // The set of pieces that were considered for requesting.
+ pieces map[int]struct{}
}
// Wrapper around connection.request that tracks request heat.
func (me *requestFiller) request(req request) bool {
+ me.pieces[int(req.Index)] = struct{}{}
if me.c.RequestPending(req) {
return true
}
}
ret = make([]request, 0, (me.s.Readahead+chunkSize-1)/chunkSize)
for pi := int(lastReadOffset / int64(t.UsualPieceSize())); pi < t.NumPieces() && int64(pi)*int64(t.UsualPieceSize()) < lastReadOffset+me.s.Readahead; pi++ {
- if !t.wantPiece(pi) || !me.c.PeerHasPiece(pp.Integer(pi)) {
+ if t.havePiece(pi) || !me.c.PeerHasPiece(pp.Integer(pi)) {
continue
}
for cs := range t.Pieces[pi].PendingChunkSpecs {
return true
}
-func (me *responsiveDownloadStrategy) FillRequests(t *torrent, c *connection) {
- (requestFiller{c, t, me}).Run()
+func (me *responsiveDownloadStrategy) FillRequests(t *torrent, c *connection) (pieces []int) {
+ rf := requestFiller{c, t, me, make(map[int]struct{}, t.NumPieces())}
+ rf.Run()
+ for p := range rf.pieces {
+ pieces = append(pieces, p)
+ }
+ return
}
func (me *responsiveDownloadStrategy) TorrentGotChunk(t *torrent, req request) {