Browse Source

Use refcounted buffers for IO.

Need to use buffers multiple times when broadcasting.
pull/6/head
Joachim Bauch 12 years ago
parent
commit
f185864ca9
  1. 98
      src/app/spreed-speakfreely-server/buffercache.go
  2. 23
      src/app/spreed-speakfreely-server/connection.go
  3. 8
      src/app/spreed-speakfreely-server/hub.go
  4. 8
      src/app/spreed-speakfreely-server/roomworker.go
  5. 20
      src/app/spreed-speakfreely-server/server.go

98
src/app/spreed-speakfreely-server/buffercache.go

@ -22,24 +22,100 @@ package main
import ( import (
"bytes" "bytes"
"io"
"sync/atomic"
) )
type Buffer interface {
// bytes.Buffer
Reset()
Bytes() []byte
ReadFrom(r io.Reader) (n int64, err error)
// io.Writer
Write(p []byte) (n int, err error)
// provide direct access
GetBuffer() *bytes.Buffer
// refcounting
Incref()
Decref()
}
type BufferCache interface { type BufferCache interface {
Push(buffer *bytes.Buffer) New() Buffer
Wrap(data []byte) Buffer
}
type cachedBuffer struct {
bytes.Buffer
refcnt int32
cache *bufferCache
}
func (b *cachedBuffer) GetBuffer() *bytes.Buffer {
return &b.Buffer
}
Pop() *bytes.Buffer func (b *cachedBuffer) Incref() {
atomic.AddInt32(&b.refcnt, 1)
}
func (b *cachedBuffer) Decref() {
if atomic.AddInt32(&b.refcnt, -1) == 0 {
b.cache.push(b)
}
}
type directBuffer struct {
buf *bytes.Buffer
refcnt int32
cache *bufferCache
}
func (b *directBuffer) Reset() {
b.buf.Reset()
}
func (b *directBuffer) Bytes() []byte {
return b.buf.Bytes()
}
func (b *directBuffer) ReadFrom(r io.Reader) (n int64, err error) {
return b.buf.ReadFrom(r)
}
func (b *directBuffer) Write(p []byte) (n int, err error) {
return b.buf.Write(p)
}
func (b *directBuffer) GetBuffer() *bytes.Buffer {
return b.buf
}
func (b *directBuffer) Incref() {
atomic.AddInt32(&b.refcnt, 1)
}
func (b *directBuffer) Decref() {
if atomic.AddInt32(&b.refcnt, -1) == 0 {
b.cache.push(b)
}
} }
type bufferCache struct { type bufferCache struct {
buffers chan *bytes.Buffer buffers chan Buffer
initialSize int initialSize int
} }
func NewBufferCache(count int, initialSize int) BufferCache { func NewBufferCache(count int, initialSize int) BufferCache {
return &bufferCache{buffers: make(chan *bytes.Buffer, count), initialSize: initialSize} return &bufferCache{buffers: make(chan Buffer, count), initialSize: initialSize}
} }
func (cache *bufferCache) Push(buffer *bytes.Buffer) { func (cache *bufferCache) push(buffer Buffer) {
if buffer, ok := buffer.(*directBuffer); ok {
buffer.Reset()
return
}
buffer.Reset() buffer.Reset()
select { select {
case cache.buffers <- buffer: case cache.buffers <- buffer:
@ -51,15 +127,21 @@ func (cache *bufferCache) Push(buffer *bytes.Buffer) {
} }
} }
func (cache *bufferCache) Pop() *bytes.Buffer { func (cache *bufferCache) New() Buffer {
var buffer *bytes.Buffer var buffer Buffer
select { select {
case buffer = <-cache.buffers: case buffer = <-cache.buffers:
// reuse existing buffer // reuse existing buffer
buffer.Incref()
break break
default: default:
buffer = bytes.NewBuffer(make([]byte, 0, cache.initialSize)) buffer = &cachedBuffer{refcnt: 1, cache: cache}
buffer.GetBuffer().Grow(cache.initialSize)
break break
} }
return buffer return buffer
} }
func (cache *bufferCache) Wrap(data []byte) Buffer {
return &directBuffer{refcnt: 1, cache: cache, buf: bytes.NewBuffer(data)}
}

23
src/app/spreed-speakfreely-server/connection.go

@ -59,7 +59,7 @@ type Connection struct {
// Data handling. // Data handling.
condition *sync.Cond condition *sync.Cond
queue [][]byte queue []Buffer
mutex sync.Mutex mutex sync.Mutex
isClosed bool isClosed bool
@ -82,7 +82,7 @@ func NewConnection(h *Hub, ws *websocket.Conn, remoteAddr string) *Connection {
RemoteAddr: remoteAddr, RemoteAddr: remoteAddr,
} }
c.condition = sync.NewCond(&c.mutex) c.condition = sync.NewCond(&c.mutex)
c.queue = make([][]byte, 0) c.queue = make([]Buffer, 0)
return c return c
@ -132,7 +132,7 @@ func (c *Connection) unregister() {
c.h.unregisterHandler(c) c.h.unregisterHandler(c)
} }
func (c *Connection) readAll(dest *bytes.Buffer, r io.Reader) error { func (c *Connection) readAll(dest Buffer, r io.Reader) error {
var err error var err error
defer func() { defer func() {
e := recover() e := recover()
@ -146,7 +146,6 @@ func (c *Connection) readAll(dest *bytes.Buffer, r io.Reader) error {
} }
}() }()
dest.Reset()
_, err = dest.ReadFrom(r) _, err = dest.ReadFrom(r)
return err return err
} }
@ -172,10 +171,10 @@ func (c *Connection) readPump() {
} }
switch op { switch op {
case websocket.TextMessage: case websocket.TextMessage:
message := c.h.buffers.Pop() message := c.h.buffers.New()
err = c.readAll(message, r) err = c.readAll(message, r)
if err != nil { if err != nil {
c.h.buffers.Push(message) message.Decref()
break break
} }
now := time.Now() now := time.Now()
@ -189,8 +188,8 @@ func (c *Connection) readPump() {
} }
} }
times.PushBack(now) times.PushBack(now)
c.h.server.OnText(c, message.Bytes()) c.h.server.OnText(c, message)
c.h.buffers.Push(message) message.Decref()
} }
} }
@ -199,7 +198,7 @@ func (c *Connection) readPump() {
} }
// Write message to outbound queue. // Write message to outbound queue.
func (c *Connection) send(message []byte) { func (c *Connection) send(message Buffer) {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
@ -211,6 +210,7 @@ func (c *Connection) send(message []byte) {
log.Println("Outbound queue overflow", c.Idx, len(c.queue)) log.Println("Outbound queue overflow", c.Idx, len(c.queue))
return return
} }
message.Incref()
c.queue = append(c.queue, message) c.queue = append(c.queue, message)
c.condition.Signal() c.condition.Signal()
@ -259,15 +259,18 @@ func (c *Connection) writePump() {
c.mutex.Unlock() c.mutex.Unlock()
if err := c.ping(); err != nil { if err := c.ping(); err != nil {
log.Println("Error while sending ping", c.Idx, err) log.Println("Error while sending ping", c.Idx, err)
message.Decref()
goto cleanup goto cleanup
} }
} else { } else {
c.mutex.Unlock() c.mutex.Unlock()
} }
if err := c.write(websocket.TextMessage, message); err != nil { if err := c.write(websocket.TextMessage, message.Bytes()); err != nil {
log.Println("Error while writing", c.Idx, err) log.Println("Error while writing", c.Idx, err)
message.Decref()
goto cleanup goto cleanup
} }
message.Decref()
c.mutex.Lock() c.mutex.Lock()
} }
if ping { if ping {

8
src/app/spreed-speakfreely-server/hub.go

@ -42,7 +42,7 @@ const (
type MessageRequest struct { type MessageRequest struct {
From string From string
To string To string
Message []byte Message Buffer
Id string Id string
} }
@ -289,12 +289,16 @@ func (h *Hub) unicastHandler(m *MessageRequest) {
func (h *Hub) aliveHandler(c *Connection, alive *DataAlive) { func (h *Hub) aliveHandler(c *Connection, alive *DataAlive) {
aliveJson, err := json.Marshal(&DataOutgoing{From: c.Id, Data: alive}) aliveJson := h.buffers.New()
encoder := json.NewEncoder(aliveJson)
err := encoder.Encode(&DataOutgoing{From: c.Id, Data: alive})
if err != nil { if err != nil {
log.Println("Alive error while encoding JSON", err) log.Println("Alive error while encoding JSON", err)
aliveJson.Decref()
return return
} }
c.send(aliveJson) c.send(aliveJson)
aliveJson.Decref()
} }

8
src/app/spreed-speakfreely-server/roomworker.go

@ -164,12 +164,16 @@ func (r *RoomWorker) usersHandler(c *Connection) {
} }
} }
users.Users = ul users.Users = ul
usersJson, err := json.Marshal(&DataOutgoing{From: c.Id, Data: users}) usersJson := c.h.buffers.New()
encoder := json.NewEncoder(usersJson)
err := encoder.Encode(&DataOutgoing{From: c.Id, Data: users})
if err != nil { if err != nil {
log.Println("Users error while encoding JSON", err) log.Println("Users error while encoding JSON", err)
usersJson.Decref()
return return
} }
c.send(usersJson) c.send(usersJson)
usersJson.Decref()
} }
@ -189,9 +193,11 @@ func (r *RoomWorker) broadcastHandler(m *MessageRequest) {
} }
//fmt.Printf("%s\n", m.Message) //fmt.Printf("%s\n", m.Message)
ec.send(m.Message) ec.send(m.Message)
m.Message.Decref()
} }
} }
m.Message.Incref()
r.Run(worker) r.Run(worker)
} }

