Add a memory pool

Re-use memory for UDP queries.
This commit is contained in:
Miek Gieben 2014-01-05 13:39:33 +00:00
parent 7a27e05cb7
commit 49ece3e490
2 changed files with 78 additions and 6 deletions

53
pool.go Normal file
View File

@ -0,0 +1,53 @@
package dns
import (
"container/list"
"time"
)
func mkBuf(size int) []byte { return make([]byte, size) }
type queued struct {
when time.Time
buf []byte
}
func pool(size int) (get, give chan []byte) {
get = make(chan []byte)
give = make(chan []byte)
go func() {
q := new(list.List)
for {
e := q.Front()
if e == nil {
q.PushFront(queued{when: time.Now(), buf: mkBuf(size)})
e = q.Front()
}
timeout := time.NewTimer(time.Minute)
select {
case b := <-give:
timeout.Stop()
q.PushFront(queued{when: time.Now(), buf: b})
case get <- e.Value.(queued).buf:
timeout.Stop()
q.Remove(e)
case <-timeout.C:
e := q.Front()
for e != nil {
n := e.Next()
if time.Since(e.Value.(queued).when) > time.Minute {
q.Remove(e)
e.Value = nil
}
e = n
}
}
}
}()
return
}

View File

@ -202,6 +202,8 @@ type Server struct {
WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections
IdleTimeout func() time.Duration // TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966)
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>
Pool bool // if true use a pool to recycle buffers for UDP queries
get, give chan []byte // channels for the pool
}
// ListenAndServe starts a nameserver on the configured address in *Server.
@ -210,6 +212,9 @@ func (srv *Server) ListenAndServe() error {
if addr == "" {
addr = ":domain"
}
if srv.Pool {
srv.get, srv.give = pool(srv.UDPSize)
}
switch srv.Net {
case "tcp", "tcp4", "tcp6":
a, e := net.ResolveTCPAddr(srv.Net, addr)
@ -279,6 +284,7 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
for {
m, a, e := srv.readUDP(l, rtimeout)
if e != nil {
println(e.Error())
continue
}
go srv.serve(a, handler, m, l, nil)
@ -292,7 +298,12 @@ func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, t *net
q := 0
Redo:
req := new(Msg)
if req.Unpack(m) != nil { // Send a FormatError back
err := req.Unpack(m)
println("GONNT")
if srv.Pool && u != nil {
srv.give <- m[:srv.UDPSize]
}
if err != nil { // Send a FormatError back
x := new(Msg)
x.SetRcodeFormatError(req)
w.WriteMsg(x)
@ -347,11 +358,11 @@ func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, er
if err != nil {
return nil, err
}
return nil, ErrConn
return nil, ErrShortRead
}
length, _ := unpackUint16(l, 0)
if length == 0 {
return nil, ErrConn
return nil, ErrShortRead
}
m := make([]byte, int(length))
n, err = conn.Read(m[:int(length)])
@ -359,7 +370,7 @@ func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, er
if err != nil {
return nil, err
}
return nil, ErrConn
return nil, ErrShortRead
}
i := n
for i < int(length) {
@ -376,10 +387,18 @@ func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, er
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, net.Addr, error) {
conn.SetReadDeadline(time.Now().Add(timeout))
m := make([]byte, srv.UDPSize)
var m []byte
if srv.Pool {
m = <-srv.get
} else {
m = make([]byte, srv.UDPSize)
}
n, a, e := conn.ReadFromUDP(m)
if e != nil || n == 0 {
return nil, nil, ErrConn
if e != nil {
return nil, nil, e
}
return nil, nil, ErrShortRead
}
m = m[:n]
return m, a, nil