evio/evio_net.go

362 lines
6.9 KiB
Go
Raw Normal View History

2017-11-02 18:08:18 +03:00
// Copyright 2017 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
2017-10-30 00:05:23 +03:00
package evio
import (
2017-10-30 15:59:57 +03:00
"io"
2017-10-30 00:05:23 +03:00
"net"
"sort"
"sync"
2017-10-30 15:59:57 +03:00
"sync/atomic"
2017-10-30 00:05:23 +03:00
"time"
)
2017-10-31 00:02:10 +03:00
type netConn struct {
2017-11-01 03:44:57 +03:00
id int
wake int64
conn net.Conn
detached bool
outbuf []byte
err error
}
func (c *netConn) Read(p []byte) (n int, err error) {
return c.conn.Read(p)
}
func (c *netConn) Write(p []byte) (n int, err error) {
if c.detached {
if len(c.outbuf) > 0 {
for len(c.outbuf) > 0 {
n, err = c.conn.Write(c.outbuf)
if n > 0 {
c.outbuf = c.outbuf[n:]
}
if err != nil {
return 0, err
}
}
c.outbuf = nil
}
var tn int
if len(p) > 0 {
for len(p) > 0 {
n, err = c.conn.Write(p)
if n > 0 {
p = p[n:]
tn += n
}
if err != nil {
return tn, err
}
}
p = nil
}
return tn, nil
}
return c.conn.Write(p)
}
func (c *netConn) Close() error {
return c.conn.Close()
2017-10-31 00:02:10 +03:00
}
2017-10-30 00:05:23 +03:00
// servenet uses the stdlib net package instead of syscalls.
func servenet(events Events, lns []*listener) error {
2017-10-30 15:59:57 +03:00
var id int64
var mu sync.Mutex
var cmu sync.Mutex
2017-10-31 00:02:10 +03:00
var idconn = make(map[int]*netConn)
2017-10-30 15:59:57 +03:00
var done bool
2017-11-07 16:49:33 +03:00
ctx := Server{
2017-11-06 13:58:06 +03:00
Wake: func(id int) bool {
cmu.Lock()
c := idconn[id]
cmu.Unlock()
if c == nil {
return false
}
atomic.StoreInt64(&c.wake, 1)
// force a quick wakeup
c.conn.SetDeadline(time.Time{}.Add(1))
return true
},
2017-10-30 00:05:23 +03:00
}
2017-11-02 03:36:35 +03:00
var swg sync.WaitGroup
swg.Add(1)
2017-10-30 15:59:57 +03:00
var ferr error
shutdown := func(err error) {
mu.Lock()
if done {
mu.Unlock()
return
}
2017-11-02 03:36:35 +03:00
defer swg.Done()
2017-10-30 15:59:57 +03:00
done = true
ferr = err
for _, ln := range lns {
ln.ln.Close()
2017-10-30 00:05:23 +03:00
}
type connid struct {
conn net.Conn
id int
}
var connids []connid
2017-10-30 15:59:57 +03:00
cmu.Lock()
2017-10-30 00:05:23 +03:00
for id, conn := range idconn {
connids = append(connids, connid{conn.conn, id})
}
2017-10-31 00:02:10 +03:00
idconn = make(map[int]*netConn)
2017-10-30 15:59:57 +03:00
cmu.Unlock()
mu.Unlock()
2017-10-30 00:05:23 +03:00
sort.Slice(connids, func(i, j int) bool {
return connids[j].id < connids[i].id
})
for _, connid := range connids {
connid.conn.Close()
if events.Closed != nil {
2017-10-30 15:59:57 +03:00
mu.Lock()
2017-11-01 03:44:57 +03:00
events.Closed(connid.id, nil)
2017-10-30 15:59:57 +03:00
mu.Unlock()
2017-10-30 00:05:23 +03:00
}
}
}
2017-11-06 13:58:06 +03:00
ctx.Addrs = make([]net.Addr, len(lns))
for i, ln := range lns {
ctx.Addrs[i] = ln.naddr
}
2017-10-30 15:59:57 +03:00
if events.Serving != nil {
2017-11-06 13:58:06 +03:00
if events.Serving(ctx) == Shutdown {
2017-10-30 15:59:57 +03:00
return nil
}
}
2017-11-02 03:36:35 +03:00
var lwg sync.WaitGroup
lwg.Add(len(lns))
2017-10-30 15:59:57 +03:00
for i, ln := range lns {
go func(lnidx int, ln net.Listener) {
2017-11-02 03:36:35 +03:00
defer lwg.Done()
2017-10-30 15:59:57 +03:00
for {
conn, err := ln.Accept()
if err != nil {
if err == io.EOF {
shutdown(nil)
} else {
shutdown(err)
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
return
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
id := int(atomic.AddInt64(&id, 1))
go func(id int, conn net.Conn) {
var closed bool
defer func() {
if !closed {
conn.Close()
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
}()
var packet [0xFFFF]byte
var cout []byte
var caction Action
2017-10-31 00:02:10 +03:00
c := &netConn{id: id, conn: conn}
2017-10-30 15:59:57 +03:00
cmu.Lock()
idconn[id] = c
cmu.Unlock()
if events.Opened != nil {
var out []byte
var opts Options
var action Action
mu.Lock()
if !done {
2017-11-07 16:49:33 +03:00
out, opts, action = events.Opened(id, Conn{
AddrIndex: lnidx,
LocalAddr: conn.LocalAddr(),
RemoteAddr: conn.RemoteAddr(),
})
2017-10-30 15:59:57 +03:00
}
mu.Unlock()
if opts.TCPKeepAlive > 0 {
if conn, ok := conn.(*net.TCPConn); ok {
conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(opts.TCPKeepAlive)
}
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
if len(out) > 0 {
cout = append(cout, out...)
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
caction = action
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
for {
var n int
var out []byte
var action Action
if caction != None {
goto write
}
2017-11-02 03:36:35 +03:00
conn.SetReadDeadline(time.Now().Add(time.Microsecond))
2017-11-01 03:44:57 +03:00
n, err = c.Read(packet[:])
2017-10-30 15:59:57 +03:00
if err != nil && !istimeout(err) {
2017-11-02 03:36:35 +03:00
if err != io.EOF {
c.err = err
}
2017-10-30 15:59:57 +03:00
goto close
}
if n > 0 {
if events.Data != nil {
mu.Lock()
if !done {
out, action = events.Data(id, append([]byte{}, packet[:n]...))
}
mu.Unlock()
}
} else if atomic.LoadInt64(&c.wake) != 0 {
atomic.StoreInt64(&c.wake, 0)
if events.Data != nil {
mu.Lock()
if !done {
out, action = events.Data(id, nil)
}
mu.Unlock()
}
}
if len(out) > 0 {
cout = append(cout, out...)
}
caction = action
goto write
write:
if len(cout) > 0 {
if events.Prewrite != nil {
mu.Lock()
if !done {
action = events.Prewrite(id, len(cout))
}
mu.Unlock()
if action == Shutdown {
caction = Shutdown
}
}
2017-11-02 03:36:35 +03:00
conn.SetWriteDeadline(time.Now().Add(time.Microsecond))
2017-11-01 03:44:57 +03:00
n, err := c.Write(cout)
2017-10-30 15:59:57 +03:00
if err != nil && !istimeout(err) {
2017-11-02 03:36:35 +03:00
if err != io.EOF {
c.err = err
}
2017-10-30 15:59:57 +03:00
goto close
}
cout = cout[n:]
if len(cout) == 0 {
cout = nil
}
if events.Postwrite != nil {
mu.Lock()
if !done {
action = events.Postwrite(id, n, len(cout))
}
mu.Unlock()
if action == Shutdown {
caction = Shutdown
}
}
}
if caction == Shutdown {
goto close
2017-10-30 00:05:23 +03:00
}
2017-10-31 00:02:10 +03:00
if len(cout) == 0 {
if caction != None {
goto close
}
}
2017-10-30 00:05:23 +03:00
continue
2017-10-30 15:59:57 +03:00
close:
cmu.Lock()
delete(idconn, c.id)
cmu.Unlock()
mu.Lock()
if done {
mu.Unlock()
return
}
mu.Unlock()
if caction == Detach {
if events.Detached != nil {
2017-11-01 03:44:57 +03:00
if len(cout) > 0 {
c.outbuf = cout
}
2017-11-02 03:36:35 +03:00
c.detached = true
2017-10-31 00:02:10 +03:00
conn.SetDeadline(time.Time{})
2017-10-30 15:59:57 +03:00
mu.Lock()
if !done {
2017-11-01 03:44:57 +03:00
caction = events.Detached(c.id, c)
2017-10-30 15:59:57 +03:00
}
mu.Unlock()
closed = true
if caction == Shutdown {
goto fail
}
2017-11-01 03:44:57 +03:00
return
2017-10-30 15:59:57 +03:00
}
}
conn.Close()
if events.Closed != nil {
var action Action
mu.Lock()
if !done {
2017-11-01 03:44:57 +03:00
action = events.Closed(c.id, c.err)
2017-10-30 15:59:57 +03:00
}
mu.Unlock()
if action == Shutdown {
caction = Shutdown
}
}
closed = true
if caction == Shutdown {
goto fail
}
return
fail:
shutdown(nil)
return
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
}(id, conn)
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
}(i, ln.ln)
2017-10-30 00:05:23 +03:00
}
2017-10-30 15:59:57 +03:00
go func() {
for {
2017-11-02 03:36:35 +03:00
mu.Lock()
if done {
mu.Unlock()
break
}
mu.Unlock()
2017-10-30 15:59:57 +03:00
var delay time.Duration
var action Action
2017-11-02 03:36:35 +03:00
mu.Lock()
2017-10-30 15:59:57 +03:00
if events.Tick != nil {
2017-11-02 03:36:35 +03:00
if !done {
delay, action = events.Tick()
}
} else {
2017-10-30 15:59:57 +03:00
mu.Unlock()
2017-11-02 03:36:35 +03:00
break
2017-10-30 15:59:57 +03:00
}
2017-11-02 03:36:35 +03:00
mu.Unlock()
2017-10-30 15:59:57 +03:00
if action == Shutdown {
shutdown(nil)
return
}
time.Sleep(delay)
}
}()
2017-11-02 03:36:35 +03:00
lwg.Wait() // wait for listeners
swg.Wait() // wait for shutdown
2017-10-30 15:59:57 +03:00
return ferr
2017-10-30 00:05:23 +03:00
}
func istimeout(err error) bool {
if err, ok := err.(net.Error); ok && err.Timeout() {
return true
}
return false
}