From dc39e98cfb65489e332fb008e7a53c5e9ae77d5b Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Mon, 20 Jan 2020 11:51:24 +1100 Subject: [PATCH] Fix race in requestStrategyThree request timeout callback --- client.go | 2 +- connection.go | 2 ++ request_strategy.go | 12 +++++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/client.go b/client.go index 620b4b62..0990c6e0 100644 --- a/client.go +++ b/client.go @@ -1043,7 +1043,7 @@ func (cl *Client) newTorrent(ih metainfo.Hash, specStorage storage.ClientImpl) ( L: cl.locker(), }, } - t.requestStrategy = cl.config.DefaultRequestStrategy(t.requestStrategyCallbacks()) + t.requestStrategy = cl.config.DefaultRequestStrategy(t.requestStrategyCallbacks(), &cl._mu) t.logger = cl.logger.WithValues(t).WithText(func(m log.Msg) string { return fmt.Sprintf("%v: %s", t, m.Text()) }) diff --git a/connection.go b/connection.go index 0c25e343..4c106cca 100644 --- a/connection.go +++ b/connection.go @@ -545,7 +545,9 @@ func (cn *connection) request(r request, mw messageWriter) bool { func (rs requestStrategyThree) onSentRequest(r request) { rs.lastRequested[r] = time.AfterFunc(rs.duplicateRequestTimeout, func() { + rs.timeoutLocker.Lock() delete(rs.lastRequested, r) + rs.timeoutLocker.Unlock() rs.callbacks.requestTimedOut(r) }) } diff --git a/request_strategy.go b/request_strategy.go index e2e2c128..bcf6ac63 100644 --- a/request_strategy.go +++ b/request_strategy.go @@ -1,6 +1,7 @@ package torrent import ( + "sync" "time" "github.com/anacrolix/missinggo/v2/bitmap" @@ -79,20 +80,25 @@ func (requestStrategyTwo) ShouldRequestWithoutBias(cn requestStrategyConnection) type requestStrategyThree struct { // How long to avoid duplicating a pending request. duplicateRequestTimeout time.Duration + + callbacks requestStrategyCallbacks + // The last time we requested a chunk. Deleting the request from any connection will clear this // value. lastRequested map[request]*time.Timer - callbacks requestStrategyCallbacks + // The lock to take when running a request timeout handler. + timeoutLocker sync.Locker } -type requestStrategyMaker func(callbacks requestStrategyCallbacks) requestStrategy +type requestStrategyMaker func(callbacks requestStrategyCallbacks, clientLocker sync.Locker) requestStrategy func requestStrategyThreeMaker(duplicateRequestTimeout time.Duration) requestStrategyMaker { - return func(callbacks requestStrategyCallbacks) requestStrategy { + return func(callbacks requestStrategyCallbacks, clientLocker sync.Locker) requestStrategy { return requestStrategyThree{ duplicateRequestTimeout: duplicateRequestTimeout, callbacks: callbacks, lastRequested: make(map[request]*time.Timer), + timeoutLocker: clientLocker, } } } -- 2.48.1