client.go | 2 ++ connection.go | 80 ++++++++++++++++++++++++++++++++++++++++++----------- torrent.go | 5 +++++ diff --git a/client.go b/client.go index 13f5d24abac96a2c4970f293da741a284decca5c..e66e519c9d29c215ae81baf2bb5aaab34081abbf 100644 --- a/client.go +++ b/client.go @@ -1178,6 +1178,8 @@ pieceStateChanges: pubsub.NewPubSub(), storageOpener: storageClient, maxEstablishedConns: defaultEstablishedConnsPerTorrent, + + networkingEnabled: true, } t.setChunkSize(defaultChunkSize) return diff --git a/connection.go b/connection.go index aca2b348587a1b16ac155aaddda38d0a768b5260..d496f0c70032e66330df72cc1b6bd0d3850952c0 100644 --- a/connection.go +++ b/connection.go @@ -484,26 +484,67 @@ // changes in .sentHaves. cn.sentHaves = append([]bool(nil), haves...) } -func (cn *connection) updateRequests() { - if !cn.t.haveInfo() { - return +func nextRequestState( + networkingEnabled bool, + currentRequests map[request]struct{}, + peerChoking bool, + nextPieces prioritybitmap.PriorityBitmap, + pendingChunks func(piece int, f func(chunkSpec) bool) bool, + requestsLowWater int, + requestsHighWater int, +) ( + requests map[request]struct{}, + interested bool, +) { + if !networkingEnabled || nextPieces.IsEmpty() { + return nil, false } - if cn.Interested { - if cn.PeerChoked { - return - } - if len(cn.Requests) > cn.requestsLowWater { - return + if peerChoking || len(currentRequests) > requestsLowWater { + return currentRequests, true + } + requests = make(map[request]struct{}, requestsHighWater) + for r := range currentRequests { + requests[r] = struct{}{} + } + nextPieces.IterTyped(func(piece int) bool { + return pendingChunks(piece, func(cs chunkSpec) bool { + if len(requests) >= requestsHighWater { + return false + } + r := request{pp.Integer(piece), cs} + requests[r] = struct{}{} + return true + }) + }) + return requests, true +} + +func (cn *connection) updateRequests() { + rs, i := nextRequestState( + cn.t.networkingEnabled, + cn.Requests, + cn.PeerChoked, + cn.pieceRequestOrder, + func(piece int, f func(chunkSpec) bool) bool { + return undirtiedChunks(piece, cn.t, f) + }, + cn.requestsLowWater, + cn.nominalMaxRequests()) + for r := range cn.Requests { + if _, ok := rs[r]; !ok { + if !cn.Cancel(r) { + panic("wat") + } } } - cn.fillRequests() - if len(cn.Requests) == 0 && !cn.PeerChoked { - // So we're not choked, but we don't want anything right now. We may - // have completed readahead, and the readahead window has not rolled - // over to the next piece. Better to stay interested in case we're - // going to want data in the near future. - cn.SetInterested(!cn.t.haveAllPieces()) + for r := range rs { + if _, ok := cn.Requests[r]; !ok { + if !cn.Request(r) { + panic("how") + } + } } + cn.SetInterested(i) } func (cn *connection) fillRequests() { @@ -523,6 +564,13 @@ chunkIndices := c.t.pieces[piece].undirtiedChunkIndices().ToSortedSlice() return iter.ForPerm(len(chunkIndices), func(i int) bool { req := request{pp.Integer(piece), c.t.chunkIndexSpec(chunkIndices[i], piece)} return c.Request(req) + }) +} + +func undirtiedChunks(piece int, t *Torrent, f func(chunkSpec) bool) bool { + chunkIndices := t.pieces[piece].undirtiedChunkIndices().ToSortedSlice() + return iter.ForPerm(len(chunkIndices), func(i int) bool { + return f(t.chunkIndexSpec(chunkIndices[i], piece)) }) } diff --git a/torrent.go b/torrent.go index 8230acbfd574ad7164c44b28b55d304713c82644..d361bd6e338828f0289b934d4d7dd7ae5b5d3ad0 100644 --- a/torrent.go +++ b/torrent.go @@ -43,6 +43,8 @@ // Maintains state of torrent within a Client. type Torrent struct { cl *Client + networkingEnabled bool + closed missinggo.Event infoHash metainfo.Hash pieces []piece @@ -1348,6 +1350,9 @@ return true } func (t *Torrent) wantConns() bool { + if !t.networkingEnabled { + return false + } if t.closed.IsSet() { return false }