Index: src/pkg/net/fd_plan9.go |
=================================================================== |
--- a/src/pkg/net/fd_plan9.go |
+++ b/src/pkg/net/fd_plan9.go |
@@ -13,12 +13,23 @@ |
// Network file descritor. |
type netFD struct { |
- proto, name, dir string |
- ctl, data *os.File |
- laddr, raddr Addr |
+ // locking/lifetime of sysfd + serialize access to Read and Write methods |
+ fdmu fdMutex |
+ |
+ // immutable until Close |
+ proto string |
+ n string |
+ dir string |
+ ctl, data *os.File |
+ laddr, raddr Addr |
} |
+var ( |
+ netdir string // default network |
+) |
+ |
func sysInit() { |
+ netdir = "/net" |
} |
func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) { |
@@ -27,16 +38,99 @@ |
return dialChannel(net, ra, dialer, deadline) |
} |
-func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD { |
- return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, data, laddr, raddr} |
+func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) { |
+ return &netFD{proto: proto, n: name, dir: netdir + "/" + proto + "/" + name, ctl: ctl, data: data, laddr: laddr, raddr: raddr}, nil |
+} |
+ |
+func (fd *netFD) init() error { |
+ // stub for future fd.pd.Init(fd) |
+ return nil |
+} |
+ |
+func (fd *netFD) name() string { |
+ var ls, rs string |
+ if fd.laddr != nil { |
+ ls = fd.laddr.String() |
+ } |
+ if fd.raddr != nil { |
+ rs = fd.raddr.String() |
+ } |
+ return fd.proto + ":" + ls + "->" + rs |
} |
func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil } |
+func (fd *netFD) destroy() { |
+ if !fd.ok() { |
+ return |
+ } |
+ err := fd.ctl.Close() |
+ if fd.data != nil { |
+ if err1 := fd.data.Close(); err1 != nil && err == nil { |
+ err = err1 |
+ } |
+ } |
+ fd.ctl = nil |
+ fd.data = nil |
+} |
+ |
+// Add a reference to this fd. |
+// Returns an error if the fd cannot be used. |
+func (fd *netFD) incref() error { |
+ if !fd.fdmu.Incref() { |
+ return errClosing |
+ } |
+ return nil |
+} |
+ |
+// Remove a reference to this FD and close if we've been asked to do so |
+// (and there are no references left). |
+func (fd *netFD) decref() { |
+ if fd.fdmu.Decref() { |
+ fd.destroy() |
+ } |
+} |
+ |
+// Add a reference to this fd and lock for reading. |
+// Returns an error if the fd cannot be used. |
+func (fd *netFD) readLock() error { |
+ if !fd.fdmu.RWLock(true) { |
+ return errClosing |
+ } |
+ return nil |
+} |
+ |
+// Unlock for reading and remove a reference to this FD. |
+func (fd *netFD) readUnlock() { |
+ if fd.fdmu.RWUnlock(true) { |
+ fd.destroy() |
+ } |
+} |
+ |
+// Add a reference to this fd and lock for writing. |
+// Returns an error if the fd cannot be used. |
+func (fd *netFD) writeLock() error { |
+ if !fd.fdmu.RWLock(false) { |
+ return errClosing |
+ } |
+ return nil |
+} |
+ |
+// Unlock for writing and remove a reference to this FD. |
+func (fd *netFD) writeUnlock() { |
+ if fd.fdmu.RWUnlock(false) { |
+ fd.destroy() |
+ } |
+} |
+ |
func (fd *netFD) Read(b []byte) (n int, err error) { |
if !fd.ok() || fd.data == nil { |
return 0, syscall.EINVAL |
} |
+ if err := fd.readLock(); err != nil { |
+ return 0, err |
+ } |
+ defer fd.readUnlock() |
n, err = fd.data.Read(b) |
if fd.proto == "udp" && err == io.EOF { |
n = 0 |
@@ -49,6 +143,10 @@ |
if !fd.ok() || fd.data == nil { |
return 0, syscall.EINVAL |
} |
+ if err := fd.writeLock(); err != nil { |
+ return 0, err |
+ } |
+ defer fd.writeUnlock() |
return fd.data.Write(b) |
} |
@@ -67,6 +165,9 @@ |
} |
func (fd *netFD) Close() error { |
+ if !fd.fdmu.IncrefAndClose() { |
+ return errClosing |
+ } |
if !fd.ok() { |
return syscall.EINVAL |
} |