X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=t.go;h=45856cc7acbeaa5988074f74435d078a49111a57;hb=HEAD;hp=cc69d8e3c7ac9c04c756af316bd81c81fc1c0d08;hpb=846da661031adb76eaf4cacde7ae33cd042a358f;p=btrtrc.git diff --git a/t.go b/t.go index cc69d8e3..45856cc7 100644 --- a/t.go +++ b/t.go @@ -1,71 +1,93 @@ package torrent import ( + "strconv" "strings" - "github.com/anacrolix/missinggo/pubsub" + "github.com/anacrolix/chansync/events" + "github.com/anacrolix/missinggo/v2/pubsub" + "github.com/anacrolix/sync" "github.com/anacrolix/torrent/metainfo" ) -// The torrent's infohash. This is fixed and cannot change. It uniquely -// identifies a torrent. +// The Torrent's infohash. This is fixed and cannot change. It uniquely identifies a torrent. func (t *Torrent) InfoHash() metainfo.Hash { return t.infoHash } -// Returns a channel that is closed when the info (.Info()) for the torrent -// has become available. -func (t *Torrent) GotInfo() <-chan struct{} { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - return t.gotMetainfo.C() +// Returns a channel that is closed when the info (.Info()) for the torrent has become available. +func (t *Torrent) GotInfo() events.Done { + return t.gotMetainfoC } // Returns the metainfo info dictionary, or nil if it's not yet available. -func (t *Torrent) Info() *metainfo.Info { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - return t.info -} - -// Returns a Reader bound to the torrent's data. All read calls block until -// the data requested is actually available. -func (t *Torrent) NewReader() (ret *Reader) { - ret = &Reader{ - mu: &t.cl.mu, - t: t, - readahead: 5 * 1024 * 1024, +func (t *Torrent) Info() (info *metainfo.Info) { + t.nameMu.RLock() + info = t.info + t.nameMu.RUnlock() + return +} + +// Returns a Reader bound to the torrent's data. All read calls block until the data requested is +// actually available. Note that you probably want to ensure the Torrent Info is available first. +func (t *Torrent) NewReader() Reader { + return t.newReader(0, t.length()) +} + +func (t *Torrent) newReader(offset, length int64) Reader { + r := reader{ + mu: t.cl.locker(), + t: t, + offset: offset, + length: length, + } + r.readaheadFunc = defaultReadaheadFunc + t.addReader(&r) + return &r +} + +type PieceStateRuns []PieceStateRun + +func (me PieceStateRuns) String() (s string) { + if len(me) > 0 { + var sb strings.Builder + sb.WriteString(me[0].String()) + for i := 1; i < len(me); i += 1 { + sb.WriteByte(' ') + sb.WriteString(me[i].String()) + } + return sb.String() } - t.addReader(ret) return } -// Returns the state of pieces of the torrent. They are grouped into runs of -// same state. The sum of the state run lengths is the number of pieces -// in the torrent. -func (t *Torrent) PieceStateRuns() []PieceStateRun { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - return t.pieceStateRuns() +// Returns the state of pieces of the torrent. They are grouped into runs of same state. The sum of +// the state run-lengths is the number of pieces in the torrent. +func (t *Torrent) PieceStateRuns() (runs PieceStateRuns) { + t.cl.rLock() + runs = t.pieceStateRuns() + t.cl.rUnlock() + return } -func (t *Torrent) PieceState(piece int) PieceState { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - return t.pieceState(piece) +func (t *Torrent) PieceState(piece pieceIndex) (ps PieceState) { + t.cl.rLock() + ps = t.pieceState(piece) + t.cl.rUnlock() + return } // The number of pieces in the torrent. This requires that the info has been // obtained first. -func (t *Torrent) NumPieces() int { +func (t *Torrent) NumPieces() pieceIndex { return t.numPieces() } // Get missing bytes count for specific piece. func (t *Torrent) PieceBytesMissing(piece int) int64 { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() + t.cl.rLock() + defer t.cl.rUnlock() return int64(t.pieces[piece].bytesLeft()) } @@ -74,144 +96,181 @@ func (t *Torrent) PieceBytesMissing(piece int) int64 { // this. No data corruption can, or should occur to either the torrent's data, // or connected peers. func (t *Torrent) Drop() { - t.cl.mu.Lock() - t.cl.dropTorrent(t.infoHash) - t.cl.mu.Unlock() + var wg sync.WaitGroup + defer wg.Wait() + t.cl.lock() + defer t.cl.unlock() + err := t.cl.dropTorrent(t.infoHash, &wg) + if err != nil { + panic(err) + } } -// Number of bytes of the entire torrent we have completed. +// Number of bytes of the entire torrent we have completed. This is the sum of +// completed pieces, and dirtied chunks of incomplete pieces. Do not use this +// for download rate, as it can go down when pieces are lost or fail checks. +// Sample Torrent.Stats.DataBytesRead for actual file data download rate. func (t *Torrent) BytesCompleted() int64 { - t.cl.mu.RLock() - defer t.cl.mu.RUnlock() + t.cl.rLock() + defer t.cl.rUnlock() return t.bytesCompleted() } // The subscription emits as (int) the index of pieces as their state changes. // A state change is when the PieceState for a piece alters in value. -func (t *Torrent) SubscribePieceStateChanges() *pubsub.Subscription { +func (t *Torrent) SubscribePieceStateChanges() *pubsub.Subscription[PieceStateChange] { return t.pieceStateChanges.Subscribe() } // Returns true if the torrent is currently being seeded. This occurs when the // client is willing to upload without wanting anything in return. -func (t *Torrent) Seeding() bool { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - return t.seeding() +func (t *Torrent) Seeding() (ret bool) { + t.cl.rLock() + ret = t.seeding() + t.cl.rUnlock() + return } -// Clobbers the torrent display name. The display name is used as the torrent -// name if the metainfo is not available. +// Clobbers the torrent display name if metainfo is unavailable. +// The display name is used as the torrent name while the metainfo is unavailable. func (t *Torrent) SetDisplayName(dn string) { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - t.setDisplayName(dn) + t.nameMu.Lock() + if !t.haveInfo() { + t.displayName = dn + } + t.nameMu.Unlock() } // The current working name for the torrent. Either the name in the info dict, // or a display name given such as by the dn value in a magnet link, or "". func (t *Torrent) Name() string { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() return t.name() } // The completed length of all the torrent data, in all its files. This is // derived from the torrent info, when it is available. func (t *Torrent) Length() int64 { - if t.info == nil { - panic("not valid until info obtained") - } - return t.length + return t._length.Value } // Returns a run-time generated metainfo for the torrent that includes the // info bytes and announce-list as currently known to the client. func (t *Torrent) Metainfo() metainfo.MetaInfo { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() + t.cl.rLock() + defer t.cl.rUnlock() return t.newMetaInfo() } -func (t *Torrent) addReader(r *Reader) { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() +func (t *Torrent) addReader(r *reader) { + t.cl.lock() + defer t.cl.unlock() if t.readers == nil { - t.readers = make(map[*Reader]struct{}) + t.readers = make(map[*reader]struct{}) } t.readers[r] = struct{}{} r.posChanged() } -func (t *Torrent) deleteReader(r *Reader) { +func (t *Torrent) deleteReader(r *reader) { delete(t.readers, r) t.readersChanged() } -func (t *Torrent) DownloadPieces(begin, end int) { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - t.pendPieceRange(begin, end) +// Raise the priorities of pieces in the range [begin, end) to at least Normal +// priority. Piece indexes are not the same as bytes. Requires that the info +// has been obtained, see Torrent.Info and Torrent.GotInfo. +func (t *Torrent) DownloadPieces(begin, end pieceIndex) { + t.cl.lock() + t.downloadPiecesLocked(begin, end) + t.cl.unlock() } -func (t *Torrent) CancelPieces(begin, end int) { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - t.unpendPieceRange(begin, end) +func (t *Torrent) downloadPiecesLocked(begin, end pieceIndex) { + for i := begin; i < end; i++ { + if t.pieces[i].priority.Raise(PiecePriorityNormal) { + t.updatePiecePriority(i, "Torrent.DownloadPieces") + } + } } -// Returns handles to the files in the torrent. This requires the metainfo is -// available first. -func (t *Torrent) Files() (ret []File) { - info := t.Info() - if info == nil { - return +func (t *Torrent) CancelPieces(begin, end pieceIndex) { + t.cl.lock() + t.cancelPiecesLocked(begin, end, "Torrent.CancelPieces") + t.cl.unlock() +} + +func (t *Torrent) cancelPiecesLocked(begin, end pieceIndex, reason string) { + for i := begin; i < end; i++ { + p := &t.pieces[i] + if p.priority == PiecePriorityNone { + continue + } + p.priority = PiecePriorityNone + t.updatePiecePriority(i, reason) } +} + +func (t *Torrent) initFiles() { var offset int64 - for _, fi := range info.UpvertedFiles() { - ret = append(ret, File{ + t.files = new([]*File) + for _, fi := range t.info.UpvertedFiles() { + *t.files = append(*t.files, &File{ t, - strings.Join(append([]string{info.Name}, fi.Path...), "/"), + strings.Join(append([]string{t.info.BestName()}, fi.BestPath()...), "/"), offset, fi.Length, fi, + fi.DisplayPath(t.info), + PiecePriorityNone, }) offset += fi.Length } - return } -func (t *Torrent) AddPeers(pp []Peer) { - cl := t.cl - cl.mu.Lock() - defer cl.mu.Unlock() - t.addPeers(pp) +// Returns handles to the files in the torrent. This requires that the Info is +// available first. +func (t *Torrent) Files() []*File { + return *t.files +} + +func (t *Torrent) AddPeers(pp []PeerInfo) (n int) { + t.cl.lock() + defer t.cl.unlock() + n = t.addPeers(pp) + return } // Marks the entire torrent for download. Requires the info first, see -// GotInfo. +// GotInfo. Sets piece priorities for historical reasons. func (t *Torrent) DownloadAll() { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - t.pendPieceRange(0, t.numPieces()) + t.DownloadPieces(0, t.numPieces()) } func (t *Torrent) String() string { s := t.name() if s == "" { - s = t.infoHash.HexString() + return t.infoHash.HexString() + } else { + return strconv.Quote(s) } - return s } func (t *Torrent) AddTrackers(announceList [][]string) { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() + t.cl.lock() + defer t.cl.unlock() t.addTrackers(announceList) } -func (t *Torrent) Piece(i int) *Piece { - t.cl.mu.Lock() - defer t.cl.mu.Unlock() - return &t.pieces[i] +func (t *Torrent) Piece(i pieceIndex) *Piece { + return t.piece(i) +} + +func (t *Torrent) PeerConns() []*PeerConn { + t.cl.rLock() + defer t.cl.rUnlock() + ret := make([]*PeerConn, 0, len(t.conns)) + for c := range t.conns { + ret = append(ret, c) + } + return ret }