From 6e3fd9a9e5fa594dbd2c4312cce11a2ca1e55e4e Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Wed, 7 May 2025 10:20:05 +1000 Subject: [PATCH] Remove extra Torrent closed context goroutine for trackers --- client.go | 4 +++- internal/testutil/greeting.go | 3 ++- internal/testutil/spec.go | 10 ++++++---- torrent.go | 9 +++++++-- torrent_test.go | 15 ++++----------- tracker_scraper.go | 13 +------------ 6 files changed, 23 insertions(+), 31 deletions(-) diff --git a/client.go b/client.go index 62f6aadf..121bab21 100644 --- a/client.go +++ b/client.go @@ -1346,7 +1346,8 @@ func (cl *Client) newTorrent(ih metainfo.Hash, specStorage storage.ClientImpl) ( }) } -// Return a Torrent ready for insertion into a Client. +// Return a Torrent ready for insertion into a Client. This is also the method to call to create +// Torrents for testing. func (cl *Client) newTorrentOpt(opts AddTorrentOpts) (t *Torrent) { var v1InfoHash g.Option[infohash.T] if !opts.InfoHash.IsZero() { @@ -1383,6 +1384,7 @@ func (cl *Client) newTorrentOpt(opts AddTorrentOpts) (t *Torrent) { webSeeds: make(map[string]*Peer), gotMetainfoC: make(chan struct{}), } + t.closedCtx, t.closedCtxCancel = context.WithCancel(context.Background()) var salt [8]byte rand.Read(salt[:]) t.smartBanCache.Hash = func(b []byte) uint64 { diff --git a/internal/testutil/greeting.go b/internal/testutil/greeting.go index 6544483c..dbdab554 100644 --- a/internal/testutil/greeting.go +++ b/internal/testutil/greeting.go @@ -33,7 +33,8 @@ func CreateDummyTorrentData(dirName string) string { } func GreetingMetaInfo() *metainfo.MetaInfo { - return Greeting.Metainfo(5) + mi, _ := Greeting.Generate(5) + return &mi } // Gives a temporary directory containing the completed "greeting" torrent, diff --git a/internal/testutil/spec.go b/internal/testutil/spec.go index ad0e4074..c8727aa1 100644 --- a/internal/testutil/spec.go +++ b/internal/testutil/spec.go @@ -15,6 +15,7 @@ type File struct { Data string } +// High-level description of a torrent for testing purposes. type Torrent struct { Files []File Name string @@ -58,10 +59,11 @@ func (t *Torrent) Info(pieceLength int64) metainfo.Info { return info } -func (t *Torrent) Metainfo(pieceLength int64) *metainfo.MetaInfo { - mi := metainfo.MetaInfo{} +// Create an info and metainfo with bytes set for the torrent with the provided piece length. +func (t *Torrent) Generate(pieceLength int64) (mi metainfo.MetaInfo, info metainfo.Info) { var err error - mi.InfoBytes, err = bencode.Marshal(t.Info(pieceLength)) + info = t.Info(pieceLength) + mi.InfoBytes, err = bencode.Marshal(info) expect.Nil(err) - return &mi + return } diff --git a/torrent.go b/torrent.go index 93e5b868..b8ac0248 100644 --- a/torrent.go +++ b/torrent.go @@ -72,8 +72,12 @@ type Torrent struct { dataUploadDisallowed bool userOnWriteChunkErr func(error) - closed chansync.SetOnce - onClose []func() + closed chansync.SetOnce + // A background Context cancelled when the Torrent is closed. Added to minimize extra goroutines + // in tracker handlers. + closedCtx context.Context + closedCtxCancel func() + onClose []func() infoHash g.Option[metainfo.Hash] infoHashV2 g.Option[infohash_v2.T] @@ -1031,6 +1035,7 @@ func (t *Torrent) close(wg *sync.WaitGroup) (err error) { err = errors.New("already closed") return } + t.closedCtxCancel() for _, f := range t.onClose { f() } diff --git a/torrent_test.go b/torrent_test.go index 663716df..2b3a087d 100644 --- a/torrent_test.go +++ b/torrent_test.go @@ -11,7 +11,6 @@ import ( "testing" g "github.com/anacrolix/generics" - "github.com/anacrolix/log" "github.com/anacrolix/missinggo/v2" "github.com/anacrolix/missinggo/v2/bitmap" qt "github.com/frankban/quicktest" @@ -226,25 +225,19 @@ func TestTorrentMetainfoIncompleteMetadata(t *testing.T) { func TestRelativeAvailabilityHaveNone(t *testing.T) { c := qt.New(t) var err error - cl := Client{ - config: TestingConfig(t), - } - tt := Torrent{ - cl: &cl, - logger: log.Default, - gotMetainfoC: make(chan struct{}), - } + cl := newTestingClient(t) + mi, info := testutil.Greeting.Generate(5) + tt := cl.newTorrentOpt(AddTorrentOpts{InfoHash: mi.HashInfoBytes()}) tt.setChunkSize(2) g.MakeMapIfNil(&tt.conns) pc := PeerConn{} - pc.t = &tt + pc.t = tt pc.legacyPeerImpl = &pc pc.initRequestState() g.InitNew(&pc.callbacks) tt.conns[&pc] = struct{}{} err = pc.peerSentHave(0) c.Assert(err, qt.IsNil) - info := testutil.Greeting.Info(5) err = tt.setInfo(&info) c.Assert(err, qt.IsNil) tt.onSetInfo() diff --git a/tracker_scraper.go b/tracker_scraper.go index 10fc87b2..b8d72e98 100644 --- a/tracker_scraper.go +++ b/tracker_scraper.go @@ -219,22 +219,11 @@ func (me *trackerScraper) Stop() { func (me *trackerScraper) Run() { defer me.announceStopped() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - // TODO: Get rid of the need for this. - go func() { - defer cancel() - select { - case <-ctx.Done(): - case <-me.t.Closed(): - } - }() - // make sure first announce is a "started" e := tracker.Started for { - ar := me.announce(ctx, e) + ar := me.announce(me.t.closedCtx, e) // after first announce, get back to regular "none" e = tracker.None me.t.cl.lock() -- 2.51.0