]> Sergey Matveev's repositories - btrtrc.git/blob - request_strategy.go
dc7538d1d32c39a888b4566cf27555c70666a835
[btrtrc.git] / request_strategy.go
1 package torrent
2
3 import (
4         "sync"
5         "time"
6
7         "github.com/anacrolix/missinggo/v2/bitmap"
8         "github.com/anacrolix/missinggo/v2/prioritybitmap"
9
10         pp "github.com/anacrolix/torrent/peer_protocol"
11 )
12
13 type requestStrategyPiece interface {
14         numChunks() pp.Integer
15         dirtyChunks() bitmap.Bitmap
16         chunkIndexRequest(i pp.Integer) request
17 }
18
19 type requestStrategyTorrent interface {
20         numConns() int
21         numReaders() int
22         numPieces() int
23         readerPiecePriorities() (now, readahead bitmap.Bitmap)
24         ignorePieces() bitmap.Bitmap
25         pendingPieces() *prioritybitmap.PriorityBitmap
26 }
27
28 type requestStrategyConnection interface {
29         torrent() requestStrategyTorrent
30         peerPieces() bitmap.Bitmap
31         pieceRequestOrder() *prioritybitmap.PriorityBitmap
32         fastest() bool
33         stats() *ConnStats
34         totalExpectingTime() time.Duration
35         peerMaxRequests() int
36         chunksReceivedWhileExpecting() int64
37 }
38
39 type requestStrategyDefaults struct{}
40
41 func (requestStrategyDefaults) hooks() requestStrategyHooks {
42         return requestStrategyHooks{
43                 sentRequest:    func(request) {},
44                 deletedRequest: func(request) {},
45         }
46 }
47
48 type requestStrategy interface {
49         iterPendingPieces(requestStrategyConnection, func(pieceIndex) bool) bool
50         iterUndirtiedChunks(requestStrategyPiece, func(chunkSpec) bool) bool
51         nominalMaxRequests(requestStrategyConnection) int
52         shouldRequestWithoutBias(requestStrategyConnection) bool
53         piecePriority(requestStrategyConnection, pieceIndex, piecePriority, int) int
54         hooks() requestStrategyHooks
55 }
56
57 type requestStrategyHooks struct {
58         sentRequest    func(request)
59         deletedRequest func(request)
60 }
61
62 type requestStrategyCallbacks interface {
63         requestTimedOut(request)
64 }
65
66 // Favour higher priority pieces with some fuzzing to reduce overlaps and wastage across
67 // connections.
68 type requestStrategyFuzzing struct {
69         requestStrategyDefaults
70 }
71
72 // The fastest connection downloads strictly in order of priority, while all others adhere to their
73 // piece inclinations.
74 type requestStrategyFastest struct {
75         requestStrategyDefaults
76 }
77
78 func newRequestStrategyMaker(rs requestStrategy) RequestStrategyMaker {
79         return func(requestStrategyCallbacks, sync.Locker) requestStrategy {
80                 return rs
81         }
82 }
83
84 func RequestStrategyFastest() RequestStrategyMaker {
85         return newRequestStrategyMaker(requestStrategyFastest{})
86 }
87
88 func RequestStrategyFuzzing() RequestStrategyMaker {
89         return newRequestStrategyMaker(requestStrategyFuzzing{})
90 }
91
92 func (requestStrategyFastest) ShouldRequestWithoutBias(cn requestStrategyConnection) bool {
93         if cn.torrent().numReaders() == 0 {
94                 return false
95         }
96         if cn.torrent().numConns() == 1 {
97                 return true
98         }
99         if cn.fastest() {
100                 return true
101         }
102         return false
103 }
104
105 // Requests are strictly by piece priority, and not duplicated until duplicateRequestTimeout is
106 // reached.
107 type requestStrategyDuplicateRequestTimeout struct {
108         // How long to avoid duplicating a pending request.
109         duplicateRequestTimeout time.Duration
110
111         callbacks requestStrategyCallbacks
112
113         // The last time we requested a chunk. Deleting the request from any connection will clear this
114         // value.
115         lastRequested map[request]*time.Timer
116         // The lock to take when running a request timeout handler.
117         timeoutLocker sync.Locker
118 }
119
120 type RequestStrategyMaker func(callbacks requestStrategyCallbacks, clientLocker sync.Locker) requestStrategy
121
122 func RequestStrategyDuplicateRequestTimeout(duplicateRequestTimeout time.Duration) RequestStrategyMaker {
123         return func(callbacks requestStrategyCallbacks, clientLocker sync.Locker) requestStrategy {
124                 return requestStrategyDuplicateRequestTimeout{
125                         duplicateRequestTimeout: duplicateRequestTimeout,
126                         callbacks:               callbacks,
127                         lastRequested:           make(map[request]*time.Timer),
128                         timeoutLocker:           clientLocker,
129                 }
130         }
131 }
132
133 func (rs requestStrategyDuplicateRequestTimeout) hooks() requestStrategyHooks {
134         return requestStrategyHooks{
135                 deletedRequest: func(r request) {
136                         if t, ok := rs.lastRequested[r]; ok {
137                                 t.Stop()
138                                 delete(rs.lastRequested, r)
139                         }
140                 },
141                 sentRequest: rs.onSentRequest,
142         }
143 }