mirror of https://github.com/tidwall/evio.git
314 lines
6.2 KiB
Go
314 lines
6.2 KiB
Go
package evio
|
|
|
|
import (
|
|
"net"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// servenet uses the stdlib net package instead of syscalls.
|
|
func servenet(events Events, lns []*listener) error {
|
|
type cconn struct {
|
|
id int
|
|
conn net.Conn
|
|
outbuf []byte
|
|
outpos int
|
|
action Action
|
|
wake bool
|
|
detached bool
|
|
}
|
|
type fail struct{ err error }
|
|
type accept struct {
|
|
lnidx int
|
|
conn net.Conn
|
|
}
|
|
type read struct {
|
|
conn net.Conn
|
|
packet []byte
|
|
}
|
|
type tick struct{}
|
|
type write struct{ conn net.Conn }
|
|
type close struct{ conn net.Conn }
|
|
var cond = sync.NewCond(&sync.Mutex{})
|
|
lock := func() { cond.L.Lock() }
|
|
unlock := func() { cond.L.Unlock() }
|
|
var evs []interface{}
|
|
send := func(ev interface{}, broadcast bool) {
|
|
if broadcast {
|
|
lock()
|
|
}
|
|
if _, ok := ev.(fail); ok {
|
|
evs = evs[:0]
|
|
}
|
|
evs = append(evs, ev)
|
|
if broadcast {
|
|
cond.Broadcast()
|
|
unlock()
|
|
}
|
|
}
|
|
for i, ln := range lns {
|
|
go func(lnidx int, ln net.Listener) {
|
|
for {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
send(fail{err}, true)
|
|
return
|
|
}
|
|
send(accept{lnidx, conn}, true)
|
|
go func(conn net.Conn) {
|
|
defer send(close{conn}, true)
|
|
var packet [0xFFFF]byte
|
|
for {
|
|
n, err := conn.Read(packet[:])
|
|
if err != nil && !istimeout(err) {
|
|
return
|
|
} else if n > 0 {
|
|
send(read{conn, append([]byte{}, packet[:n]...)}, true)
|
|
}
|
|
}
|
|
}(conn)
|
|
}
|
|
}(i, ln.ln)
|
|
}
|
|
var id int
|
|
var connconn = make(map[net.Conn]*cconn)
|
|
var idconn = make(map[int]*cconn)
|
|
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
|
|
send(read{c.conn, nil}, false)
|
|
cond.Broadcast()
|
|
}
|
|
unlock()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ok
|
|
|
|
}
|
|
if events.Serving != nil {
|
|
if events.Serving(wake) == Shutdown {
|
|
return nil
|
|
}
|
|
}
|
|
defer func() {
|
|
lock()
|
|
type connid struct {
|
|
conn net.Conn
|
|
id int
|
|
}
|
|
var connids []connid
|
|
for id, conn := range idconn {
|
|
connids = append(connids, connid{conn.conn, id})
|
|
}
|
|
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 {
|
|
unlock()
|
|
events.Closed(connid.id)
|
|
lock()
|
|
}
|
|
}
|
|
unlock()
|
|
}()
|
|
var tickerDelay time.Duration
|
|
if events.Tick != nil {
|
|
go func() {
|
|
for {
|
|
send(tick{}, true)
|
|
lock()
|
|
d := tickerDelay
|
|
unlock()
|
|
time.Sleep(d)
|
|
}
|
|
}()
|
|
}
|
|
lock()
|
|
again:
|
|
for {
|
|
for i := 0; i < len(evs); i++ {
|
|
ev := evs[i]
|
|
switch ev := ev.(type) {
|
|
case nil:
|
|
case tick:
|
|
if events.Tick != nil {
|
|
unlock()
|
|
delay, action := events.Tick()
|
|
lock()
|
|
tickerDelay = delay
|
|
if action == Shutdown {
|
|
send(fail{nil}, false)
|
|
continue again
|
|
}
|
|
|
|
}
|
|
case accept:
|
|
id++
|
|
c := &cconn{id: id, conn: ev.conn}
|
|
connconn[ev.conn] = c
|
|
idconn[id] = c
|
|
if events.Opened != nil {
|
|
unlock()
|
|
out, opts, action := events.Opened(id,
|
|
Addr{ev.lnidx, ev.conn.LocalAddr(), ev.conn.RemoteAddr()})
|
|
lock()
|
|
if opts.TCPKeepAlive > 0 {
|
|
if conn, ok := ev.conn.(*net.TCPConn); ok {
|
|
if err := conn.SetKeepAlive(true); err != nil {
|
|
send(fail{err}, false)
|
|
continue again
|
|
}
|
|
if err := conn.SetKeepAlivePeriod(opts.TCPKeepAlive); err != nil {
|
|
send(fail{err}, false)
|
|
continue again
|
|
}
|
|
}
|
|
}
|
|
if len(out) > 0 {
|
|
c.outbuf = append(c.outbuf, out...)
|
|
}
|
|
c.action = action
|
|
if c.action != None || len(c.outbuf) > 0 {
|
|
send(write{c.conn}, false)
|
|
}
|
|
}
|
|
case read:
|
|
var out []byte
|
|
var in []byte
|
|
c := connconn[ev.conn]
|
|
if c == nil {
|
|
continue
|
|
}
|
|
if c.action != None {
|
|
send(write{c.conn}, false)
|
|
continue
|
|
}
|
|
if c.wake {
|
|
c.wake = false
|
|
} else {
|
|
in = ev.packet
|
|
}
|
|
if events.Data != nil {
|
|
unlock()
|
|
out, c.action = events.Data(c.id, in)
|
|
lock()
|
|
}
|
|
if len(out) > 0 {
|
|
c.outbuf = append(c.outbuf, out...)
|
|
}
|
|
if c.action != None || len(c.outbuf) > 0 {
|
|
send(write{c.conn}, false)
|
|
}
|
|
case write:
|
|
c := connconn[ev.conn]
|
|
if c == nil {
|
|
continue
|
|
}
|
|
if len(c.outbuf)-c.outpos > 0 {
|
|
if events.Prewrite != nil {
|
|
unlock()
|
|
action := events.Prewrite(c.id, len(c.outbuf[c.outpos:]))
|
|
lock()
|
|
if action > c.action {
|
|
c.action = action
|
|
}
|
|
}
|
|
c.conn.SetWriteDeadline(time.Now().Add(time.Millisecond))
|
|
n, err := c.conn.Write(c.outbuf[c.outpos:])
|
|
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()
|
|
if action > c.action {
|
|
c.action = action
|
|
}
|
|
}
|
|
if err != nil {
|
|
if c.action == Shutdown {
|
|
send(close{c.conn}, false)
|
|
} else if istimeout(err) {
|
|
send(write{c.conn}, false)
|
|
} else {
|
|
send(close{c.conn}, false)
|
|
}
|
|
continue
|
|
}
|
|
c.outpos += n
|
|
if len(c.outbuf)-c.outpos == 0 {
|
|
c.outbuf = c.outbuf[:0]
|
|
c.outpos = 0
|
|
}
|
|
}
|
|
if c.action == Shutdown {
|
|
send(close{c.conn}, false)
|
|
} else if len(c.outbuf)-c.outpos == 0 {
|
|
if c.action != None {
|
|
send(close{c.conn}, false)
|
|
}
|
|
} else {
|
|
send(write{c.conn}, false)
|
|
}
|
|
case close:
|
|
c := connconn[ev.conn]
|
|
if c == nil {
|
|
continue
|
|
}
|
|
delete(connconn, c.conn)
|
|
delete(idconn, c.id)
|
|
if c.action == Detach {
|
|
c.detached = true
|
|
if events.Detached != nil {
|
|
unlock()
|
|
c.action = events.Detached(c.id, c.conn)
|
|
lock()
|
|
if c.action == Shutdown {
|
|
send(fail{nil}, false)
|
|
continue again
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
c.conn.Close()
|
|
if events.Closed != nil {
|
|
unlock()
|
|
action := events.Closed(c.id)
|
|
lock()
|
|
if action == Shutdown {
|
|
c.action = Shutdown
|
|
}
|
|
}
|
|
if c.action == Shutdown {
|
|
send(fail{nil}, false)
|
|
continue again
|
|
}
|
|
case fail:
|
|
unlock()
|
|
return ev.err
|
|
}
|
|
}
|
|
evs = evs[:0]
|
|
cond.Wait()
|
|
}
|
|
}
|
|
|
|
func istimeout(err error) bool {
|
|
if err, ok := err.(net.Error); ok && err.Timeout() {
|
|
return true
|
|
}
|
|
return false
|
|
}
|