From 43891309ada546674c30923a461ae18c94d4307e Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Tue, 27 Oct 2020 14:10:17 +1100 Subject: [PATCH] Return errors from Reader if data downloading won't occur Chunk write errors to storage can disable data download. Previously Readers would wait indefinitely for the data to become available. This change returns an error instead of stalling. --- reader.go | 4 ++++ test/issue377_test.go | 24 +++++++++++++++++++++++- torrent.go | 3 +++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/reader.go b/reader.go index 1f68fdbe..1e1c36e1 100644 --- a/reader.go +++ b/reader.go @@ -199,6 +199,10 @@ func (r *reader) waitAvailable(pos, wanted int64, ctxErr *error, wait bool) (ava err = *ctxErr return } + if r.t.dataDownloadDisallowed || !r.t.networkingEnabled { + err = errors.New("downloading disabled and data not already available") + return + } if !wait { return } diff --git a/test/issue377_test.go b/test/issue377_test.go index abc57a90..05ef1429 100644 --- a/test/issue377_test.go +++ b/test/issue377_test.go @@ -2,6 +2,8 @@ package test import ( "errors" + "io" + "io/ioutil" "os" "sync" "testing" @@ -73,7 +75,27 @@ func testReceiveChunkStorageFailure(t *testing.T, seederFast bool) { // Tell the seeder to find the leecher. Is it guaranteed seeders will always try to do this? seederTorrent.AddClientPeer(leecherClient) <-leecherTorrent.GotInfo() - assertReadAllGreeting(t, leecherTorrent.NewReader()) + r := leecherTorrent.Files()[0].NewReader() + defer r.Close() + // We can't use assertReadAllGreeting here, because the default storage write error handler + // disables data downloads, which now causes Readers to error when they're blocked. + if false { + assertReadAllGreeting(t, leecherTorrent.NewReader()) + } else { + for func() bool { + // We don't seem to need to seek, but that's probably just because the storage failure is + // happening on the first read. + r.Seek(0, io.SeekStart) + output, err := ioutil.ReadAll(r) + if err != nil { + t.Logf("got error while reading: %v", err) + return true + } + assert.EqualValues(t, testutil.GreetingFileContents, output) + return false + }() { + } + } // TODO: Check that PeerConns fastEnabled matches seederFast? //select {} } diff --git a/torrent.go b/torrent.go index ace386eb..561d3d60 100644 --- a/torrent.go +++ b/torrent.go @@ -2024,6 +2024,7 @@ func (t *Torrent) onWriteChunkErr(err error) { go t.userOnWriteChunkErr(err) return } + t.logger.WithDefaultLevel(log.Critical).Printf("default chunk write error handler: disabling data download") t.disallowDataDownloadLocked() } @@ -2038,12 +2039,14 @@ func (t *Torrent) disallowDataDownloadLocked() { t.iterPeers(func(c *peer) { c.updateRequests() }) + t.tickleReaders() } func (t *Torrent) AllowDataDownload() { t.cl.lock() defer t.cl.unlock() t.dataDownloadDisallowed = false + t.tickleReaders() t.iterPeers(func(c *peer) { c.updateRequests() })