20
src/app/spreed-speakfreely-server/server.go

@ -53,11 +53,11 @@ func (s *Server) OnUnregister(c *Connection) {
} }
} }
func (s *Server) OnText(c *Connection, b []byte) { func (s *Server) OnText(c *Connection, b Buffer) {
//log.Printf("OnText from %d: %s\n", c.id, b) //log.Printf("OnText from %d: %s\n", c.id, b)
var msg DataIncoming var msg DataIncoming
err := json.Unmarshal(b, &msg) err := json.Unmarshal(b.Bytes(), &msg)
if err != nil { if err != nil {
log.Println("OnText error while decoding JSON", err) log.Println("OnText error while decoding JSON", err)
log.Printf("JSON:\n%s\n", b) log.Printf("JSON:\n%s\n", b)
@ -146,20 +146,18 @@ func (s *Server) OnText(c *Connection, b []byte) {
func (s *Server) Unicast(c *Connection, to string, m interface{}) { func (s *Server) Unicast(c *Connection, to string, m interface{}) {
b := c.h.buffers.Pop() b := c.h.buffers.New()
encoder := json.NewEncoder(b) encoder := json.NewEncoder(b)
err := encoder.Encode(&DataOutgoing{From: c.Id, To: to, Data: m}) err := encoder.Encode(&DataOutgoing{From: c.Id, To: to, Data: m})
if err != nil { if err != nil {
c.h.buffers.Push(b) b.Decref()
log.Println("Unicast error while encoding JSON", err) log.Println("Unicast error while encoding JSON", err)
return return
} }
var msg = &MessageRequest{From: c.Id, To: to, Message: b.Bytes()} var msg = &MessageRequest{From: c.Id, To: to, Message: b}
c.h.unicastHandler(msg) c.h.unicastHandler(msg)
c.h.buffers.Push(b) b.Decref()
} }
func (s *Server) Alive(c *Connection, alive *DataAlive) { func (s *Server) Alive(c *Connection, alive *DataAlive) {
@ -183,16 +181,18 @@ func (s *Server) Broadcast(c *Connection, m interface{}) {
return return
} }
buffer := c.h.buffers.Wrap(b)
if c.h.isGlobalRoomid(c.Roomid) { if c.h.isGlobalRoomid(c.Roomid) {
c.h.RunForAllRooms(func(room *RoomWorker) { c.h.RunForAllRooms(func(room *RoomWorker) {
var msg = &MessageRequest{From: c.Id, Message: b, Id: room.Id} var msg = &MessageRequest{From: c.Id, Message: buffer, Id: room.Id}
room.broadcastHandler(msg) room.broadcastHandler(msg)
}) })
} else { } else {
var msg = &MessageRequest{From: c.Id, Message: b, Id: c.Roomid} var msg = &MessageRequest{From: c.Id, Message: buffer, Id: c.Roomid}
room := c.h.GetRoom(c.Roomid) room := c.h.GetRoom(c.Roomid)
room.broadcastHandler(msg) room.broadcastHandler(msg)
} }
buffer.Decref()
} }

Loading…
Cancel
Save