evio/evio_loop.go

565 lines
11 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-28 22:23:13 +03:00
// +build netbsd openbsd freebsd darwin dragonfly linux
2017-10-26 23:43:31 +03:00
2017-10-29 00:58:59 +03:00
package evio
2017-10-26 23:43:31 +03:00
import (
"errors"
"net"
"os"
"sort"
"strconv"
"sync"
"syscall"
"time"
2017-10-28 22:23:13 +03:00
2017-10-29 00:58:59 +03:00
"github.com/tidwall/evio/internal"
2017-10-26 23:43:31 +03:00
)
func (ln *listener) close() {
if ln.fd != 0 {
syscall.Close(ln.fd)
}
if ln.f != nil {
ln.f.Close()
}
if ln.ln != nil {
ln.ln.Close()
}
if ln.network == "unix" {
os.RemoveAll(ln.addr)
}
}
func (ln *listener) system() error {
var err error
switch netln := ln.ln.(type) {
default:
ln.close()
2017-11-02 03:36:35 +03:00
return errors.New("network not supported")
2017-10-26 23:43:31 +03:00
case *net.TCPListener:
ln.f, err = netln.File()
case *net.UnixListener:
ln.f, err = netln.File()
}
if err != nil {
ln.close()
return err
}
ln.fd = int(ln.f.Fd())
return syscall.SetNonblock(ln.fd, true)
}
2017-10-31 00:02:10 +03:00
type unixConn struct {
2017-11-07 16:49:33 +03:00
id, fd int
outbuf []byte
outpos int
action Action
opts Options
timeout time.Time
err error
wake bool
writeon bool
detached bool
closed bool
opening bool
}
func (c *unixConn) Timeout() time.Time {
return c.timeout
2017-10-31 00:02:10 +03:00
}
func (c *unixConn) Read(p []byte) (n int, err error) {
2017-11-01 03:44:57 +03:00
return syscall.Read(c.fd, p)
2017-10-31 00:02:10 +03:00
}
func (c *unixConn) Write(p []byte) (n int, err error) {
if c.detached {
2017-11-01 03:44:57 +03:00
if len(c.outbuf) > 0 {
for len(c.outbuf) > 0 {
n, err = syscall.Write(c.fd, c.outbuf)
if n > 0 {
c.outbuf = c.outbuf[n:]
}
if err != nil {
return 0, err
}
2017-10-31 00:02:10 +03:00
}
2017-11-01 03:44:57 +03:00
c.outbuf = nil
}
var tn int
if len(p) > 0 {
for len(p) > 0 {
n, err = syscall.Write(c.fd, p)
if n > 0 {
p = p[n:]
tn += n
}
if err != nil {
return tn, err
}
2017-10-31 00:02:10 +03:00
}
2017-11-01 03:44:57 +03:00
p = nil
2017-10-31 00:02:10 +03:00
}
2017-11-01 03:44:57 +03:00
return tn, nil
2017-10-31 00:02:10 +03:00
}
2017-11-01 03:44:57 +03:00
return syscall.Write(c.fd, p)
2017-10-31 00:02:10 +03:00
}
func (c *unixConn) Close() error {
2017-11-02 15:03:54 +03:00
if c.closed {
return syscall.EINVAL
}
2017-10-31 00:02:10 +03:00
err := syscall.Close(c.fd)
2017-11-02 15:03:54 +03:00
c.fd = -1
c.closed = true
2017-10-31 00:02:10 +03:00
return err
2017-10-26 23:43:31 +03:00
}
func serve(events Events, lns []*listener) error {
2017-10-28 22:23:13 +03:00
p, err := internal.MakePoll()
2017-10-26 23:43:31 +03:00
if err != nil {
return err
}
defer syscall.Close(p)
for _, ln := range lns {
2017-10-28 22:23:13 +03:00
if err := internal.AddRead(p, ln.fd); err != nil {
2017-10-26 23:43:31 +03:00
return err
}
}
var mu sync.Mutex
lock := func() { mu.Lock() }
unlock := func() { mu.Unlock() }
2017-10-31 00:02:10 +03:00
fdconn := make(map[int]*unixConn)
idconn := make(map[int]*unixConn)
2017-11-07 16:49:33 +03:00
timeoutqueue := internal.NewTimeoutQueue()
2017-11-06 13:58:06 +03:00
var id int
2017-11-07 16:49:33 +03:00
ctx := Server{
2017-11-06 13:58:06 +03:00
Wake: func(id int) bool {
var ok = true
var err error
lock()
c := idconn[id]
if c == nil {
ok = false
} else if !c.wake {
c.wake = true
2017-11-07 16:49:33 +03:00
err = internal.AddWrite(p, c.fd, &c.writeon)
2017-11-06 13:58:06 +03:00
}
unlock()
if err != nil {
panic(err)
}
return ok
},
2017-11-07 16:49:33 +03:00
Dial: func(addr string, timeout time.Duration) (int, error) {
network, address, _ := parseAddr(addr)
var taddr net.Addr
2017-11-06 13:58:06 +03:00
var err error
2017-11-07 16:49:33 +03:00
switch network {
2017-11-06 13:58:06 +03:00
default:
2017-11-07 16:49:33 +03:00
return 0, errors.New("invalid network")
case "unix":
case "tcp", "tcp4", "tcp6":
taddr, err = net.ResolveTCPAddr(network, address)
2017-11-06 13:58:06 +03:00
if err != nil {
2017-11-07 16:49:33 +03:00
return 0, err
2017-11-06 13:58:06 +03:00
}
2017-11-07 16:49:33 +03:00
}
var fd int
var sa syscall.Sockaddr
switch taddr := taddr.(type) {
case *net.UnixAddr:
sa = &syscall.SockaddrUnix{Name: taddr.Name}
case *net.TCPAddr:
if len(taddr.IP) == 4 {
var sa4 syscall.SockaddrInet4
copy(sa4.Addr[:], taddr.IP[:])
sa4.Port = taddr.Port
sa = &sa4
fd, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
} else if len(taddr.IP) == 16 {
var sa6 syscall.SockaddrInet6
copy(sa6.Addr[:], taddr.IP[:])
sa6.Port = taddr.Port
sa = &sa6
fd, err = syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, 0)
} else {
return 0, errors.New("invalid network")
2017-11-06 13:58:06 +03:00
}
2017-11-07 16:49:33 +03:00
}
2017-11-06 13:58:06 +03:00
if err != nil {
2017-11-07 16:49:33 +03:00
return 0, err
}
if err := syscall.SetNonblock(fd, true); err != nil {
syscall.Close(fd)
return 0, err
}
err = syscall.Connect(fd, sa)
if err != nil && err != syscall.EINPROGRESS {
syscall.Close(fd)
return 0, err
2017-11-06 13:58:06 +03:00
}
lock()
err = internal.AddRead(p, fd)
if err != nil {
unlock()
2017-11-07 16:49:33 +03:00
syscall.Close(fd)
return 0, err
2017-11-06 13:58:06 +03:00
}
id++
2017-11-07 16:49:33 +03:00
c := &unixConn{id: id, fd: fd, opening: true}
2017-11-06 13:58:06 +03:00
err = internal.AddWrite(p, fd, &c.writeon)
if err != nil {
unlock()
2017-11-07 16:49:33 +03:00
syscall.Close(fd)
return 0, err
}
fdconn[fd] = c
idconn[id] = c
if timeout != 0 {
c.timeout = time.Now().Add(timeout)
timeoutqueue.Push(c)
2017-11-06 13:58:06 +03:00
}
unlock()
2017-11-07 16:49:33 +03:00
return id, nil
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-26 23:43:31 +03:00
}
if events.Serving != nil {
2017-11-06 13:58:06 +03:00
switch events.Serving(ctx) {
2017-10-26 23:43:31 +03:00
case Shutdown:
return nil
}
}
defer func() {
lock()
2017-11-01 03:44:57 +03:00
type fdid struct {
2017-11-07 16:49:33 +03:00
fd, id int
opening bool
2017-11-01 03:44:57 +03:00
}
2017-10-26 23:43:31 +03:00
var fdids []fdid
2017-11-01 03:44:57 +03:00
for fd, c := range fdconn {
2017-11-07 16:49:33 +03:00
fdids = append(fdids, fdid{fd, c.id, c.opening})
2017-10-26 23:43:31 +03:00
}
sort.Slice(fdids, func(i, j int) bool {
return fdids[j].id < fdids[i].id
})
for _, fdid := range fdids {
syscall.Close(fdid.fd)
2017-11-07 16:49:33 +03:00
if fdid.opening {
if events.Opened != nil {
laddr := getlocaladdr(fdid.fd)
raddr := getremoteaddr(fdid.fd)
unlock()
events.Opened(fdid.id, Conn{
Closing: true,
AddrIndex: -1,
LocalAddr: laddr,
RemoteAddr: raddr,
})
lock()
}
}
2017-10-26 23:43:31 +03:00
if events.Closed != nil {
unlock()
2017-11-01 03:44:57 +03:00
events.Closed(fdid.id, nil)
2017-10-26 23:43:31 +03:00
lock()
}
}
2017-11-07 16:49:33 +03:00
syscall.Close(p)
2017-10-26 23:43:31 +03:00
unlock()
}()
var packet [0xFFFF]byte
2017-10-28 22:23:13 +03:00
var evs = internal.MakeEvents(64)
2017-11-07 16:49:33 +03:00
nextTicker := time.Now()
2017-10-26 23:43:31 +03:00
for {
2017-11-07 16:49:33 +03:00
delay := nextTicker.Sub(time.Now())
if delay < 0 {
delay = 0
} else if delay > time.Second/4 {
delay = time.Second / 4
}
pn, err := internal.Wait(p, evs, delay)
2017-10-26 23:43:31 +03:00
if err != nil && err != syscall.EINTR {
return err
}
if events.Tick != nil {
2017-11-07 16:49:33 +03:00
remain := nextTicker.Sub(time.Now())
if remain < 0 {
var tickerDelay time.Duration
2017-10-26 23:43:31 +03:00
var action Action
2017-11-07 16:49:33 +03:00
if events.Tick != nil {
tickerDelay, action = events.Tick()
if action == Shutdown {
return nil
}
} else {
tickerDelay = time.Hour
}
nextTicker = time.Now().Add(tickerDelay + remain)
}
}
// check timeouts
if timeoutqueue.Len() > 0 {
var count int
now := time.Now()
for {
v := timeoutqueue.Peek()
if v == nil {
break
}
c := v.(*unixConn)
if now.After(v.Timeout()) {
timeoutqueue.Pop()
if _, ok := idconn[c.id]; ok {
delete(idconn, c.id)
delete(fdconn, c.fd)
syscall.Close(c.fd)
if events.Opened != nil {
laddr := getlocaladdr(c.fd)
raddr := getremoteaddr(c.fd)
events.Opened(c.id, Conn{
Closing: true,
AddrIndex: -1,
LocalAddr: laddr,
RemoteAddr: raddr,
})
}
if events.Closed != nil {
events.Closed(c.id, syscall.ETIMEDOUT)
}
count++
}
} else {
break
2017-10-26 23:43:31 +03:00
}
2017-11-07 16:49:33 +03:00
}
if count > 0 {
// invalidate the current events and wait for more
continue
2017-10-26 23:43:31 +03:00
}
}
lock()
for i := 0; i < pn; i++ {
var in []byte
2017-10-31 00:02:10 +03:00
var c *unixConn
2017-10-26 23:43:31 +03:00
var nfd int
var n int
var out []byte
var ln *listener
2017-10-29 23:09:40 +03:00
var lnidx int
2017-10-28 22:23:13 +03:00
var fd = internal.GetFD(evs, i)
2017-10-29 23:09:40 +03:00
for lnidx, ln = range lns {
2017-10-26 23:43:31 +03:00
if fd == ln.fd {
goto accept
}
}
2017-11-07 16:49:33 +03:00
ln = nil
2017-10-26 23:43:31 +03:00
c = fdconn[fd]
if c == nil {
syscall.Close(fd)
goto next
}
2017-11-07 16:49:33 +03:00
if c.opening {
2017-11-06 13:58:06 +03:00
2017-11-07 16:49:33 +03:00
lnidx = -1
goto opened
2017-11-06 13:58:06 +03:00
}
2017-10-26 23:43:31 +03:00
goto read
accept:
2017-11-07 16:49:33 +03:00
nfd, _, err = syscall.Accept(fd)
2017-10-26 23:43:31 +03:00
if err != nil {
goto next
}
if err = syscall.SetNonblock(nfd, true); err != nil {
goto fail
}
2017-10-28 22:23:13 +03:00
if err = internal.AddRead(p, nfd); err != nil {
2017-10-26 23:43:31 +03:00
goto fail
}
id++
2017-11-07 16:49:33 +03:00
c = &unixConn{id: id, fd: nfd}
2017-10-26 23:43:31 +03:00
fdconn[nfd] = c
idconn[id] = c
2017-11-07 16:49:33 +03:00
goto opened
opened:
2017-10-26 23:43:31 +03:00
if events.Opened != nil {
2017-11-07 16:49:33 +03:00
laddr := getlocaladdr(fd)
raddr := getremoteaddr(fd)
2017-10-26 23:43:31 +03:00
unlock()
2017-11-07 16:49:33 +03:00
out, c.opts, c.action = events.Opened(c.id, Conn{
AddrIndex: lnidx,
LocalAddr: laddr,
RemoteAddr: raddr,
})
2017-10-26 23:43:31 +03:00
lock()
if c.opts.TCPKeepAlive > 0 {
2017-11-07 16:49:33 +03:00
internal.SetKeepAlive(c.fd, int(c.opts.TCPKeepAlive/time.Second))
2017-10-26 23:43:31 +03:00
}
if len(out) > 0 {
c.outbuf = append(c.outbuf, out...)
}
}
2017-11-07 16:49:33 +03:00
if c.opening {
c.opening = false
goto next
}
2017-10-26 23:43:31 +03:00
goto write
read:
if c.action != None {
goto write
}
if c.wake {
c.wake = false
} else {
2017-10-31 00:02:10 +03:00
n, err = c.Read(packet[:])
2017-10-26 23:43:31 +03:00
if n == 0 || err != nil {
if err == syscall.EAGAIN {
goto write
}
2017-11-01 03:44:57 +03:00
c.err = err
2017-10-26 23:43:31 +03:00
goto close
}
in = append([]byte{}, packet[:n]...)
}
if events.Data != nil {
unlock()
out, c.action = events.Data(c.id, in)
lock()
}
if len(out) > 0 {
c.outbuf = append(c.outbuf, out...)
}
goto write
write:
if len(c.outbuf)-c.outpos > 0 {
if events.Prewrite != nil {
unlock()
action := events.Prewrite(c.id, len(c.outbuf[c.outpos:]))
lock()
2017-10-28 03:01:03 +03:00
if action == Shutdown {
2017-10-26 23:43:31 +03:00
c.action = Shutdown
}
}
2017-10-31 00:02:10 +03:00
n, err = c.Write(c.outbuf[c.outpos:])
2017-10-26 23:43:31 +03:00
if events.Postwrite != nil {
amount := n
if amount < 0 {
amount = 0
}
unlock()
action := events.Postwrite(c.id, amount, len(c.outbuf)-c.outpos-amount)
lock()
2017-10-28 03:01:03 +03:00
if action == Shutdown {
2017-10-26 23:43:31 +03:00
c.action = Shutdown
}
}
if n == 0 || err != nil {
if c.action == Shutdown {
goto close
}
if err == syscall.EAGAIN {
2017-11-07 16:49:33 +03:00
if err = internal.AddWrite(p, c.fd, &c.writeon); err != nil {
2017-10-26 23:43:31 +03:00
goto fail
}
goto next
}
2017-11-01 03:44:57 +03:00
c.err = err
2017-10-26 23:43:31 +03:00
goto close
}
c.outpos += n
if len(c.outbuf)-c.outpos == 0 {
c.outpos = 0
2017-11-03 04:31:36 +03:00
c.outbuf = c.outbuf[:0]
2017-10-26 23:43:31 +03:00
}
}
if c.action == Shutdown {
goto close
}
if len(c.outbuf)-c.outpos == 0 {
if !c.wake {
2017-11-07 16:49:33 +03:00
if err = internal.DelWrite(p, c.fd, &c.writeon); err != nil {
2017-10-26 23:43:31 +03:00
goto fail
}
}
if c.action != None {
goto close
}
} else {
2017-11-07 16:49:33 +03:00
if err = internal.AddWrite(p, c.fd, &c.writeon); err != nil {
2017-10-26 23:43:31 +03:00
goto fail
}
}
goto next
close:
delete(fdconn, c.fd)
delete(idconn, c.id)
2017-11-07 16:49:33 +03:00
//delete(idtimeout, c.id)
2017-10-26 23:43:31 +03:00
if c.action == Detach {
if events.Detached != nil {
2017-10-31 00:02:10 +03:00
c.detached = true
if len(c.outbuf)-c.outpos > 0 {
c.outbuf = append(c.outbuf[:0], c.outbuf[c.outpos:]...)
} else {
c.outbuf = nil
}
c.outpos = 0
syscall.SetNonblock(c.fd, false)
2017-10-26 23:43:31 +03:00
unlock()
2017-10-31 00:02:10 +03:00
c.action = events.Detached(c.id, c)
2017-10-26 23:43:31 +03:00
lock()
if c.action == Shutdown {
goto fail
}
goto next
}
}
syscall.Close(c.fd)
if events.Closed != nil {
unlock()
2017-11-01 03:44:57 +03:00
action := events.Closed(c.id, c.err)
2017-10-26 23:43:31 +03:00
lock()
if action == Shutdown {
c.action = Shutdown
}
}
if c.action == Shutdown {
err = nil
goto fail
}
goto next
fail:
unlock()
return err
next:
}
unlock()
}
}
2017-11-07 16:49:33 +03:00
func getlocaladdr(fd int) net.Addr {
2017-10-29 23:09:40 +03:00
sa, _ := syscall.Getsockname(fd)
2017-11-07 16:49:33 +03:00
return getaddr(sa)
2017-10-29 23:09:40 +03:00
}
2017-11-07 16:49:33 +03:00
func getremoteaddr(fd int) net.Addr {
sa, _ := syscall.Getpeername(fd)
return getaddr(sa)
}
func getaddr(sa syscall.Sockaddr) net.Addr {
switch sa := sa.(type) {
default:
return nil
case *syscall.SockaddrInet4:
return &net.TCPAddr{IP: net.IP(sa.Addr[:]), Port: sa.Port}
case *syscall.SockaddrInet6:
return &net.TCPAddr{IP: net.IP(sa.Addr[:]), Port: sa.Port, Zone: strconv.FormatInt(int64(sa.ZoneId), 10)}
case *syscall.SockaddrUnix:
return &net.UnixAddr{Net: "unix", Name: sa.Name}
2017-10-26 23:43:31 +03:00
}
}