From f451a39b69e57fe5c4e433d9408ca560b374fd54 Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Sun, 27 Nov 2016 14:26:45 +1100 Subject: [PATCH] Fix harmless race conditions in Client.WriteStatus introduced way back --- client.go | 26 ++++++++++++++++++-------- torrent.go | 6 +++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/client.go b/client.go index 2e6ded8b..5dfb465e 100644 --- a/client.go +++ b/client.go @@ -79,6 +79,10 @@ type Client struct { func (cl *Client) BadPeerIPs() []string { cl.mu.RLock() defer cl.mu.RUnlock() + return cl.badPeerIPsLocked() +} + +func (cl *Client) badPeerIPsLocked() []string { return slices.FromMapKeys(cl.badPeerIPs).([]string) } @@ -123,6 +127,8 @@ func (cl *Client) sortedTorrents() (ret []*Torrent) { // Writes out a human readable status of the client, such as for writing to a // HTTP status page. func (cl *Client) WriteStatus(_w io.Writer) { + cl.mu.Lock() + defer cl.mu.Unlock() w := bufio.NewWriter(_w) defer w.Flush() if addr := cl.ListenAddr(); addr != nil { @@ -131,7 +137,7 @@ func (cl *Client) WriteStatus(_w io.Writer) { fmt.Fprintln(w, "Not listening!") } fmt.Fprintf(w, "Peer ID: %+q\n", cl.PeerID()) - fmt.Fprintf(w, "Banned IPs: %d\n", len(cl.BadPeerIPs())) + fmt.Fprintf(w, "Banned IPs: %d\n", len(cl.badPeerIPsLocked())) if dht := cl.DHT(); dht != nil { dhtStats := dht.Stats() fmt.Fprintf(w, "DHT nodes: %d (%d good, %d banned)\n", dhtStats.Nodes, dhtStats.GoodNodes, dhtStats.BadNodes) @@ -140,19 +146,19 @@ func (cl *Client) WriteStatus(_w io.Writer) { fmt.Fprintf(w, "DHT announces: %d\n", dhtStats.ConfirmedAnnounces) fmt.Fprintf(w, "Outstanding transactions: %d\n", dhtStats.OutstandingTransactions) } - fmt.Fprintf(w, "# Torrents: %d\n", len(cl.Torrents())) + fmt.Fprintf(w, "# Torrents: %d\n", len(cl.torrentsAsSlice())) fmt.Fprintln(w) - for _, t := range slices.Sort(append([]*Torrent(nil), cl.Torrents()...), func(l, r *Torrent) bool { + for _, t := range slices.Sort(cl.torrentsAsSlice(), func(l, r *Torrent) bool { return l.InfoHash().AsString() < r.InfoHash().AsString() }).([]*Torrent) { - if t.Name() == "" { + if t.name() == "" { fmt.Fprint(w, "") } else { - fmt.Fprint(w, t.Name()) + fmt.Fprint(w, t.name()) } fmt.Fprint(w, "\n") if t.Info() != nil { - fmt.Fprintf(w, "%f%% of %d bytes (%s)", 100*(1-float64(t.BytesMissing())/float64(t.Info().TotalLength())), t.length, humanize.Bytes(uint64(t.Info().TotalLength()))) + fmt.Fprintf(w, "%f%% of %d bytes (%s)", 100*(1-float64(t.bytesMissingLocked())/float64(t.Info().TotalLength())), t.length, humanize.Bytes(uint64(t.Info().TotalLength()))) } else { w.WriteString("") } @@ -1269,12 +1275,16 @@ func (cl *Client) WaitAll() bool { } // Returns handles to all the torrents loaded in the Client. -func (cl *Client) Torrents() (ret []*Torrent) { +func (cl *Client) Torrents() []*Torrent { cl.mu.Lock() + defer cl.mu.Unlock() + return cl.torrentsAsSlice() +} + +func (cl *Client) torrentsAsSlice() (ret []*Torrent) { for _, t := range cl.torrents { ret = append(ret, t) } - cl.mu.Unlock() return } diff --git a/torrent.go b/torrent.go index 3fd3f0b2..ed58da14 100644 --- a/torrent.go +++ b/torrent.go @@ -447,7 +447,7 @@ func (t *Torrent) writeStatus(w io.Writer) { if t.Info() != nil { fmt.Fprintf(w, "Num Pieces: %d\n", t.numPieces()) fmt.Fprint(w, "Piece States:") - for _, psr := range t.PieceStateRuns() { + for _, psr := range t.pieceStateRuns() { w.Write([]byte(" ")) w.Write([]byte(pieceStateRunStatusChars(psr))) } @@ -509,6 +509,10 @@ func (t *Torrent) newMetaInfo() metainfo.MetaInfo { func (t *Torrent) BytesMissing() int64 { t.mu().RLock() defer t.mu().RUnlock() + return t.bytesMissingLocked() +} + +func (t *Torrent) bytesMissingLocked() int64 { return t.bytesLeft() }