From: Matt Joiner Date: Thu, 17 Aug 2017 15:51:02 +0000 (+1000) Subject: Try a state-delta function for updating request state X-Git-Tag: v1.0.0~441 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=76c60ffa77016feeeb31ea732f6deb056178520a;p=btrtrc.git Try a state-delta function for updating request state Also adds Torrent.networkingEnabled, though it isn't yet useful. --- diff --git a/client.go b/client.go index 13f5d24a..e66e519c 100644 --- a/client.go +++ b/client.go @@ -1178,6 +1178,8 @@ func (cl *Client) newTorrent(ih metainfo.Hash, specStorage storage.ClientImpl) ( storageOpener: storageClient, maxEstablishedConns: defaultEstablishedConnsPerTorrent, + + networkingEnabled: true, } t.setChunkSize(defaultChunkSize) return diff --git a/connection.go b/connection.go index aca2b348..d496f0c7 100644 --- a/connection.go +++ b/connection.go @@ -484,26 +484,67 @@ func (cn *connection) Bitfield(haves []bool) { cn.sentHaves = append([]bool(nil), haves...) } +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 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() { - if !cn.t.haveInfo() { - return - } - if cn.Interested { - if cn.PeerChoked { - return - } - if len(cn.Requests) > cn.requestsLowWater { - return + 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() { @@ -526,6 +567,13 @@ func (c *connection) requestPiecePendingChunks(piece int) (again bool) { }) } +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)) + }) +} + func (cn *connection) stopRequestingPiece(piece int) { cn.pieceRequestOrder.Remove(piece) cn.updateRequests() diff --git a/torrent.go b/torrent.go index 8230acbf..d361bd6e 100644 --- a/torrent.go +++ b/torrent.go @@ -43,6 +43,8 @@ type peersKey struct { type Torrent struct { cl *Client + networkingEnabled bool + closed missinggo.Event infoHash metainfo.Hash pieces []piece @@ -1348,6 +1350,9 @@ func (t *Torrent) addConnection(c *connection, outgoing bool) bool { } func (t *Torrent) wantConns() bool { + if !t.networkingEnabled { + return false + } if t.closed.IsSet() { return false }