]> Sergey Matveev's repositories - btrtrc.git/blob - t.go
Improve some lock handling under panics
[btrtrc.git] / t.go
1 package torrent
2
3 import (
4         "strconv"
5         "strings"
6
7         "github.com/anacrolix/missinggo/pubsub"
8
9         "github.com/anacrolix/torrent/metainfo"
10 )
11
12 // The Torrent's infohash. This is fixed and cannot change. It uniquely identifies a torrent.
13 func (t *Torrent) InfoHash() metainfo.Hash {
14         return t.infoHash
15 }
16
17 // Returns a channel that is closed when the info (.Info()) for the torrent has become available.
18 func (t *Torrent) GotInfo() <-chan struct{} {
19         // TODO: We shouldn't need to lock to take a channel here, if the event is only ever set.
20         t.cl.lock()
21         defer t.cl.unlock()
22         return t.gotMetainfo.C()
23 }
24
25 // Returns the metainfo info dictionary, or nil if it's not yet available.
26 func (t *Torrent) Info() *metainfo.Info {
27         t.cl.lock()
28         defer t.cl.unlock()
29         return t.info
30 }
31
32 // Returns a Reader bound to the torrent's data. All read calls block until the data requested is
33 // actually available. Note that you probably want to ensure the Torrent Info is available first.
34 func (t *Torrent) NewReader() Reader {
35         r := reader{
36                 mu:        t.cl.locker(),
37                 t:         t,
38                 readahead: 5 * 1024 * 1024,
39                 length:    *t.length,
40         }
41         t.addReader(&r)
42         return &r
43 }
44
45 type PieceStateRuns []PieceStateRun
46
47 func (me PieceStateRuns) String() string {
48         ss := make([]string, 0, len(me))
49         for _, psr := range me {
50                 ss = append(ss, psr.String())
51         }
52         return strings.Join(ss, " ")
53 }
54
55 // Returns the state of pieces of the torrent. They are grouped into runs of same state. The sum of
56 // the state run-lengths is the number of pieces in the torrent.
57 func (t *Torrent) PieceStateRuns() PieceStateRuns {
58         t.cl.rLock()
59         defer t.cl.rUnlock()
60         return t.pieceStateRuns()
61 }
62
63 func (t *Torrent) PieceState(piece pieceIndex) PieceState {
64         t.cl.rLock()
65         defer t.cl.rUnlock()
66         return t.pieceState(piece)
67 }
68
69 // The number of pieces in the torrent. This requires that the info has been
70 // obtained first.
71 func (t *Torrent) NumPieces() pieceIndex {
72         return t.numPieces()
73 }
74
75 // Get missing bytes count for specific piece.
76 func (t *Torrent) PieceBytesMissing(piece int) int64 {
77         t.cl.lock()
78         defer t.cl.unlock()
79
80         return int64(t.pieces[piece].bytesLeft())
81 }
82
83 // Drop the torrent from the client, and close it. It's always safe to do
84 // this. No data corruption can, or should occur to either the torrent's data,
85 // or connected peers.
86 func (t *Torrent) Drop() {
87         t.cl.lock()
88         defer t.cl.unlock()
89         t.cl.dropTorrent(t.infoHash)
90 }
91
92 // Number of bytes of the entire torrent we have completed. This is the sum of
93 // completed pieces, and dirtied chunks of incomplete pieces. Do not use this
94 // for download rate, as it can go down when pieces are lost or fail checks.
95 // Sample Torrent.Stats.DataBytesRead for actual file data download rate.
96 func (t *Torrent) BytesCompleted() int64 {
97         t.cl.rLock()
98         defer t.cl.rUnlock()
99         return t.bytesCompleted()
100 }
101
102 // The subscription emits as (int) the index of pieces as their state changes.
103 // A state change is when the PieceState for a piece alters in value.
104 func (t *Torrent) SubscribePieceStateChanges() *pubsub.Subscription {
105         return t.pieceStateChanges.Subscribe()
106 }
107
108 // Returns true if the torrent is currently being seeded. This occurs when the
109 // client is willing to upload without wanting anything in return.
110 func (t *Torrent) Seeding() bool {
111         t.cl.lock()
112         defer t.cl.unlock()
113         return t.seeding()
114 }
115
116 // Clobbers the torrent display name. The display name is used as the torrent
117 // name if the metainfo is not available.
118 func (t *Torrent) SetDisplayName(dn string) {
119         t.nameMu.Lock()
120         defer t.nameMu.Unlock()
121         if t.haveInfo() {
122                 return
123         }
124         t.displayName = dn
125 }
126
127 // The current working name for the torrent. Either the name in the info dict,
128 // or a display name given such as by the dn value in a magnet link, or "".
129 func (t *Torrent) Name() string {
130         return t.name()
131 }
132
133 // The completed length of all the torrent data, in all its files. This is
134 // derived from the torrent info, when it is available.
135 func (t *Torrent) Length() int64 {
136         return *t.length
137 }
138
139 // Returns a run-time generated metainfo for the torrent that includes the
140 // info bytes and announce-list as currently known to the client.
141 func (t *Torrent) Metainfo() metainfo.MetaInfo {
142         t.cl.lock()
143         defer t.cl.unlock()
144         return t.newMetaInfo()
145 }
146
147 func (t *Torrent) addReader(r *reader) {
148         t.cl.lock()
149         defer t.cl.unlock()
150         if t.readers == nil {
151                 t.readers = make(map[*reader]struct{})
152         }
153         t.readers[r] = struct{}{}
154         r.posChanged()
155 }
156
157 func (t *Torrent) deleteReader(r *reader) {
158         delete(t.readers, r)
159         t.readersChanged()
160 }
161
162 // Raise the priorities of pieces in the range [begin, end) to at least Normal
163 // priority. Piece indexes are not the same as bytes. Requires that the info
164 // has been obtained, see Torrent.Info and Torrent.GotInfo.
165 func (t *Torrent) DownloadPieces(begin, end pieceIndex) {
166         t.cl.lock()
167         defer t.cl.unlock()
168         t.downloadPiecesLocked(begin, end)
169 }
170
171 func (t *Torrent) downloadPiecesLocked(begin, end pieceIndex) {
172         for i := begin; i < end; i++ {
173                 if t.pieces[i].priority.Raise(PiecePriorityNormal) {
174                         t.updatePiecePriority(i)
175                 }
176         }
177 }
178
179 func (t *Torrent) CancelPieces(begin, end pieceIndex) {
180         t.cl.lock()
181         defer t.cl.unlock()
182         t.cancelPiecesLocked(begin, end)
183 }
184
185 func (t *Torrent) cancelPiecesLocked(begin, end pieceIndex) {
186         for i := begin; i < end; i++ {
187                 p := &t.pieces[i]
188                 if p.priority == PiecePriorityNone {
189                         continue
190                 }
191                 p.priority = PiecePriorityNone
192                 t.updatePiecePriority(i)
193         }
194 }
195
196 func (t *Torrent) initFiles() {
197         var offset int64
198         t.files = new([]*File)
199         for _, fi := range t.info.UpvertedFiles() {
200                 var path []string
201                 if len(fi.PathUTF8) != 0 {
202                         path = fi.PathUTF8
203                 } else {
204                         path = fi.Path
205                 }
206                 *t.files = append(*t.files, &File{
207                         t,
208                         strings.Join(append([]string{t.info.Name}, path...), "/"),
209                         offset,
210                         fi.Length,
211                         fi,
212                         PiecePriorityNone,
213                 })
214                 offset += fi.Length
215         }
216
217 }
218
219 // Returns handles to the files in the torrent. This requires that the Info is
220 // available first.
221 func (t *Torrent) Files() []*File {
222         return *t.files
223 }
224
225 func (t *Torrent) AddPeers(pp []PeerInfo) int {
226         cl := t.cl
227         cl.lock()
228         defer cl.unlock()
229         return t.addPeers(pp)
230 }
231
232 // Marks the entire torrent for download. Requires the info first, see
233 // GotInfo. Sets piece priorities for historical reasons.
234 func (t *Torrent) DownloadAll() {
235         t.DownloadPieces(0, t.numPieces())
236 }
237
238 func (t *Torrent) String() string {
239         s := t.name()
240         if s == "" {
241                 return t.infoHash.HexString()
242         } else {
243                 return strconv.Quote(s)
244         }
245 }
246
247 func (t *Torrent) AddTrackers(announceList [][]string) {
248         t.cl.lock()
249         defer t.cl.unlock()
250         t.addTrackers(announceList)
251 }
252
253 func (t *Torrent) Piece(i pieceIndex) *Piece {
254         return t.piece(i)
255 }
256
257 func (t *Torrent) PeerConns() []*PeerConn {
258         t.cl.rLock()
259         defer t.cl.rUnlock()
260         ret := make([]*PeerConn, 0, len(t.conns))
261         for c := range t.conns {
262                 ret = append(ret, c)
263         }
264         return ret
265 }