]> Sergey Matveev's repositories - btrtrc.git/blob - piece.go
Recalculate all piece priorities more efficiently
[btrtrc.git] / piece.go
1 package torrent
2
3 import (
4         "math/rand"
5         "sync"
6
7         "github.com/bradfitz/iter"
8
9         pp "github.com/anacrolix/torrent/peer_protocol"
10 )
11
12 // Piece priority describes the importance of obtaining a particular piece.
13
14 type piecePriority byte
15
16 func (me *piecePriority) Raise(maybe piecePriority) {
17         if maybe > *me {
18                 *me = maybe
19         }
20 }
21
22 const (
23         PiecePriorityNone      piecePriority = iota // Not wanted.
24         PiecePriorityNormal                         // Wanted.
25         PiecePriorityReadahead                      // May be required soon.
26         PiecePriorityNext                           // Succeeds a piece where a read occurred.
27         PiecePriorityNow                            // A read occurred in this piece.
28 )
29
30 type piece struct {
31         // The completed piece SHA1 hash, from the metainfo "pieces" field.
32         Hash pieceSum
33         // Chunks we've written to since the last check. The chunk offset and
34         // length can be determined by the request chunkSize in use.
35         DirtyChunks      []bool
36         Hashing          bool
37         QueuedForHash    bool
38         EverHashed       bool
39         PublicPieceState PieceState
40         priority         piecePriority
41
42         pendingWritesMutex sync.Mutex
43         pendingWrites      int
44         noPendingWrites    sync.Cond
45 }
46
47 func (p *piece) pendingChunk(cs chunkSpec, chunkSize pp.Integer) bool {
48         ci := chunkIndex(cs, chunkSize)
49         if ci >= len(p.DirtyChunks) {
50                 return true
51         }
52         return !p.DirtyChunks[ci]
53 }
54
55 func (p *piece) numDirtyChunks() (ret int) {
56         for _, dirty := range p.DirtyChunks {
57                 if dirty {
58                         ret++
59                 }
60         }
61         return
62 }
63
64 func (p *piece) unpendChunkIndex(i int) {
65         for i >= len(p.DirtyChunks) {
66                 p.DirtyChunks = append(p.DirtyChunks, false)
67         }
68         p.DirtyChunks[i] = true
69 }
70
71 func (p *piece) pendChunkIndex(i int) {
72         if i >= len(p.DirtyChunks) {
73                 return
74         }
75         p.DirtyChunks[i] = false
76 }
77
78 func chunkIndexSpec(index int, pieceLength, chunkSize pp.Integer) chunkSpec {
79         ret := chunkSpec{pp.Integer(index) * chunkSize, chunkSize}
80         if ret.Begin+ret.Length > pieceLength {
81                 ret.Length = pieceLength - ret.Begin
82         }
83         return ret
84 }
85
86 func (p *piece) shuffledPendingChunkSpecs(t *torrent, piece int) (css []chunkSpec) {
87         // defer func() {
88         //      log.Println(piece, css)
89         // }()
90         numPending := t.pieceNumPendingChunks(piece)
91         if numPending == 0 {
92                 return
93         }
94         css = make([]chunkSpec, 0, numPending)
95         for ci := range iter.N(t.pieceNumChunks(piece)) {
96                 if ci >= len(p.DirtyChunks) || !p.DirtyChunks[ci] {
97                         css = append(css, t.chunkIndexSpec(ci, piece))
98                 }
99         }
100         if len(css) <= 1 {
101                 return
102         }
103         for i := range css {
104                 j := rand.Intn(i + 1)
105                 css[i], css[j] = css[j], css[i]
106         }
107         return
108 }
109
110 func (p *piece) incrementPendingWrites() {
111         p.pendingWritesMutex.Lock()
112         p.pendingWrites++
113         p.pendingWritesMutex.Unlock()
114 }
115
116 func (p *piece) decrementPendingWrites() {
117         p.pendingWritesMutex.Lock()
118         if p.pendingWrites == 0 {
119                 panic("assertion")
120         }
121         p.pendingWrites--
122         if p.pendingWrites == 0 {
123                 p.noPendingWrites.Broadcast()
124         }
125         p.pendingWritesMutex.Unlock()
126 }
127
128 func (p *piece) waitNoPendingWrites() {
129         p.pendingWritesMutex.Lock()
130         for p.pendingWrites != 0 {
131                 p.noPendingWrites.Wait()
132         }
133         p.pendingWritesMutex.Unlock()
134 }