Commit 01a77bb9 authored by Kirill Smelkov's avatar Kirill Smelkov

xnet: Networker += Close

Even though there is no need to manually release resources for NetPlain
and NetTLS, for other networkers - for example lonet - releasing
resources is needed. In general programs cannot always use lonet
explicitly, as there might be a factory function that creates a
Networker according to given parameters.

-> Adjust Networker interface to include general Close.
-> Adjust NetPlain to follow added "interrupt-on-close" semantic for uniformity.
parent b03d65ff
...@@ -22,12 +22,14 @@ package xnet ...@@ -22,12 +22,14 @@ package xnet
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net" "net"
"os" "os"
"crypto/tls" "crypto/tls"
"lab.nexedi.com/kirr/go123/xcontext"
"lab.nexedi.com/kirr/go123/xsync" "lab.nexedi.com/kirr/go123/xsync"
) )
...@@ -51,6 +53,12 @@ type Networker interface { ...@@ -51,6 +53,12 @@ type Networker interface {
// //
// See net.Listen for semantic details. // See net.Listen for semantic details.
Listen(ctx context.Context, laddr string) (Listener, error) Listen(ctx context.Context, laddr string) (Listener, error)
// Close releases resources associated with the network access-point.
//
// In-progress and future network operations such as Dial and Listen,
// originated via this access-point, will return with an error.
Close() error
} }
// Listener amends net.Listener for Accept to handle cancellation. // Listener amends net.Listener for Accept to handle cancellation.
...@@ -72,15 +80,25 @@ func init() { ...@@ -72,15 +80,25 @@ func init() {
hostname = host hostname = host
} }
var errNetClosed = errors.New("network access-point is closed")
// NetPlain creates Networker corresponding to regular network accessors from std package net. // NetPlain creates Networker corresponding to regular network accessors from std package net.
// //
// network is "tcp", "tcp4", "tcp6", "unix", etc... // network is "tcp", "tcp4", "tcp6", "unix", etc...
func NetPlain(network string) Networker { func NetPlain(network string) Networker {
return &netPlain{network, hostname} n := &netPlain{network: network, hostname: hostname}
n.ctx, n.cancel = context.WithCancel(context.Background())
return n
} }
type netPlain struct { type netPlain struct {
network, hostname string network, hostname string
// ctx.cancel is merged into context of network operations.
// ctx is cancelled on Close.
ctx context.Context
cancel func()
} }
func (n *netPlain) Network() string { func (n *netPlain) Network() string {
...@@ -91,17 +109,76 @@ func (n *netPlain) Name() string { ...@@ -91,17 +109,76 @@ func (n *netPlain) Name() string {
return n.hostname return n.hostname
} }
func (n *netPlain) Close() error {
n.cancel()
return nil
}
func (n *netPlain) Dial(ctx context.Context, addr string) (net.Conn, error) { func (n *netPlain) Dial(ctx context.Context, addr string) (net.Conn, error) {
d := net.Dialer{} ctx, cancel := xcontext.Merge(ctx, n.ctx)
return d.DialContext(ctx, n.network, addr) defer cancel()
dialErr := func(err error) error {
return &net.OpError{Op: "dial", Net: n.network, Addr: &strAddr{n.network, addr}, Err: err}
}
// don't try to call Dial if already closed / canceled
var conn net.Conn
err := ctx.Err()
if err == nil {
d := net.Dialer{}
conn, err = d.DialContext(ctx, n.network, addr)
} else {
err = dialErr(err)
}
if err != nil {
// convert n.ctx cancel -> "closed" error
if n.ctx.Err() != nil {
switch e := err.(type) {
case *net.OpError:
e.Err = errNetClosed
default:
// just in case
err = dialErr(errNetClosed)
}
}
}
return conn, err
} }
func (n *netPlain) Listen(ctx context.Context, laddr string) (Listener, error) { func (n *netPlain) Listen(ctx context.Context, laddr string) (Listener, error) {
lc := net.ListenConfig{} ctx, cancel := xcontext.Merge(ctx, n.ctx)
rawl, err := lc.Listen(ctx, n.network, laddr) defer cancel()
listenErr := func(err error) error {
return &net.OpError{Op: "listen", Net: n.network, Addr: &strAddr{n.network, laddr}, Err: err}
}
// don't try to call Listen if already closed / canceled
var rawl net.Listener
err := ctx.Err()
if err == nil {
lc := net.ListenConfig{}
rawl, err = lc.Listen(ctx, n.network, laddr)
} else {
err = listenErr(err)
}
if err != nil { if err != nil {
// convert n.ctx cancel -> "closed" error
if n.ctx.Err() != nil {
switch e := err.(type) {
case *net.OpError:
e.Err = errNetClosed
default:
// just in case
err = listenErr(errNetClosed)
}
}
return nil, err return nil, err
} }
return WithCtxL(rawl), nil return WithCtxL(rawl), nil
} }
...@@ -128,6 +205,10 @@ func (n *netTLS) Name() string { ...@@ -128,6 +205,10 @@ func (n *netTLS) Name() string {
return n.inner.Name() return n.inner.Name()
} }
func (n *netTLS) Close() error {
return n.inner.Close()
}
func (n *netTLS) Dial(ctx context.Context, addr string) (net.Conn, error) { func (n *netTLS) Dial(ctx context.Context, addr string) (net.Conn, error) {
c, err := n.inner.Dial(ctx, addr) c, err := n.inner.Dial(ctx, addr)
if err != nil { if err != nil {
...@@ -167,6 +248,17 @@ func (l *listenerTLS) Accept(ctx context.Context) (net.Conn, error) { ...@@ -167,6 +248,17 @@ func (l *listenerTLS) Accept(ctx context.Context) (net.Conn, error) {
} }
// ---- misc ----
// strAddr turns string into net.Addr.
type strAddr struct {
net string
addr string
}
func (a *strAddr) Network() string { return a.net }
func (a *strAddr) String() string { return a.addr }
// ---------------------------------------- // ----------------------------------------
// BindCtx*(xnet.X, ctx) -> net.X // BindCtx*(xnet.X, ctx) -> net.X
......
...@@ -51,6 +51,7 @@ type Tracer interface { ...@@ -51,6 +51,7 @@ type Tracer interface {
TraceNetConnect(*TraceConnect) TraceNetConnect(*TraceConnect)
TraceNetListen(*TraceListen) TraceNetListen(*TraceListen)
TraceNetTx(*TraceTx) TraceNetTx(*TraceTx)
// XXX +TraceNetClose?
} }
// TraceDial is event corresponding to network dial start. // TraceDial is event corresponding to network dial start.
...@@ -94,6 +95,11 @@ func (nt *netTrace) Name() string { ...@@ -94,6 +95,11 @@ func (nt *netTrace) Name() string {
return nt.inner.Name() return nt.inner.Name()
} }
func (nt *netTrace) Close() error {
// XXX +trace?
return nt.inner.Close()
}
func (nt *netTrace) Dial(ctx context.Context, addr string) (net.Conn, error) { func (nt *netTrace) Dial(ctx context.Context, addr string) (net.Conn, error) {
nt.tracer.TraceNetDial(&TraceDial{Dialer: nt.inner.Name(), Addr: addr}) nt.tracer.TraceNetDial(&TraceDial{Dialer: nt.inner.Name(), Addr: addr})
c, err := nt.inner.Dial(ctx, addr) c, err := nt.inner.Dial(ctx, addr)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment