fine mux, add goroutine pool

This commit is contained in:
ffdfgdfg
2019-11-09 23:02:29 +08:00
parent 5f58c34c8b
commit f362c96e1e
7 changed files with 352 additions and 228 deletions

View File

@@ -4,6 +4,7 @@ import (
"errors"
"io"
"net"
"runtime"
"sync"
"sync/atomic"
"time"
@@ -57,7 +58,12 @@ func (s *conn) Read(buf []byte) (n int, err error) {
return 0, nil
}
// waiting for takeout from receive window finish or timeout
//now := time.Now()
n, err = s.receiveWindow.Read(buf, s.connId)
//t := time.Now().Sub(now)
//if t.Seconds() > 0.5 {
//logs.Warn("conn read long", n, t.Seconds())
//}
//var errstr string
//if err == nil {
// errstr = "err:nil"
@@ -82,7 +88,12 @@ func (s *conn) Write(buf []byte) (n int, err error) {
return 0, nil
}
//logs.Warn("write buf", len(buf))
//now := time.Now()
n, err = s.sendWindow.WriteFull(buf, s.connId)
//t := time.Now().Sub(now)
//if t.Seconds() > 0.5 {
// logs.Warn("conn write long", n, t.Seconds())
//}
return
}
@@ -133,11 +144,25 @@ func (s *conn) SetWriteDeadline(t time.Time) error {
}
type window struct {
off uint32
maxSize uint32
closeOp bool
closeOpCh chan struct{}
mux *Mux
off uint32
maxSize uint32
closeOp bool
closeOpCh chan struct{}
remainingWait uint64
mux *Mux
}
func (Self *window) unpack(ptrs uint64) (remaining, wait uint32) {
const mask = 1<<dequeueBits - 1
remaining = uint32((ptrs >> dequeueBits) & mask)
wait = uint32(ptrs & mask)
return
}
func (Self *window) pack(remaining, wait uint32) uint64 {
const mask = 1<<dequeueBits - 1
return (uint64(remaining) << dequeueBits) |
uint64(wait&mask)
}
func (Self *window) New() {
@@ -153,37 +178,30 @@ func (Self *window) CloseWindow() {
}
type ReceiveWindow struct {
bufQueue ReceiveWindowQueue
element *ListElement
readLength uint32
//readOp chan struct{}
readWait bool
windowFull uint32
count int8
//bw *bandwidth
once sync.Once
bufQueue ReceiveWindowQueue
element *common.ListElement
count int8
once sync.Once
window
}
func (Self *ReceiveWindow) New(mux *Mux) {
// initial a window for receive
//Self.readOp = make(chan struct{})
Self.bufQueue.New()
//Self.bw = new(bandwidth)
Self.element = new(ListElement)
Self.maxSize = 8192
Self.element = common.ListElementPool.Get()
Self.maxSize = 4096
Self.mux = mux
Self.window.New()
}
func (Self *ReceiveWindow) remainingSize() (n uint32) {
func (Self *ReceiveWindow) remainingSize(delta uint16) (n uint32) {
// receive window remaining
return atomic.LoadUint32(&Self.maxSize) - Self.bufQueue.Len()
}
func (Self *ReceiveWindow) readSize() (n uint32) {
// acknowledge the size already read
return atomic.SwapUint32(&Self.readLength, 0)
l := int64(atomic.LoadUint32(&Self.maxSize)) - int64(Self.bufQueue.Len())
l -= int64(delta)
if l > 0 {
n = uint32(l)
}
return
}
func (Self *ReceiveWindow) calcSize() {
@@ -194,8 +212,9 @@ func (Self *ReceiveWindow) calcSize() {
if n < 8192 {
n = 8192
}
if n < Self.bufQueue.Len() {
n = Self.bufQueue.Len()
bufLen := Self.bufQueue.Len()
if n < bufLen {
n = bufLen
}
// set the minimal size
if n > 2*Self.maxSize {
@@ -210,28 +229,39 @@ func (Self *ReceiveWindow) calcSize() {
Self.count = -10
}
Self.count += 1
return
}
func (Self *ReceiveWindow) Write(buf []byte, l uint16, part bool, id int32) (err error) {
if Self.closeOp {
return errors.New("conn.receiveWindow: write on closed window")
}
element := new(ListElement)
err = element.New(buf, l, part)
element, err := NewListElement(buf, l, part)
//logs.Warn("push the buf", len(buf), l, (&element).l)
if err != nil {
return
}
Self.bufQueue.Push(element) // must push data before allow read
//logs.Warn("read session calc size ", Self.maxSize)
// calculating the receive window size
Self.calcSize()
//logs.Warn("read session calc size finish", Self.maxSize)
if Self.remainingSize() == 0 {
atomic.StoreUint32(&Self.windowFull, 1)
//logs.Warn("window full true", Self.windowFull)
Self.calcSize() // calculate the max window size
var wait uint32
start:
ptrs := atomic.LoadUint64(&Self.remainingWait)
_, wait = Self.unpack(ptrs)
newRemaining := Self.remainingSize(l)
// calculate the remaining window size now, plus the element we will push
if newRemaining == 0 {
//logs.Warn("window full true", remaining)
wait = 1
}
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, wait)) {
goto start
// another goroutine change the status, make sure shall we need wait
}
Self.bufQueue.Push(element)
// status check finish, now we can push the element into the queue
if wait == 0 {
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.maxSize, newRemaining)
// send the remaining window size, not including zero size
}
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.maxSize, Self.readSize())
return nil
}
@@ -243,10 +273,10 @@ func (Self *ReceiveWindow) Read(p []byte, id int32) (n int, err error) {
l := 0
//logs.Warn("receive window read off, element.l", Self.off, Self.element.l)
copyData:
//Self.bw.StartRead()
if Self.off == uint32(Self.element.l) {
if Self.off == uint32(Self.element.L) {
// on the first Read method invoked, Self.off and Self.element.l
// both zero value
common.ListElementPool.Put(Self.element)
Self.element, err = Self.bufQueue.Pop()
// if the queue is empty, Pop method will wait until one element push
// into the queue successful, or timeout.
@@ -258,38 +288,44 @@ copyData:
}
//logs.Warn("pop element", Self.element.l, Self.element.part)
}
l = copy(p[pOff:], Self.element.buf[Self.off:Self.element.l])
//Self.bw.SetCopySize(l)
l = copy(p[pOff:], Self.element.Buf[Self.off:Self.element.L])
pOff += l
Self.off += uint32(l)
atomic.AddUint32(&Self.readLength, uint32(l))
//logs.Warn("window read length buf len", Self.readLength, Self.bufQueue.Len())
n += l
l = 0
//Self.bw.EndRead()
if Self.off == uint32(Self.element.l) {
if Self.off == uint32(Self.element.L) {
//logs.Warn("put the element end ", string(Self.element.buf[:15]))
common.WindowBuff.Put(Self.element.buf)
Self.sendStatus(id)
common.WindowBuff.Put(Self.element.Buf)
Self.sendStatus(id, Self.element.L)
// check the window full status
}
if pOff < len(p) && Self.element.part {
if pOff < len(p) && Self.element.Part {
// element is a part of the segments, trying to fill up buf p
goto copyData
}
return // buf p is full or all of segments in buf, return
}
func (Self *ReceiveWindow) sendStatus(id int32) {
if Self.bufQueue.Len() == 0 {
// window is full before read or empty now
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, atomic.LoadUint32(&Self.maxSize), Self.readSize())
// acknowledge other side, have empty some receive window space
//}
func (Self *ReceiveWindow) sendStatus(id int32, l uint16) {
var remaining, wait uint32
for {
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, wait = Self.unpack(ptrs)
remaining += uint32(l)
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(remaining, 0)) {
break
}
runtime.Gosched()
// another goroutine change remaining or wait status, make sure
// we need acknowledge other side
}
if atomic.LoadUint32(&Self.windowFull) > 0 && Self.remainingSize() > 0 {
atomic.StoreUint32(&Self.windowFull, 0)
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, atomic.LoadUint32(&Self.maxSize), Self.readSize())
// now we get the current window status success
if wait == 1 {
//logs.Warn("send the wait status", remaining)
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, atomic.LoadUint32(&Self.maxSize), remaining)
}
return
}
func (Self *ReceiveWindow) SetTimeOut(t time.Time) {
@@ -308,17 +344,16 @@ func (Self *ReceiveWindow) CloseWindow() {
}
type SendWindow struct {
buf []byte
sentLength uint32
setSizeCh chan struct{}
setSizeWait uint32
timeout time.Time
buf []byte
setSizeCh chan struct{}
timeout time.Time
window
}
func (Self *SendWindow) New(mux *Mux) {
Self.setSizeCh = make(chan struct{})
Self.maxSize = 4096
atomic.AddUint64(&Self.remainingWait, uint64(4096)<<dequeueBits)
Self.mux = mux
Self.window.New()
}
@@ -329,11 +364,8 @@ func (Self *SendWindow) SetSendBuf(buf []byte) {
Self.off = 0
}
func (Self *SendWindow) RemainingSize() (n uint32) {
return atomic.LoadUint32(&Self.maxSize) - atomic.LoadUint32(&Self.sentLength)
}
func (Self *SendWindow) SetSize(windowSize, readLength uint32) (closed bool) {
func (Self *SendWindow) SetSize(windowSize, newRemaining uint32) (closed bool) {
// set the window size from receive window
defer func() {
if recover() != nil {
closed = true
@@ -343,37 +375,46 @@ func (Self *SendWindow) SetSize(windowSize, readLength uint32) (closed bool) {
close(Self.setSizeCh)
return true
}
if readLength == 0 && atomic.LoadUint32(&Self.maxSize) == windowSize {
//logs.Warn("waiting for another window size")
return false // waiting for receive another usable window size
}
//logs.Warn("set send window size to ", windowSize, readLength)
Self.slide(windowSize, readLength)
if Self.RemainingSize() == 0 {
//logs.Warn("waiting for another window size after slide")
// keep the wait status
//atomic.StoreUint32(&Self.setSizeWait, 1)
return false
}
if atomic.CompareAndSwapUint32(&Self.setSizeWait, 1, 0) {
// send window into the wait status, need notice the channel
select {
case Self.setSizeCh <- struct{}{}:
//logs.Warn("send window remaining size is 0 finish")
return false
case <-Self.closeOpCh:
close(Self.setSizeCh)
return true
//logs.Warn("set send window size to ", windowSize, newRemaining)
var remaining, wait, newWait uint32
for {
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, wait = Self.unpack(ptrs)
if remaining == newRemaining {
//logs.Warn("waiting for another window size")
return false // waiting for receive another usable window size
}
if newRemaining == 0 && wait == 1 {
newWait = 1 // keep the wait status,
// also if newRemaining is not zero, change wait to 0
}
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(newRemaining, newWait)) {
break
}
// anther goroutine change wait status or window size
}
if wait == 1 {
// send window into the wait status, need notice the channel
//logs.Warn("send window remaining size is 0")
Self.allow()
}
// send window not into the wait status, so just do slide
return false
}
func (Self *SendWindow) slide(windowSize, readLength uint32) {
atomic.AddUint32(&Self.sentLength, ^readLength-1)
atomic.StoreUint32(&Self.maxSize, windowSize)
func (Self *SendWindow) allow() {
select {
case Self.setSizeCh <- struct{}{}:
//logs.Warn("send window remaining size is 0 finish")
return
case <-Self.closeOpCh:
close(Self.setSizeCh)
return
}
}
func (Self *SendWindow) sent(sentSize uint32) {
atomic.AddUint64(&Self.remainingWait, ^(uint64(sentSize)<<dequeueBits - 1))
}
func (Self *SendWindow) WriteTo() (p []byte, sendSize uint32, part bool, err error) {
@@ -386,24 +427,35 @@ func (Self *SendWindow) WriteTo() (p []byte, sendSize uint32, part bool, err err
return nil, 0, false, io.EOF
// send window buff is drain, return eof and get another one
}
if Self.RemainingSize() == 0 {
atomic.StoreUint32(&Self.setSizeWait, 1)
var remaining uint32
start:
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, _ = Self.unpack(ptrs)
if remaining == 0 {
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, 1)) {
goto start // another goroutine change the window, try again
}
// into the wait status
//logs.Warn("send window into wait status")
err = Self.waitReceiveWindow()
if err != nil {
return nil, 0, false, err
}
//logs.Warn("rem into wait finish")
goto start
}
// there are still remaining window
//logs.Warn("rem", remaining)
if len(Self.buf[Self.off:]) > common.MAXIMUM_SEGMENT_SIZE {
sendSize = common.MAXIMUM_SEGMENT_SIZE
//logs.Warn("cut buf by mss")
} else {
sendSize = uint32(len(Self.buf[Self.off:]))
}
if Self.RemainingSize() < sendSize {
if remaining < sendSize {
// usable window size is small than
// window MAXIMUM_SEGMENT_SIZE or send buf left
sendSize = Self.RemainingSize()
sendSize = remaining
//logs.Warn("cut buf by remainingsize", sendSize, len(Self.buf[Self.off:]))
}
//logs.Warn("send size", sendSize)
@@ -412,7 +464,7 @@ func (Self *SendWindow) WriteTo() (p []byte, sendSize uint32, part bool, err err
}
p = Self.buf[Self.off : sendSize+Self.off]
Self.off += sendSize
atomic.AddUint32(&Self.sentLength, sendSize)
Self.sent(sendSize)
return
}
@@ -439,6 +491,7 @@ func (Self *SendWindow) waitReceiveWindow() (err error) {
func (Self *SendWindow) WriteFull(buf []byte, id int32) (n int, err error) {
Self.SetSendBuf(buf) // set the buf to send window
//logs.Warn("set the buf to send window")
var bufSeg []byte
var part bool
var l uint32