From 0bd7f5b7a93320178cbe863d102dba70d60190a6 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Mon, 8 Jul 2024 10:50:33 +0200 Subject: [PATCH 01/22] add piece buf printers --- peer.go | 1 + torrent.go | 4 ++++ webseed-peer.go | 2 +- webseed/client.go | 16 ++++++++++++---- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/peer.go b/peer.go index 29bf8df8e0..4050caa032 100644 --- a/peer.go +++ b/peer.go @@ -764,6 +764,7 @@ func (c *Peer) receiveChunk(msg *pp.Message) error { req := c.t.requestIndexFromRequest(ppReq, true) recordBlockForSmartBan := sync.OnceFunc(func() { + fmt.Println("RCNK", c.t.Name(), msg.Index, len(msg.Piece), c.t.smartBanCache.Hash(msg.Piece)) c.recordBlockForSmartBan(req, msg.Piece) }) // This needs to occur before we return, but we try to do it when the client is unlocked. It diff --git a/torrent.go b/torrent.go index 0e03c218c2..5a84ffc427 100644 --- a/torrent.go +++ b/torrent.go @@ -2826,6 +2826,10 @@ func (t *Torrent) tryCreatePieceHasher(lock bool) bool { log.Fmsg("piece %v (%s) hash failure copy error: %v", p, p.hash.HexString(), copyErr).Log(t.logger) } + buf := bytes.NewBuffer(nil) + _, _ = p.Storage().WriteTo(buf) + fmt.Println("HSH", t.Name(), p.index, p.length, t.smartBanCache.Hash(buf.Bytes())) + storageLock.RUnlock() p.mu.Lock() diff --git a/webseed-peer.go b/webseed-peer.go index b95a33f20d..897cac8055 100644 --- a/webseed-peer.go +++ b/webseed-peer.go @@ -156,7 +156,7 @@ func (cn *webseedPeer) nominalMaxRequests(lock bool, lockTorrent bool) maxReques var limitedBuffPool = storage.NewLimitedBufferPool(bufPool, 5_000_000_000) func (ws *webseedPeer) doRequest(r Request) error { - webseedRequest := ws.client.NewRequest(ws.intoSpec(r), limitedBuffPool, ws.requestRateLimiter, &ws.receiving) + webseedRequest := ws.client.NewRequest(ws.intoSpec(r), limitedBuffPool, ws.requestRateLimiter, &ws.receiving, ws.peer.t.smartBanCache.Hash) ws.peer.mu.Lock() ws.activeRequests[r] = webseedRequest diff --git a/webseed/client.go b/webseed/client.go index c6b027c1dd..8746301b70 100644 --- a/webseed/client.go +++ b/webseed/client.go @@ -1,6 +1,7 @@ package webseed import ( + "bytes" "context" "errors" "fmt" @@ -27,6 +28,7 @@ type requestPart struct { do func() (*http.Response, io.ReadWriteCloser, error) // Wrap http response bodies for such things as download rate limiting. responseBodyWrapper ResponseBodyWrapper + hasher func(b []byte) uint64 // temp for testing } type Request struct { @@ -72,7 +74,7 @@ type RequestResult struct { Err error } -func (ws *Client) NewRequest(r RequestSpec, buffers storage.BufferPool, limiter *rate.Limiter, receivingCounter *atomic.Int64) Request { +func (ws *Client) NewRequest(r RequestSpec, buffers storage.BufferPool, limiter *rate.Limiter, receivingCounter *atomic.Int64, hasher func(b []byte) uint64) Request { ctx, cancel := context.WithCancel(context.Background()) var requestParts []requestPart if !ws.fileIndex.Locate(r, func(i int, e segments.Extent) bool { @@ -110,6 +112,7 @@ func (ws *Client) NewRequest(r RequestSpec, buffers storage.BufferPool, limiter return response, buff, err } + part.hasher = hasher requestParts = append(requestParts, part) return true }) { @@ -148,7 +151,7 @@ func (me ErrBadResponse) Error() string { return me.Msg } -func recvPartResult(ctx context.Context, buf io.Writer, part requestPart, resp *http.Response) error { +func recvPartResult(ctx context.Context, writer io.Writer, part requestPart, resp *http.Response) error { defer resp.Body.Close() var body io.Reader = resp.Body if part.responseBodyWrapper != nil { @@ -161,7 +164,12 @@ func recvPartResult(ctx context.Context, buf io.Writer, part requestPart, resp * } switch resp.StatusCode { case http.StatusPartialContent: - copied, err := io.Copy(buf, body) + buf, err := io.ReadAll(body) + if err != nil { + return err + } + fmt.Println("RECV", part.req.URL, part.e.Start/part.e.Length, len(buf), part.hasher(buf)) + copied, err := io.Copy(writer, bytes.NewBuffer(buf)) if err != nil { return err } @@ -189,7 +197,7 @@ func recvPartResult(ctx context.Context, buf io.Writer, part requestPart, resp * if discarded != 0 { log.Printf("discarded %v bytes in webseed request response part", discarded) } - _, err := io.CopyN(buf, body, part.e.Length) + _, err := io.CopyN(writer, body, part.e.Length) return err } else { return ErrBadResponse{"resp status ok but requested range", resp} From fd900d42788c4c28b127c21f57d0043c6bf576df Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Mon, 8 Jul 2024 11:28:32 +0200 Subject: [PATCH 02/22] add piece buf printers --- torrent.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/torrent.go b/torrent.go index 5a84ffc427..866be326f6 100644 --- a/torrent.go +++ b/torrent.go @@ -2689,6 +2689,7 @@ func (t *Torrent) pieceHashed(piece pieceIndex, passed bool, hashIoErr error) { // single peer for a piece, and we never progress that piece to completion, we // will never smart-ban them. Discovered in // https://github.com/anacrolix/torrent/issues/715. + fmt.Printf("banning %v for being sole dirtier of piece %v after failed piece check", c, piece) t.logger.Levelf(log.Warning, "banning %v for being sole dirtier of piece %v after failed piece check", c, piece) c.ban() } @@ -2828,7 +2829,7 @@ func (t *Torrent) tryCreatePieceHasher(lock bool) bool { buf := bytes.NewBuffer(nil) _, _ = p.Storage().WriteTo(buf) - fmt.Println("HSH", t.Name(), p.index, p.length, t.smartBanCache.Hash(buf.Bytes())) + fmt.Println("HSH", t.Name(), p.index, p.length(true), t.smartBanCache.Hash(buf.Bytes()), correct, sum, *p.hash) storageLock.RUnlock() From b61c24cef76311bafc0382b81edcc5361e8bc037 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Mon, 8 Jul 2024 12:06:37 +0200 Subject: [PATCH 03/22] add piece buf printers --- torrent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/torrent.go b/torrent.go index 866be326f6..124aab20a8 100644 --- a/torrent.go +++ b/torrent.go @@ -2689,7 +2689,7 @@ func (t *Torrent) pieceHashed(piece pieceIndex, passed bool, hashIoErr error) { // single peer for a piece, and we never progress that piece to completion, we // will never smart-ban them. Discovered in // https://github.com/anacrolix/torrent/issues/715. - fmt.Printf("banning %v for being sole dirtier of piece %v after failed piece check", c, piece) + fmt.Printf("banning %v for being sole dirtier of piece %v after failed piece check\n", c, piece) t.logger.Levelf(log.Warning, "banning %v for being sole dirtier of piece %v after failed piece check", c, piece) c.ban() } From 7215ba7f01fdd6a139c76ff8c19b71a46f20e037 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Mon, 8 Jul 2024 18:58:28 +0200 Subject: [PATCH 04/22] restore close on ban --- peer.go | 1 - torrent.go | 4 ---- webseed-peer.go | 8 +++++++- webseed/client.go | 12 ++---------- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/peer.go b/peer.go index 4050caa032..29bf8df8e0 100644 --- a/peer.go +++ b/peer.go @@ -764,7 +764,6 @@ func (c *Peer) receiveChunk(msg *pp.Message) error { req := c.t.requestIndexFromRequest(ppReq, true) recordBlockForSmartBan := sync.OnceFunc(func() { - fmt.Println("RCNK", c.t.Name(), msg.Index, len(msg.Piece), c.t.smartBanCache.Hash(msg.Piece)) c.recordBlockForSmartBan(req, msg.Piece) }) // This needs to occur before we return, but we try to do it when the client is unlocked. It diff --git a/torrent.go b/torrent.go index 124aab20a8..2f6eec3aa8 100644 --- a/torrent.go +++ b/torrent.go @@ -2827,10 +2827,6 @@ func (t *Torrent) tryCreatePieceHasher(lock bool) bool { log.Fmsg("piece %v (%s) hash failure copy error: %v", p, p.hash.HexString(), copyErr).Log(t.logger) } - buf := bytes.NewBuffer(nil) - _, _ = p.Storage().WriteTo(buf) - fmt.Println("HSH", t.Name(), p.index, p.length(true), t.smartBanCache.Hash(buf.Bytes()), correct, sum, *p.hash) - storageLock.RUnlock() p.mu.Lock() diff --git a/webseed-peer.go b/webseed-peer.go index 897cac8055..3c50d0ecb0 100644 --- a/webseed-peer.go +++ b/webseed-peer.go @@ -156,7 +156,7 @@ func (cn *webseedPeer) nominalMaxRequests(lock bool, lockTorrent bool) maxReques var limitedBuffPool = storage.NewLimitedBufferPool(bufPool, 5_000_000_000) func (ws *webseedPeer) doRequest(r Request) error { - webseedRequest := ws.client.NewRequest(ws.intoSpec(r), limitedBuffPool, ws.requestRateLimiter, &ws.receiving, ws.peer.t.smartBanCache.Hash) + webseedRequest := ws.client.NewRequest(ws.intoSpec(r), limitedBuffPool, ws.requestRateLimiter, &ws.receiving) ws.peer.mu.Lock() ws.activeRequests[r] = webseedRequest @@ -495,7 +495,13 @@ func (cn *webseedPeer) ban() { cn.peer.mu.Lock() cn.peer.banCount++ + banCount := cn.peer.banCount cn.peer.mu.Unlock() + + if banCount > 5 { + cn.peer.close(true) + fmt.Println("BCLS", cn.String(), cn.peer.t.numActivePeers(true)) + } } func (cn *webseedPeer) isLowOnRequests(lock bool, lockTorrent bool) bool { diff --git a/webseed/client.go b/webseed/client.go index 8746301b70..4e9781d6e3 100644 --- a/webseed/client.go +++ b/webseed/client.go @@ -1,7 +1,6 @@ package webseed import ( - "bytes" "context" "errors" "fmt" @@ -28,7 +27,6 @@ type requestPart struct { do func() (*http.Response, io.ReadWriteCloser, error) // Wrap http response bodies for such things as download rate limiting. responseBodyWrapper ResponseBodyWrapper - hasher func(b []byte) uint64 // temp for testing } type Request struct { @@ -74,7 +72,7 @@ type RequestResult struct { Err error } -func (ws *Client) NewRequest(r RequestSpec, buffers storage.BufferPool, limiter *rate.Limiter, receivingCounter *atomic.Int64, hasher func(b []byte) uint64) Request { +func (ws *Client) NewRequest(r RequestSpec, buffers storage.BufferPool, limiter *rate.Limiter, receivingCounter *atomic.Int64) Request { ctx, cancel := context.WithCancel(context.Background()) var requestParts []requestPart if !ws.fileIndex.Locate(r, func(i int, e segments.Extent) bool { @@ -112,7 +110,6 @@ func (ws *Client) NewRequest(r RequestSpec, buffers storage.BufferPool, limiter return response, buff, err } - part.hasher = hasher requestParts = append(requestParts, part) return true }) { @@ -164,12 +161,7 @@ func recvPartResult(ctx context.Context, writer io.Writer, part requestPart, res } switch resp.StatusCode { case http.StatusPartialContent: - buf, err := io.ReadAll(body) - if err != nil { - return err - } - fmt.Println("RECV", part.req.URL, part.e.Start/part.e.Length, len(buf), part.hasher(buf)) - copied, err := io.Copy(writer, bytes.NewBuffer(buf)) + copied, err := io.Copy(writer, body) if err != nil { return err } From d89315de5773599c7e29f10cbfac4c6bad8966b4 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Mon, 8 Jul 2024 19:15:28 +0200 Subject: [PATCH 05/22] add printer status --- webseed-peer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webseed-peer.go b/webseed-peer.go index 3c50d0ecb0..8f38973aa5 100644 --- a/webseed-peer.go +++ b/webseed-peer.go @@ -499,8 +499,9 @@ func (cn *webseedPeer) ban() { cn.peer.mu.Unlock() if banCount > 5 { + prev := cn.peer.t.numActivePeers(true) cn.peer.close(true) - fmt.Println("BCLS", cn.String(), cn.peer.t.numActivePeers(true)) + fmt.Println("BCLS", cn.String(), cn.peer.closed.IsSet(), "P", prev, "A", cn.peer.t.numActivePeers(true)) } } From a5bd06384820b6a9f08f45b18ece506497b09753 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Mon, 8 Jul 2024 19:18:43 +0200 Subject: [PATCH 06/22] check closed --- webseed-peer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webseed-peer.go b/webseed-peer.go index 8f38973aa5..6ed713c1a1 100644 --- a/webseed-peer.go +++ b/webseed-peer.go @@ -498,7 +498,7 @@ func (cn *webseedPeer) ban() { banCount := cn.peer.banCount cn.peer.mu.Unlock() - if banCount > 5 { + if banCount > 5 && !cn.peer.closed.IsSet() { prev := cn.peer.t.numActivePeers(true) cn.peer.close(true) fmt.Println("BCLS", cn.String(), cn.peer.closed.IsSet(), "P", prev, "A", cn.peer.t.numActivePeers(true)) From 6096c4ad058998c8dcacb213599e40d97a2a0a01 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Mon, 8 Jul 2024 19:27:28 +0200 Subject: [PATCH 07/22] add printer status --- webseed-peer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webseed-peer.go b/webseed-peer.go index 6ed713c1a1..a98e047cb1 100644 --- a/webseed-peer.go +++ b/webseed-peer.go @@ -499,9 +499,9 @@ func (cn *webseedPeer) ban() { cn.peer.mu.Unlock() if banCount > 5 && !cn.peer.closed.IsSet() { - prev := cn.peer.t.numActivePeers(true) + preva, prevc := cn.peer.t.numActivePeers(true), cn.peer.closed.IsSet() cn.peer.close(true) - fmt.Println("BCLS", cn.String(), cn.peer.closed.IsSet(), "P", prev, "A", cn.peer.t.numActivePeers(true)) + fmt.Println("BCLS", cn.String(), "P", prevc, preva, "A", cn.peer.closed.IsSet(), cn.peer.t.numActivePeers(true)) } } From a37cc0f3846aa2872c4802d37a45f3d5f301e0c8 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Tue, 9 Jul 2024 10:06:35 +0200 Subject: [PATCH 08/22] add lock detection --- deferrwl.go | 6 +--- go.mod | 3 ++ go.sum | 5 ++++ torrent.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 92 insertions(+), 6 deletions(-) diff --git a/deferrwl.go b/deferrwl.go index 2e682f3faf..d398ccc538 100644 --- a/deferrwl.go +++ b/deferrwl.go @@ -1,14 +1,10 @@ package torrent -import ( - "github.com/anacrolix/sync" -) - // Runs deferred actions on Unlock. Note that actions are assumed to be the results of changes that // would only occur with a write lock at present. The race detector should catch instances of defers // without the write lock being held. type lockWithDeferreds struct { - internal sync.RWMutex + internal mu //sync.RWMutex unlockActions []func() //lc atomic.Int32 diff --git a/go.mod b/go.mod index d517ecb213..671e3759cf 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/frankban/quicktest v1.14.6 github.com/fsnotify/fsnotify v1.5.4 github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 + github.com/go-stack/stack v1.8.0 github.com/google/btree v1.1.2 github.com/google/go-cmp v0.5.9 github.com/gorilla/websocket v1.5.0 @@ -42,6 +43,7 @@ require ( github.com/pion/webrtc/v3 v3.1.42 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.12.2 + github.com/sasha-s/go-deadlock v0.3.1 github.com/stretchr/testify v1.8.4 github.com/tidwall/btree v1.6.0 go.etcd.io/bbolt v1.3.6 @@ -77,6 +79,7 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mschoch/smat v0.2.0 // indirect + github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pion/dtls/v2 v2.2.4 // indirect github.com/pion/ice/v2 v2.2.6 // indirect github.com/pion/interceptor v0.1.11 // indirect diff --git a/go.sum b/go.sum index 98f331c120..b9bb92699b 100644 --- a/go.sum +++ b/go.sum @@ -218,6 +218,7 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -364,6 +365,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E= @@ -463,6 +466,8 @@ github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 h1:Lt9DzQALzHoDwMBGJ6v github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= diff --git a/torrent.go b/torrent.go index 2f6eec3aa8..af1890cb72 100644 --- a/torrent.go +++ b/torrent.go @@ -49,10 +49,92 @@ import ( typedRoaring "github.com/anacrolix/torrent/typed-roaring" "github.com/anacrolix/torrent/webseed" "github.com/anacrolix/torrent/webtorrent" + stack2 "github.com/go-stack/stack" + "github.com/sasha-s/go-deadlock" ) var bufPool = storage.NewBufferPool() +func stack(skip int) string { + return stack2.Trace().TrimBelow(stack2.Caller(skip)).String() +} + +func init() { + deadlock.Opts.DeadlockTimeout = 3 * time.Minute +} + +type mu struct { + deadlock.RWMutex + rlc atomic.Int32 + lc atomic.Int32 + rlmu sync.Mutex + rlocker [20]string + rlocktime time.Time + locker string + nextlocker string + locktime time.Time +} + +func (m *mu) RLock() { + m.RWMutex.RLock() + m.rlmu.Lock() + rlc := m.rlc.Load() + if int(rlc) < len(m.rlocker) { + m.rlocker[rlc] = string(stack(2)) + } + if rlc == 0 { + m.rlocktime = time.Now() + } + m.rlc.Add(1) + m.rlmu.Unlock() + //fmt.Println("R", m.rlc, string(dbg.Stack())[:40]) + +} + +func (m *mu) RUnlock() { + m.rlmu.Lock() + m.rlc.Add(-1) + rlc := m.rlc.Load() + if rlc < 0 { + panic("lock underflow") + } + if rlc == 0 { + m.rlocktime = time.Time{} + } + if int(rlc) < len(m.rlocker) { + m.rlocker[m.rlc.Load()] = "" + } + m.rlmu.Unlock() + m.RWMutex.RUnlock() + //fmt.Println("RUN", m.rlc) //, string(dbg.Stack())) +} + +func (m *mu) Lock() { + m.rlmu.Lock() + if m.nextlocker == "" { + m.nextlocker = string(stack(2)) + } + m.rlmu.Unlock() + m.RWMutex.Lock() + m.lc.Add(1) + m.rlmu.Lock() + m.locker = m.nextlocker + m.locktime = time.Now() + m.nextlocker = "" + m.rlmu.Unlock() +} + +func (m *mu) Unlock() { + m.lc.Add(-1) + if m.lc.Load() < 0 { + panic("lock underflow") + } + m.locker = "" + m.locktime = time.Time{} + m.RWMutex.Unlock() + //fmt.Println("LUN", m.lc) //, string(dbg.Stack())) +} + // Maintains state of torrent within a Client. Many methods should not be called before the info is // available, see .Info and .GotInfo. type Torrent struct { @@ -122,7 +204,7 @@ type Torrent struct { // How many times we've initiated a DHT announce. TODO: Move into stats. numDHTAnnounces int - mu sync.RWMutex + mu mu //sync.RWMutex imu sync.RWMutex // Name used if the info name isn't available. Should be cleared when the // Info does become available. From b654ccb0406ad9d135ce9dde25890274ef34032e Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Tue, 9 Jul 2024 11:10:01 +0200 Subject: [PATCH 09/22] fix torrent deadlock --- peerconn.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/peerconn.go b/peerconn.go index 145e907726..9cb7112688 100644 --- a/peerconn.go +++ b/peerconn.go @@ -674,6 +674,14 @@ func (c *PeerConn) maximumPeerRequestChunkLength() (_ Option[int]) { // startFetch is for testing purposes currently. func (c *PeerConn) onReadRequest(r Request, startFetch bool, lock bool) error { + if !c.t.havePiece(pieceIndex(r.Index), true) { + // TODO: Tell the peer we don't have the piece, and reject this request. + requestsReceivedForMissingPieces.Add(1) + return fmt.Errorf("peer requested piece we don't have: %v", r.Index.Int()) + } + + pieceLength := c.t.pieceLength(pieceIndex(r.Index), true) + if lock { c.mu.Lock() defer c.mu.Unlock() @@ -714,12 +722,6 @@ func (c *PeerConn) onReadRequest(r Request, startFetch bool, lock bool) error { return err } } - if !c.t.havePiece(pieceIndex(r.Index), true) { - // TODO: Tell the peer we don't have the piece, and reject this request. - requestsReceivedForMissingPieces.Add(1) - return fmt.Errorf("peer requested piece we don't have: %v", r.Index.Int()) - } - pieceLength := c.t.pieceLength(pieceIndex(r.Index), true) // Check this after we know we have the piece, so that the piece length will be known. if chunkOverflowsPiece(r.ChunkSpec, pieceLength) { torrent.Add("bad requests received", 1) From 34abe352d4cf218b63bcaa702e005a3e92dc4945 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Tue, 9 Jul 2024 19:12:10 +0200 Subject: [PATCH 10/22] add lock to request state data --- peerconn.go | 5 ++++- torrent.go | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/peerconn.go b/peerconn.go index 9cb7112688..70ad5c5e8e 100644 --- a/peerconn.go +++ b/peerconn.go @@ -759,7 +759,9 @@ func (c *PeerConn) peerRequestDataReader(r Request, prs *peerRequestState) { panic("data must be non-nil to trigger send") } torrent.Add("peer request data read successes", 1) + c.mu.Lock() prs.data = b + c.mu.Unlock() // This might be required for the error case too (#752 and #753). c.tickleWriter() } @@ -1213,7 +1215,8 @@ another: } peerRequests = make([]peerRequest, 0, len(c.peerRequests)) for r, state := range c.peerRequests { - peerRequests = append(peerRequests, peerRequest{r, state}) + s := *state + peerRequests = append(peerRequests, peerRequest{r, &s}) } }() diff --git a/torrent.go b/torrent.go index af1890cb72..266410f084 100644 --- a/torrent.go +++ b/torrent.go @@ -1789,6 +1789,9 @@ func (t *Torrent) maxHalfOpen(lock bool) int { func (t *Torrent) openNewConns(lock bool) (initiated int) { if lock { + if t.mu.lc.Load() > 0 || t.mu.rlc.Load() > 0 { + fmt.Println("ONC", "L", t.mu.locker, "R", t.mu.rlocker) + } t.mu.Lock() defer t.mu.Unlock() } From 41dd070ca65feadbae34b2d6fa787b68c81ddb0d Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 00:21:39 +0200 Subject: [PATCH 11/22] add printer status --- mmap_span/mmap_span.go | 4 ++-- torrent.go | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mmap_span/mmap_span.go b/mmap_span/mmap_span.go index 067c74e923..adbff4d481 100644 --- a/mmap_span/mmap_span.go +++ b/mmap_span/mmap_span.go @@ -154,8 +154,8 @@ func (ms *MMapSpan) locateCopy(copyArgs func(remainingArgument, mmapped []byte) func (ms *MMapSpan) WriteAt(index int, p []byte, off int64) (n int, err error) { // log.Printf("writing %v bytes at %v", len(p), off) n, err = func() (n int, err error) { - ms.mu.RLock() - defer ms.mu.RUnlock() + ms.mu.Lock() + defer ms.mu.Unlock() n = ms.locateCopy(func(a, b []byte) (_, _ []byte) { return b, a }, p, off) if n != len(p) { err = io.ErrShortWrite diff --git a/torrent.go b/torrent.go index 266410f084..3bb70827c3 100644 --- a/torrent.go +++ b/torrent.go @@ -1788,14 +1788,19 @@ func (t *Torrent) maxHalfOpen(lock bool) int { } func (t *Torrent) openNewConns(lock bool) (initiated int) { + lc := false if lock { if t.mu.lc.Load() > 0 || t.mu.rlc.Load() > 0 { + lc = true fmt.Println("ONC", "L", t.mu.locker, "R", t.mu.rlocker) } t.mu.Lock() defer t.mu.Unlock() } + if lc { + fmt.Println("ONC", "LOCKED", "L", t.mu.locker, "R", t.mu.rlocker) + } defer t.updateWantPeersEvent(false) for t.peers.Len() != 0 { From d596a63d6c4278c671900dca0401b3058486de03 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 09:52:16 +0200 Subject: [PATCH 12/22] add peer deadlock checker --- peer.go | 2 +- torrent.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/peer.go b/peer.go index 29bf8df8e0..5585d30a3d 100644 --- a/peer.go +++ b/peer.go @@ -28,7 +28,7 @@ type ( Peer struct { // First to ensure 64-bit alignment for atomics. See #262. _stats ConnStats - mu sync.RWMutex + mu mu //sync.RWMutex t *Torrent diff --git a/torrent.go b/torrent.go index 3bb70827c3..507214697b 100644 --- a/torrent.go +++ b/torrent.go @@ -1800,6 +1800,7 @@ func (t *Torrent) openNewConns(lock bool) (initiated int) { if lc { fmt.Println("ONC", "LOCKED", "L", t.mu.locker, "R", t.mu.rlocker) + defer fmt.Println("ONC", "DONE") } defer t.updateWantPeersEvent(false) From 2a7f20d8e4cd3603ae61096411b8635ecff6a978 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 10:27:50 +0200 Subject: [PATCH 13/22] remove lock detector --- deferrwl.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deferrwl.go b/deferrwl.go index d398ccc538..bddaaf8aa1 100644 --- a/deferrwl.go +++ b/deferrwl.go @@ -1,10 +1,12 @@ package torrent +import "github.com/anacrolix/sync" + // Runs deferred actions on Unlock. Note that actions are assumed to be the results of changes that // would only occur with a write lock at present. The race detector should catch instances of defers // without the write lock being held. type lockWithDeferreds struct { - internal mu //sync.RWMutex + internal sync.RWMutex unlockActions []func() //lc atomic.Int32 From ad9219cd5bad4ead7eeee8de444593dec9c3cb5e Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 13:10:04 +0200 Subject: [PATCH 14/22] re-introduce client lock --- deferrwl.go | 35 +---------------------------------- peer.go | 2 +- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/deferrwl.go b/deferrwl.go index bddaaf8aa1..48bb0b50d2 100644 --- a/deferrwl.go +++ b/deferrwl.go @@ -1,40 +1,18 @@ package torrent -import "github.com/anacrolix/sync" - // Runs deferred actions on Unlock. Note that actions are assumed to be the results of changes that // would only occur with a write lock at present. The race detector should catch instances of defers // without the write lock being held. type lockWithDeferreds struct { - internal sync.RWMutex + internal mu //sync.RWMutex unlockActions []func() - - //lc atomic.Int32 - //locker string - //locktime time.Time - //rlc atomic.Int32 - //rlmu sync.Mutex - //rlocker [20]string } -//func stack(skip int) string { -// return stack2.Trace().TrimBelow(stack2.Caller(skip)).String() -//} - func (me *lockWithDeferreds) Lock() { me.internal.Lock() - // me.lc.Add(1) - // me.locker = stack(2) - // me.locktime = time.Now() } func (me *lockWithDeferreds) Unlock() { - // me.lc.Add(-1) - // if me.lc.Load() < 0 { - // panic("lock underflow") - // } - // me.locker = "" - // me.locktime = time.Time{} unlockActions := me.unlockActions for i := 0; i < len(unlockActions); i += 1 { unlockActions[i]() @@ -45,20 +23,9 @@ func (me *lockWithDeferreds) Unlock() { func (me *lockWithDeferreds) RLock() { me.internal.RLock() - // me.rlmu.Lock() - // me.rlocker[me.rlc.Load()] = string(stack(2)) - // me.rlc.Add(1) - // me.rlmu.Unlock() } func (me *lockWithDeferreds) RUnlock() { - // me.rlmu.Lock() - // me.rlc.Add(-1) - // if me.rlc.Load() < 0 { - // panic("lock underflow") - // } - // me.rlocker[me.rlc.Load()] = "" - // me.rlmu.Unlock() me.internal.RUnlock() } diff --git a/peer.go b/peer.go index 5585d30a3d..29bf8df8e0 100644 --- a/peer.go +++ b/peer.go @@ -28,7 +28,7 @@ type ( Peer struct { // First to ensure 64-bit alignment for atomics. See #262. _stats ConnStats - mu mu //sync.RWMutex + mu sync.RWMutex t *Torrent From 99d65bf510cf5770cc439ef0aae690b91bf7b3d0 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 14:19:29 +0200 Subject: [PATCH 15/22] export status strings --- peer.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/peer.go b/peer.go index 29bf8df8e0..6bfda7958b 100644 --- a/peer.go +++ b/peer.go @@ -245,10 +245,16 @@ func eventAgeString(t time.Time) string { } // Inspired by https://github.com/transmission/transmission/wiki/Peer-Status-Text. -func (cn *Peer) statusFlags() (ret string) { +func (cn *Peer) StatusFlags() (ret string) { c := func(b byte) { ret += string([]byte{b}) } + if cn.closed.IsSet() { + ret = "op-" + } else { + ret = "cl-" + } + if cn.requestState.Interested { c('i') } @@ -354,7 +360,7 @@ func (cn *Peer) writeStatus(w io.Writer, lock bool, lockTorrent bool) { cn.PeerMaxRequests, len(cn.peerRequests), localClientReqq, - cn.statusFlags(), + cn.StatusFlags(), cn.downloadRate()/(1<<10), ) fmt.Fprintf(w, "requested pieces:") From 1453b96cd4fbdb6b09866681f1d2d26e87e360d8 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 15:24:11 +0200 Subject: [PATCH 16/22] track peer lock not client --- deferrwl.go | 4 +++- peer.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deferrwl.go b/deferrwl.go index 48bb0b50d2..bf95be244f 100644 --- a/deferrwl.go +++ b/deferrwl.go @@ -1,10 +1,12 @@ package torrent +import "github.com/anacrolix/sync" + // Runs deferred actions on Unlock. Note that actions are assumed to be the results of changes that // would only occur with a write lock at present. The race detector should catch instances of defers // without the write lock being held. type lockWithDeferreds struct { - internal mu //sync.RWMutex + internal sync.RWMutex unlockActions []func() } diff --git a/peer.go b/peer.go index 6bfda7958b..da353d7ce6 100644 --- a/peer.go +++ b/peer.go @@ -28,7 +28,7 @@ type ( Peer struct { // First to ensure 64-bit alignment for atomics. See #262. _stats ConnStats - mu sync.RWMutex + mu mu //sync.RWMutex t *Torrent From 10f1ce2b375a2f054119402c8e2dfb35ab3e1e29 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 15:31:19 +0200 Subject: [PATCH 17/22] fix flags --- peer.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/peer.go b/peer.go index da353d7ce6..5033a9f149 100644 --- a/peer.go +++ b/peer.go @@ -250,9 +250,9 @@ func (cn *Peer) StatusFlags() (ret string) { ret += string([]byte{b}) } if cn.closed.IsSet() { - ret = "op-" - } else { ret = "cl-" + } else { + ret = "op-" } if cn.requestState.Interested { @@ -263,12 +263,14 @@ func (cn *Peer) StatusFlags() (ret string) { } c('-') ret += cn.connectionFlags() - c('-') - if cn.peerInterested { - c('i') - } - if cn.peerChoking { - c('c') + if cn.peerInterested || cn.peerChoking { + c('-') + if cn.peerInterested { + c('i') + } + if cn.peerChoking { + c('c') + } } return } From 866f4dce937df13cf2baad42dc54cae12e17928a Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Wed, 10 Jul 2024 18:19:24 +0200 Subject: [PATCH 18/22] fix fastenabled lock param --- peer.go | 4 ++-- peerconn.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/peer.go b/peer.go index 5033a9f149..3521ec6cd6 100644 --- a/peer.go +++ b/peer.go @@ -250,9 +250,9 @@ func (cn *Peer) StatusFlags() (ret string) { ret += string([]byte{b}) } if cn.closed.IsSet() { - ret = "cl-" + ret = "c-" } else { - ret = "op-" + ret = "o-" } if cn.requestState.Interested { diff --git a/peerconn.go b/peerconn.go index 70ad5c5e8e..85a0fcc756 100644 --- a/peerconn.go +++ b/peerconn.go @@ -647,7 +647,7 @@ func (c *PeerConn) fastEnabled(lock bool) bool { } func (c *PeerConn) reject(r Request, lock bool) { - if !c.fastEnabled(true) { + if !c.fastEnabled(lock) { panic("fast not enabled") } c.write(r.ToMsg(pp.Reject), lock) From 6b884788291d105706d82e4ad0de8b7846072782 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Thu, 11 Jul 2024 00:36:47 +0200 Subject: [PATCH 19/22] remove printer --- torrent.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/torrent.go b/torrent.go index 507214697b..af1890cb72 100644 --- a/torrent.go +++ b/torrent.go @@ -1788,20 +1788,11 @@ func (t *Torrent) maxHalfOpen(lock bool) int { } func (t *Torrent) openNewConns(lock bool) (initiated int) { - lc := false if lock { - if t.mu.lc.Load() > 0 || t.mu.rlc.Load() > 0 { - lc = true - fmt.Println("ONC", "L", t.mu.locker, "R", t.mu.rlocker) - } t.mu.Lock() defer t.mu.Unlock() } - if lc { - fmt.Println("ONC", "LOCKED", "L", t.mu.locker, "R", t.mu.rlocker) - defer fmt.Println("ONC", "DONE") - } defer t.updateWantPeersEvent(false) for t.peers.Len() != 0 { From 1b56deb061dbf7dd41733aedcc5e37aced8f9e9f Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Thu, 11 Jul 2024 00:41:27 +0200 Subject: [PATCH 20/22] remove printer --- torrent.go | 1 - 1 file changed, 1 deletion(-) diff --git a/torrent.go b/torrent.go index af1890cb72..a3481a0911 100644 --- a/torrent.go +++ b/torrent.go @@ -2771,7 +2771,6 @@ func (t *Torrent) pieceHashed(piece pieceIndex, passed bool, hashIoErr error) { // single peer for a piece, and we never progress that piece to completion, we // will never smart-ban them. Discovered in // https://github.com/anacrolix/torrent/issues/715. - fmt.Printf("banning %v for being sole dirtier of piece %v after failed piece check\n", c, piece) t.logger.Levelf(log.Warning, "banning %v for being sole dirtier of piece %v after failed piece check", c, piece) c.ban() } From 4b6e144f1694d44b586f1fc918ff65e6d631dde3 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Thu, 11 Jul 2024 14:03:08 +0200 Subject: [PATCH 21/22] increase ban count to 16 --- webseed-peer.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/webseed-peer.go b/webseed-peer.go index a98e047cb1..a4a8c2e451 100644 --- a/webseed-peer.go +++ b/webseed-peer.go @@ -498,10 +498,8 @@ func (cn *webseedPeer) ban() { banCount := cn.peer.banCount cn.peer.mu.Unlock() - if banCount > 5 && !cn.peer.closed.IsSet() { - preva, prevc := cn.peer.t.numActivePeers(true), cn.peer.closed.IsSet() + if banCount > 16 && !cn.peer.closed.IsSet() { cn.peer.close(true) - fmt.Println("BCLS", cn.String(), "P", prevc, preva, "A", cn.peer.closed.IsSet(), cn.peer.t.numActivePeers(true)) } } From 7de8739ed3cb07356fb9968bb44e38649a92c4b6 Mon Sep 17 00:00:00 2001 From: Mark Holt Date: Thu, 11 Jul 2024 17:59:25 +0200 Subject: [PATCH 22/22] remove deadlock tests --- go.mod | 3 -- go.sum | 5 ---- peer.go | 2 +- torrent.go | 84 +----------------------------------------------------- 4 files changed, 2 insertions(+), 92 deletions(-) diff --git a/go.mod b/go.mod index 671e3759cf..d517ecb213 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,6 @@ require ( github.com/frankban/quicktest v1.14.6 github.com/fsnotify/fsnotify v1.5.4 github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 - github.com/go-stack/stack v1.8.0 github.com/google/btree v1.1.2 github.com/google/go-cmp v0.5.9 github.com/gorilla/websocket v1.5.0 @@ -43,7 +42,6 @@ require ( github.com/pion/webrtc/v3 v3.1.42 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.12.2 - github.com/sasha-s/go-deadlock v0.3.1 github.com/stretchr/testify v1.8.4 github.com/tidwall/btree v1.6.0 go.etcd.io/bbolt v1.3.6 @@ -79,7 +77,6 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mschoch/smat v0.2.0 // indirect - github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pion/dtls/v2 v2.2.4 // indirect github.com/pion/ice/v2 v2.2.6 // indirect github.com/pion/interceptor v0.1.11 // indirect diff --git a/go.sum b/go.sum index b9bb92699b..98f331c120 100644 --- a/go.sum +++ b/go.sum @@ -218,7 +218,6 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -365,8 +364,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E= @@ -466,8 +463,6 @@ github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 h1:Lt9DzQALzHoDwMBGJ6v github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= -github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= -github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= diff --git a/peer.go b/peer.go index 3521ec6cd6..e2de52bcd4 100644 --- a/peer.go +++ b/peer.go @@ -28,7 +28,7 @@ type ( Peer struct { // First to ensure 64-bit alignment for atomics. See #262. _stats ConnStats - mu mu //sync.RWMutex + mu sync.RWMutex t *Torrent diff --git a/torrent.go b/torrent.go index a3481a0911..0e03c218c2 100644 --- a/torrent.go +++ b/torrent.go @@ -49,92 +49,10 @@ import ( typedRoaring "github.com/anacrolix/torrent/typed-roaring" "github.com/anacrolix/torrent/webseed" "github.com/anacrolix/torrent/webtorrent" - stack2 "github.com/go-stack/stack" - "github.com/sasha-s/go-deadlock" ) var bufPool = storage.NewBufferPool() -func stack(skip int) string { - return stack2.Trace().TrimBelow(stack2.Caller(skip)).String() -} - -func init() { - deadlock.Opts.DeadlockTimeout = 3 * time.Minute -} - -type mu struct { - deadlock.RWMutex - rlc atomic.Int32 - lc atomic.Int32 - rlmu sync.Mutex - rlocker [20]string - rlocktime time.Time - locker string - nextlocker string - locktime time.Time -} - -func (m *mu) RLock() { - m.RWMutex.RLock() - m.rlmu.Lock() - rlc := m.rlc.Load() - if int(rlc) < len(m.rlocker) { - m.rlocker[rlc] = string(stack(2)) - } - if rlc == 0 { - m.rlocktime = time.Now() - } - m.rlc.Add(1) - m.rlmu.Unlock() - //fmt.Println("R", m.rlc, string(dbg.Stack())[:40]) - -} - -func (m *mu) RUnlock() { - m.rlmu.Lock() - m.rlc.Add(-1) - rlc := m.rlc.Load() - if rlc < 0 { - panic("lock underflow") - } - if rlc == 0 { - m.rlocktime = time.Time{} - } - if int(rlc) < len(m.rlocker) { - m.rlocker[m.rlc.Load()] = "" - } - m.rlmu.Unlock() - m.RWMutex.RUnlock() - //fmt.Println("RUN", m.rlc) //, string(dbg.Stack())) -} - -func (m *mu) Lock() { - m.rlmu.Lock() - if m.nextlocker == "" { - m.nextlocker = string(stack(2)) - } - m.rlmu.Unlock() - m.RWMutex.Lock() - m.lc.Add(1) - m.rlmu.Lock() - m.locker = m.nextlocker - m.locktime = time.Now() - m.nextlocker = "" - m.rlmu.Unlock() -} - -func (m *mu) Unlock() { - m.lc.Add(-1) - if m.lc.Load() < 0 { - panic("lock underflow") - } - m.locker = "" - m.locktime = time.Time{} - m.RWMutex.Unlock() - //fmt.Println("LUN", m.lc) //, string(dbg.Stack())) -} - // Maintains state of torrent within a Client. Many methods should not be called before the info is // available, see .Info and .GotInfo. type Torrent struct { @@ -204,7 +122,7 @@ type Torrent struct { // How many times we've initiated a DHT announce. TODO: Move into stats. numDHTAnnounces int - mu mu //sync.RWMutex + mu sync.RWMutex imu sync.RWMutex // Name used if the info name isn't available. Should be cleared when the // Info does become available.