Fixed bug where tried closing a single net.Conn multiple times.

This commit is contained in:
Zach Klippenstein 2015-12-30 00:00:03 -08:00
parent d2cac01b63
commit 325901c2c3
3 changed files with 33 additions and 7 deletions

View file

@ -2,6 +2,7 @@ package goadb
import (
"fmt"
"io"
"net"
"runtime"
@ -66,17 +67,22 @@ func (d *netDialer) Dial() (*wire.Conn, error) {
}
}
conn := &wire.Conn{
Scanner: wire.NewScanner(netConn),
Sender: wire.NewSender(netConn),
}
// net.Conn can't be closed more than once, but wire.Conn will try to close both sender and scanner
// so we need to wrap it to make it safe.
safeConn := wire.MultiCloseable(netConn)
// Prevent leaking the network connection, not sure if TCPConn does this itself.
runtime.SetFinalizer(netConn, func(conn *net.TCPConn) {
// Note that the network connection may still be in use after the conn isn't (scanners/senders
// can give their underlying connections to other scanner/sender types), so we can't
// set the finalizer on conn.
runtime.SetFinalizer(safeConn, func(conn io.ReadWriteCloser) {
conn.Close()
})
return conn, nil
return &wire.Conn{
Scanner: wire.NewScanner(safeConn),
Sender: wire.NewSender(safeConn),
}, nil
}
func roundTripSingleResponse(d Dialer, req string) ([]byte, error) {

View file

@ -66,7 +66,7 @@ func (w *syncFileWriter) Close() error {
}
if err := w.sender.SendOctetString(wire.StatusSyncDone); err != nil {
return util.WrapErrf(err, "error closing file writer")
return util.WrapErrf(err, "error sending done chunk to close stream")
}
if err := w.sender.SendTime(w.mtime); err != nil {
return util.WrapErrf(err, "error writing file modification time")

View file

@ -5,6 +5,8 @@ import (
"io"
"regexp"
"sync"
"github.com/zach-klippenstein/goadb/util"
)
@ -80,3 +82,21 @@ func writeFully(w io.Writer, data []byte) error {
}
return nil
}
// MultiCloseable wraps c in a ReadWriteCloser that can be safely closed multiple times.
func MultiCloseable(c io.ReadWriteCloser) io.ReadWriteCloser {
return &multiCloseable{ReadWriteCloser: c}
}
type multiCloseable struct {
io.ReadWriteCloser
closeOnce sync.Once
err error
}
func (c *multiCloseable) Close() error {
c.closeOnce.Do(func() {
c.err = c.ReadWriteCloser.Close()
})
return c.err
}