2
0
mirror of synced 2025-02-24 06:38:14 +00:00

Hunting for goroutine leaks

Start implementing Client.Stop().
This commit is contained in:
Matt Joiner 2014-03-18 22:39:33 +11:00
parent 2e81f914e7
commit 998fbeb0c6
3 changed files with 68 additions and 10 deletions

View File

@ -88,6 +88,7 @@ type Request struct {
type Connection struct {
Socket net.Conn
Closed bool
post chan encoding.BinaryMarshaler
write chan []byte
@ -106,8 +107,12 @@ type Connection struct {
}
func (c *Connection) Close() {
if c.Closed {
return
}
c.Socket.Close()
close(c.post)
c.Closed = true
}
func (c *Connection) PeerHasPiece(index peer_protocol.Integer) bool {
@ -417,6 +422,7 @@ type Client struct {
sync.Mutex
mu *sync.Mutex
event sync.Cond
quit chan struct{}
halfOpen int
torrents map[InfoHash]*Torrent
@ -487,14 +493,30 @@ func (c *Client) Start() {
if err != nil {
panic("error generating peer id")
}
c.quit = make(chan struct{})
if c.Listener != nil {
go c.acceptConnections()
}
}
func (me *Client) Stop() {
close(me.quit)
me.event.Broadcast()
for _, t := range me.torrents {
for _, c := range t.Conns {
c.Close()
}
}
}
func (cl *Client) acceptConnections() {
for {
conn, err := cl.Listener.Accept()
select {
case <-cl.quit:
return
default:
}
if err != nil {
log.Print(err)
return
@ -958,9 +980,6 @@ func (me *Client) WaitAll() {
me.mu.Unlock()
}
func (me *Client) Stop() {
}
func (me *Client) replenishConnRequests(torrent *Torrent, conn *Connection) {
requestHeatMap := torrent.requestHeat()
addRequest := func(req Request) (again bool) {

View File

@ -49,6 +49,8 @@ func (tfs *torrentFS) UnsubscribeData(ch chan torrent.DataSpec) {
close(ch)
}
var _ fusefs.NodeForgetter = rootNode{}
type rootNode struct {
fs *torrentFS
}
@ -242,19 +244,18 @@ func (rootNode) Attr() fuse.Attr {
}
}
func (rootNode) Forget() {
}
func (tfs *torrentFS) Root() (fusefs.Node, fuse.Error) {
return rootNode{tfs}, nil
}
func MountAndServe(dir string, cl *torrent.Client) error {
conn, err := fuse.Mount(dir)
if err != nil {
return err
}
func New(cl *torrent.Client) *torrentFS {
fs := &torrentFS{
Client: cl,
DataSubs: make(map[chan torrent.DataSpec]struct{}),
}
go fs.publishData()
return fusefs.Serve(conn, fs)
return fs
}

View File

@ -1,6 +1,8 @@
package torrentfs
import (
"bazil.org/fuse"
fusefs "bazil.org/fuse/fs"
"bitbucket.org/anacrolix/go.torrent"
"bytes"
metainfo "github.com/nsf/libtorgo/torrent"
@ -9,6 +11,7 @@ import (
"net"
"os"
"path/filepath"
"runtime"
"testing"
)
@ -34,6 +37,8 @@ func TestTCPAddrString(t *testing.T) {
}
}
const dummyFileContents = "hello, world\n"
func createDummyTorrentData(dirName string) string {
f, _ := os.Create(filepath.Join(dirName, "greeting"))
f.WriteString("hello, world\n")
@ -53,6 +58,13 @@ func createMetaInfo(name string, w io.Writer) {
}
func TestDownloadOnDemand(t *testing.T) {
priorNumGoroutines := runtime.NumGoroutine()
defer func() {
n := runtime.NumGoroutine()
if n != priorNumGoroutines {
t.Fatalf("expected %d goroutines, but %d are running", priorNumGoroutines, n)
}
}()
dir, err := ioutil.TempDir("", "torrentfs")
if err != nil {
t.Fatal(err)
@ -79,13 +91,16 @@ func TestDownloadOnDemand(t *testing.T) {
return conn
}(),
}
defer seeder.Listener.Close()
seeder.Start()
defer seeder.Stop()
seeder.AddTorrent(metaInfo)
leecher := torrent.Client{
DataDir: filepath.Join(dir, "download"),
DataReady: make(chan torrent.DataSpec),
}
leecher.Start()
defer leecher.Stop()
leecher.AddTorrent(metaInfo)
leecher.AddPeers(torrent.BytesInfoHash(metaInfo.InfoHash), []torrent.Peer{func() torrent.Peer {
tcpAddr := seeder.Listener.Addr().(*net.TCPAddr)
@ -96,8 +111,31 @@ func TestDownloadOnDemand(t *testing.T) {
}()})
mountDir := filepath.Join(dir, "mnt")
os.Mkdir(mountDir, 0777)
err = MountAndServe(mountDir, &leecher)
fs := New(&leecher)
fuseConn, err := fuse.Mount(mountDir)
if err != nil {
t.Fatal(err)
}
go func() {
if err := fusefs.Serve(fuseConn, fs); err != nil {
t.Fatal(err)
}
if err := fuseConn.Close(); err != nil {
t.Fatal(err)
}
}()
<-fuseConn.Ready
if fuseConn.MountError != nil {
t.Fatal(fuseConn.MountError)
}
content, err := ioutil.ReadFile(filepath.Join(mountDir, "greeting"))
if err != nil {
t.Fatal(err)
}
if err := fuse.Unmount(mountDir); err != nil {
t.Fatal(err)
}
if string(content) != dummyFileContents {
t.FailNow()
}
}