github.com/anacrolix/torrent@v1.61.0/piece.go (about) 1 package torrent 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "iter" 8 "sync" 9 10 "github.com/RoaringBitmap/roaring" 11 "github.com/anacrolix/chansync" 12 g "github.com/anacrolix/generics" 13 "github.com/anacrolix/missinggo/v2/bitmap" 14 "github.com/anacrolix/missinggo/v2/panicif" 15 16 "github.com/anacrolix/torrent/merkle" 17 "github.com/anacrolix/torrent/metainfo" 18 pp "github.com/anacrolix/torrent/peer_protocol" 19 "github.com/anacrolix/torrent/segments" 20 "github.com/anacrolix/torrent/storage" 21 ) 22 23 // Why is it an int64? 24 type pieceVerifyCount = int64 25 26 type Piece struct { 27 // The completed piece SHA1 hash, from the metainfo "pieces" field. Nil if the info is not V1 28 // compatible. 29 hash *metainfo.Hash 30 // Not easy to use unique.Handle because we need this as a slice sometimes. 31 hashV2 g.Option[[32]byte] 32 t *Torrent 33 index pieceIndex 34 // First and one after the last file indexes. 35 beginFile, endFile int 36 37 readerCond chansync.BroadcastCond 38 39 numVerifies pieceVerifyCount 40 numVerifiesCond chansync.BroadcastCond 41 42 publicPieceState PieceState 43 // Piece-specific priority. There are other priorities like File and Reader. 44 priority PiecePriority 45 // Availability adjustment for this piece relative to len(Torrent.connsWithAllPieces). This is 46 // incremented for any piece a peer has when a peer has a piece, Torrent.haveInfo is true, and 47 // the Peer isn't recorded in Torrent.connsWithAllPieces. 48 relativeAvailability int 49 50 // This can be locked when the Client lock is taken, but probably not vice versa. 51 pendingWritesMutex sync.Mutex 52 pendingWrites int 53 noPendingWrites sync.Cond 54 55 // Connections that have written data to this piece since its last check. 56 // This can include connections that have closed. 57 dirtiers map[*Peer]struct{} 58 59 // Value to twiddle to detect races. 60 race byte 61 // Currently being hashed. 62 hashing bool 63 // The piece state may have changed, and is being synchronized with storage. 64 marking bool 65 // The Completion.Ok field cached from the storage layer. 66 storageCompletionOk bool 67 // Sticks on once set for the first time. 68 storageCompletionHasBeenOk bool 69 } 70 71 func (p *Piece) String() string { 72 return fmt.Sprintf("%s/%d", p.t.canonicalShortInfohash().HexString(), p.index) 73 } 74 75 func (p *Piece) Info() metainfo.Piece { 76 return p.t.info.Piece(p.index) 77 } 78 79 func (p *Piece) Storage() storage.Piece { 80 var pieceHash g.Option[[]byte] 81 if p.hash != nil { 82 pieceHash.Set(p.hash.Bytes()) 83 } else if !p.hasPieceLayer() { 84 pieceHash.Set(p.mustGetOnlyFile().piecesRoot.UnwrapPtr()[:]) 85 } else if p.hashV2.Ok { 86 pieceHash.Set(p.hashV2.Value[:]) 87 } 88 return p.t.storage.PieceWithHash(p.Info(), pieceHash) 89 } 90 91 func (p *Piece) pendingChunkIndex(chunkIndex chunkIndexType) bool { 92 return !p.chunkIndexDirty(chunkIndex) 93 } 94 95 func (p *Piece) pendingChunk(cs ChunkSpec, chunkSize pp.Integer) bool { 96 return p.pendingChunkIndex(chunkIndexFromChunkSpec(cs, chunkSize)) 97 } 98 99 func (p *Piece) hasDirtyChunks() bool { 100 return p.numDirtyChunks() != 0 101 } 102 103 func (p *Piece) numDirtyChunks() chunkIndexType { 104 return chunkIndexType(roaringBitmapRangeCardinality[RequestIndex]( 105 &p.t.dirtyChunks, 106 p.requestIndexBegin(), 107 p.t.pieceRequestIndexBegin(p.index+1), 108 )) 109 } 110 111 func (p *Piece) unpendChunkIndex(i chunkIndexType) { 112 p.t.dirtyChunks.Add(p.requestIndexBegin() + i) 113 p.t.updatePieceRequestOrderPiece(p.index) 114 p.readerCond.Broadcast() 115 } 116 117 func (p *Piece) pendChunkIndex(i RequestIndex) { 118 p.t.dirtyChunks.Remove(p.requestIndexBegin() + i) 119 p.t.updatePieceRequestOrderPiece(p.index) 120 } 121 122 func (p *Piece) numChunks() chunkIndexType { 123 return p.t.pieceNumChunks(p.index) 124 } 125 126 func (p *Piece) incrementPendingWrites() { 127 p.pendingWritesMutex.Lock() 128 p.pendingWrites++ 129 p.pendingWritesMutex.Unlock() 130 } 131 132 func (p *Piece) decrementPendingWrites() { 133 p.pendingWritesMutex.Lock() 134 if p.pendingWrites == 0 { 135 panic("assertion") 136 } 137 p.pendingWrites-- 138 if p.pendingWrites == 0 { 139 p.noPendingWrites.Broadcast() 140 } 141 p.pendingWritesMutex.Unlock() 142 } 143 144 func (p *Piece) waitNoPendingWrites() { 145 p.pendingWritesMutex.Lock() 146 for p.pendingWrites != 0 { 147 p.noPendingWrites.Wait() 148 } 149 p.pendingWritesMutex.Unlock() 150 } 151 152 func (p *Piece) chunkIndexDirty(chunk chunkIndexType) bool { 153 return p.t.dirtyChunks.Contains(p.requestIndexBegin() + chunk) 154 } 155 156 func (p *Piece) iterCleanChunks(it *roaring.IntIterator) iter.Seq[chunkIndexType] { 157 return func(yield func(chunkIndexType) bool) { 158 it.Initialize(&p.t.dirtyChunks.Bitmap) 159 begin := uint32(p.requestIndexBegin()) 160 end := uint32(p.requestIndexMaxEnd()) 161 it.AdvanceIfNeeded(begin) 162 for next := begin; next < end; next++ { 163 if !it.HasNext() || it.Next() != next { 164 if !yield(chunkIndexType(next - begin)) { 165 return 166 } 167 } 168 } 169 return 170 } 171 } 172 173 func (p *Piece) firstCleanChunk() (_ g.Option[chunkIndexType]) { 174 for some := range p.iterCleanChunks(&p.t.cl.roaringIntIterator) { 175 return g.Some(some) 176 } 177 return 178 } 179 180 func (p *Piece) chunkIndexSpec(chunk chunkIndexType) ChunkSpec { 181 return chunkIndexSpec(pp.Integer(chunk), p.length(), p.chunkSize()) 182 } 183 184 func (p *Piece) numDirtyBytes() (ret pp.Integer) { 185 // defer func() { 186 // if ret > p.length() { 187 // panic("too many dirty bytes") 188 // } 189 // }() 190 numRegularDirtyChunks := p.numDirtyChunks() 191 if p.chunkIndexDirty(p.numChunks() - 1) { 192 numRegularDirtyChunks-- 193 ret += p.chunkIndexSpec(p.lastChunkIndex()).Length 194 } 195 ret += pp.Integer(numRegularDirtyChunks) * p.chunkSize() 196 return 197 } 198 199 func (p *Piece) length() pp.Integer { 200 return p.t.pieceLength(p.index) 201 } 202 203 func (p *Piece) chunkSize() pp.Integer { 204 return p.t.chunkSize 205 } 206 207 func (p *Piece) lastChunkIndex() chunkIndexType { 208 return p.numChunks() - 1 209 } 210 211 func (p *Piece) bytesLeft() (ret pp.Integer) { 212 if p.t.pieceComplete(p.index) { 213 return 0 214 } 215 return p.length() - p.numDirtyBytes() 216 } 217 218 // Forces the piece data to be rehashed. 219 func (p *Piece) VerifyData() error { 220 return p.VerifyDataContext(context.Background()) 221 } 222 223 // Forces the piece data to be rehashed. This might be a temporary method until 224 // an event-based one is created. Possibly this blocking style is more suited to 225 // external control of hashing concurrency. 226 func (p *Piece) VerifyDataContext(ctx context.Context) error { 227 locker := p.t.cl.locker() 228 locker.Lock() 229 if p.t.closed.IsSet() { 230 return errTorrentClosed 231 } 232 target, err := p.t.queuePieceCheck(p.index) 233 locker.Unlock() 234 if err != nil { 235 return err 236 } 237 //log.Printf("target: %d", target) 238 for { 239 locker.RLock() 240 done := p.numVerifies >= target 241 //log.Printf("got %d verifies", p.numVerifies) 242 numVerifiesChanged := p.numVerifiesCond.Signaled() 243 locker.RUnlock() 244 if done { 245 //log.Print("done") 246 return nil 247 } 248 select { 249 case <-ctx.Done(): 250 return ctx.Err() 251 case <-p.t.closed.Done(): 252 return errTorrentClosed 253 case <-numVerifiesChanged: 254 } 255 } 256 } 257 258 func (p *Piece) queuedForHash() bool { 259 return p.t.piecesQueuedForHash.Contains(p.index) 260 } 261 262 func (p *Piece) torrentBeginOffset() int64 { 263 return int64(p.index) * p.t.info.PieceLength 264 } 265 266 func (p *Piece) torrentEndOffset() int64 { 267 return p.torrentBeginOffset() + int64(p.t.usualPieceSize()) 268 } 269 270 func (p *Piece) SetPriority(prio PiecePriority) { 271 p.t.cl.lock() 272 defer p.t.cl.unlock() 273 p.priority = prio 274 p.t.updatePiecePriority(p.index, "Piece.SetPriority") 275 } 276 277 // This is priority based only on piece, file and reader priorities. 278 func (p *Piece) purePriority() (ret PiecePriority) { 279 for _, f := range p.files() { 280 ret.Raise(f.prio) 281 } 282 if p.t.readerNowPieces().Contains(bitmap.BitIndex(p.index)) { 283 ret.Raise(PiecePriorityNow) 284 } 285 // if t._readerNowPieces.Contains(piece - 1) { 286 // return PiecePriorityNext 287 // } 288 if p.t.readerReadaheadPieces().Contains(bitmap.BitIndex(p.index)) { 289 ret.Raise(PiecePriorityReadahead) 290 } 291 ret.Raise(p.priority) 292 return 293 } 294 295 func (p *Piece) ignoreForRequests() bool { 296 // Ordered by cheapest checks and most likely to persist first. 297 298 // There's a method that gets this with complete, but that requires a bitmap lookup. Defer that. 299 if !p.storageCompletionOk { 300 // Piece completion unknown. 301 return true 302 } 303 if p.hashing || p.marking || !p.haveHash() || p.t.dataDownloadDisallowed.IsSet() { 304 return true 305 } 306 // This is valid after we know that storage completion has been cached. 307 if p.t.pieceComplete(p.index) { 308 return true 309 } 310 if p.queuedForHash() { 311 return true 312 } 313 return false 314 } 315 316 // This is the priority adjusted for piece state like completion, hashing etc. 317 func (p *Piece) effectivePriority() (ret PiecePriority) { 318 if p.ignoreForRequests() { 319 return PiecePriorityNone 320 } 321 return p.purePriority() 322 } 323 324 // Tells the Client to refetch the completion status from storage, updating priority etc. if 325 // necessary. Might be useful if you know the state of the piece data has 326 // changed externally. TODO: Document why this is public, maybe change name to 327 // SyncCompletion or something. 328 func (p *Piece) UpdateCompletion() { 329 p.t.cl.lock() 330 defer p.t.cl.unlock() 331 p.t.updatePieceCompletion(p.index) 332 } 333 334 // TODO: Probably don't include Completion.Err? 335 func (p *Piece) completion() (ret storage.Completion) { 336 ret.Ok = p.storageCompletionOk 337 if ret.Ok { 338 ret.Complete = p.t.pieceComplete(p.index) 339 } 340 return 341 } 342 343 func (p *Piece) allChunksDirty() bool { 344 return p.numDirtyChunks() == p.numChunks() 345 } 346 347 func (p *Piece) State() PieceState { 348 return p.t.PieceState(p.index) 349 } 350 351 // The first possible request index for the piece. 352 func (p *Piece) requestIndexBegin() RequestIndex { 353 return p.t.pieceRequestIndexBegin(p.index) 354 } 355 356 // The maximum end request index for the piece. Some of the requests might not be valid, it's for 357 // cleaning up arrays and bitmaps in broad strokes. 358 func (p *Piece) requestIndexMaxEnd() RequestIndex { 359 new := min(p.t.pieceRequestIndexBegin(p.index+1), p.t.maxEndRequest()) 360 if false { 361 old := p.t.pieceRequestIndexBegin(p.index + 1) 362 panicif.NotEq(new, old) 363 } 364 return new 365 } 366 367 func (p *Piece) availability() int { 368 return len(p.t.connsWithAllPieces) + p.relativeAvailability 369 } 370 371 // For v2 torrents, files are aligned to pieces so there should always only be a single file for a 372 // given piece. 373 func (p *Piece) mustGetOnlyFile() *File { 374 panicif.NotEq(p.numFiles(), 1) 375 return (*p.t.files)[p.beginFile] 376 } 377 378 // Sets the v2 piece hash, queuing initial piece checks if appropriate. 379 func (p *Piece) setV2Hash(v2h [32]byte) { 380 // See Torrent.onSetInfo. We want to trigger an initial check if appropriate, if we didn't yet 381 // have a piece hash (can occur with v2 when we don't start with piece layers). 382 p.t.storageLock.Lock() 383 oldV2Hash := p.hashV2.Set(v2h) 384 p.t.storageLock.Unlock() 385 if !oldV2Hash.Ok && p.hash == nil { 386 p.t.updatePieceCompletion(p.index) 387 p.t.queueInitialPieceCheck(p.index) 388 } 389 } 390 391 // Can't do certain things if we don't know the piece hash. 392 func (p *Piece) haveHash() bool { 393 if p.hash != nil { 394 return true 395 } 396 if !p.hasPieceLayer() { 397 return true 398 } 399 return p.hashV2.Ok 400 } 401 402 func (p *Piece) hasPieceLayer() bool { 403 return p.numFiles() == 1 && p.mustGetOnlyFile().length > p.t.info.PieceLength 404 } 405 406 // TODO: This looks inefficient. It will rehash everytime it is called. The hashes should be 407 // generated once. 408 func (p *Piece) obtainHashV2() (hash [32]byte, err error) { 409 if p.hashV2.Ok { 410 hash = p.hashV2.Value 411 return 412 } 413 if !p.hasPieceLayer() { 414 hash = p.mustGetOnlyFile().piecesRoot.Unwrap() 415 return 416 } 417 storage := p.Storage() 418 if c := storage.Completion(); c.Ok && !c.Complete { 419 err = errors.New("piece incomplete") 420 return 421 } 422 423 h := merkle.NewHash() 424 if _, err = storage.WriteTo(h); err != nil { 425 return 426 } 427 h.SumMinLength(hash[:0], int(p.t.info.PieceLength)) 428 return 429 } 430 431 func (p *Piece) files() iter.Seq2[int, *File] { 432 return func(yield func(int, *File) bool) { 433 for i := p.beginFile; i < p.endFile; i++ { 434 if !yield(i, (*p.t.files)[i]) { 435 return 436 } 437 } 438 } 439 } 440 441 func (p *Piece) numFiles() int { 442 return p.endFile - p.beginFile 443 } 444 445 func (p *Piece) hasActivePeerConnRequests() (ret bool) { 446 for ri := p.requestIndexBegin(); ri < p.requestIndexMaxEnd(); ri++ { 447 _, ok := p.t.requestState[ri] 448 ret = ret || ok 449 } 450 return 451 } 452 453 // The value of numVerifies after the next hashing operation that hasn't yet begun. 454 func (p *Piece) nextNovelHashCount() (ret pieceVerifyCount) { 455 ret = p.numVerifies + 1 456 if p.hashing { 457 // The next novel hash will be the one after the current one. 458 ret++ 459 } 460 return 461 } 462 463 // Here so it's zero-arity and we can use it in DeferOnce. 464 func (p *Piece) publishStateChange() { 465 t := p.t 466 cur := t.pieceState(p.index) 467 if cur != p.publicPieceState { 468 p.publicPieceState = cur 469 t.pieceStateChanges.Publish(PieceStateChange{ 470 p.index, 471 cur, 472 }) 473 } 474 } 475 476 func (p *Piece) fileExtents(offsetIntoPiece int64) iter.Seq2[int, segments.Extent] { 477 return p.t.fileSegmentsIndex.Unwrap().LocateIter(segments.Extent{ 478 p.torrentBeginOffset() + offsetIntoPiece, 479 int64(p.length()) - offsetIntoPiece, 480 }) 481 }