mirror of https://github.com/ehang-io/nps
				
				
				
			fine mux, add goroutine pool
							parent
							
								
									5f58c34c8b
								
							
						
					
					
						commit
						f362c96e1e
					
				| 
						 | 
				
			
			@ -2,8 +2,6 @@ package common
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"github.com/panjf2000/ants/v2"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,53 +149,34 @@ func (Self *muxPackagerPool) Put(pack *MuxPackager) {
 | 
			
		|||
	Self.pool.Put(pack)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type connGroup struct {
 | 
			
		||||
	src net.Conn
 | 
			
		||||
	dst net.Conn
 | 
			
		||||
	wg  *sync.WaitGroup
 | 
			
		||||
type ListElement struct {
 | 
			
		||||
	Buf  []byte
 | 
			
		||||
	L    uint16
 | 
			
		||||
	Part bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newConnGroup(src net.Conn, dst net.Conn, wg *sync.WaitGroup) connGroup {
 | 
			
		||||
	return connGroup{
 | 
			
		||||
		src: src,
 | 
			
		||||
		dst: dst,
 | 
			
		||||
		wg:  wg,
 | 
			
		||||
type listElementPool struct {
 | 
			
		||||
	pool sync.Pool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *listElementPool) New() {
 | 
			
		||||
	Self.pool = sync.Pool{
 | 
			
		||||
		New: func() interface{} {
 | 
			
		||||
			element := ListElement{}
 | 
			
		||||
			return &element
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyConnGroup(group interface{}) {
 | 
			
		||||
	cg, ok := group.(connGroup)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	_, err := CopyBuffer(cg.src, cg.dst)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		cg.src.Close()
 | 
			
		||||
		cg.dst.Close()
 | 
			
		||||
		//logs.Warn("close npc by copy from nps", err, c.connId)
 | 
			
		||||
	}
 | 
			
		||||
	cg.wg.Done()
 | 
			
		||||
func (Self *listElementPool) Get() *ListElement {
 | 
			
		||||
	return Self.pool.Get().(*ListElement)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Conns struct {
 | 
			
		||||
	conn1 net.Conn
 | 
			
		||||
	conn2 net.Conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewConns(c1 net.Conn, c2 net.Conn) Conns {
 | 
			
		||||
	return Conns{
 | 
			
		||||
		conn1: c1,
 | 
			
		||||
		conn2: c2,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyConns(group interface{}) {
 | 
			
		||||
	conns := group.(Conns)
 | 
			
		||||
	wg := new(sync.WaitGroup)
 | 
			
		||||
	wg.Add(2)
 | 
			
		||||
	_ = connCopyPool.Invoke(newConnGroup(conns.conn1, conns.conn2, wg))
 | 
			
		||||
	_ = connCopyPool.Invoke(newConnGroup(conns.conn2, conns.conn1, wg))
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
func (Self *listElementPool) Put(element *ListElement) {
 | 
			
		||||
	element.L = 0
 | 
			
		||||
	element.Buf = nil
 | 
			
		||||
	element.Part = false
 | 
			
		||||
	Self.pool.Put(element)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var once = sync.Once{}
 | 
			
		||||
| 
						 | 
				
			
			@ -205,14 +184,14 @@ var BuffPool = bufferPool{}
 | 
			
		|||
var CopyBuff = copyBufferPool{}
 | 
			
		||||
var MuxPack = muxPackagerPool{}
 | 
			
		||||
var WindowBuff = windowBufferPool{}
 | 
			
		||||
var connCopyPool, _ = ants.NewPoolWithFunc(200000, copyConnGroup, ants.WithNonblocking(false))
 | 
			
		||||
var CopyConnsPool, _ = ants.NewPoolWithFunc(100000, copyConns, ants.WithNonblocking(false))
 | 
			
		||||
var ListElementPool = listElementPool{}
 | 
			
		||||
 | 
			
		||||
func newPool() {
 | 
			
		||||
	BuffPool.New()
 | 
			
		||||
	CopyBuff.New()
 | 
			
		||||
	MuxPack.New()
 | 
			
		||||
	WindowBuff.New()
 | 
			
		||||
	ListElementPool.New()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,13 +6,14 @@ import (
 | 
			
		|||
	"encoding/binary"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/astaxie/beego/logs"
 | 
			
		||||
	"github.com/cnlh/nps/lib/goroutine"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cnlh/nps/lib/common"
 | 
			
		||||
| 
						 | 
				
			
			@ -350,25 +351,29 @@ func SetUdpSession(sess *kcp.UDPSession) {
 | 
			
		|||
 | 
			
		||||
//conn1 mux conn
 | 
			
		||||
func CopyWaitGroup(conn1, conn2 net.Conn, crypt bool, snappy bool, rate *rate.Rate, flow *file.Flow, isServer bool, rb []byte) {
 | 
			
		||||
	var in, out int64
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	//var in, out int64
 | 
			
		||||
	//var wg sync.WaitGroup
 | 
			
		||||
	connHandle := GetConn(conn1, crypt, snappy, rate, isServer)
 | 
			
		||||
	if rb != nil {
 | 
			
		||||
		connHandle.Write(rb)
 | 
			
		||||
	}
 | 
			
		||||
	go func(in *int64) {
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		*in, _ = common.CopyBuffer(connHandle, conn2)
 | 
			
		||||
		connHandle.Close()
 | 
			
		||||
		conn2.Close()
 | 
			
		||||
		wg.Done()
 | 
			
		||||
	}(&in)
 | 
			
		||||
	out, _ = common.CopyBuffer(conn2, connHandle)
 | 
			
		||||
	connHandle.Close()
 | 
			
		||||
	conn2.Close()
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	if flow != nil {
 | 
			
		||||
		flow.Add(in, out)
 | 
			
		||||
	//go func(in *int64) {
 | 
			
		||||
	//	wg.Add(1)
 | 
			
		||||
	//	*in, _ = common.CopyBuffer(connHandle, conn2)
 | 
			
		||||
	//	connHandle.Close()
 | 
			
		||||
	//	conn2.Close()
 | 
			
		||||
	//	wg.Done()
 | 
			
		||||
	//}(&in)
 | 
			
		||||
	//out, _ = common.CopyBuffer(conn2, connHandle)
 | 
			
		||||
	//connHandle.Close()
 | 
			
		||||
	//conn2.Close()
 | 
			
		||||
	//wg.Wait()
 | 
			
		||||
	//if flow != nil {
 | 
			
		||||
	//	flow.Add(in, out)
 | 
			
		||||
	//}
 | 
			
		||||
	err := goroutine.CopyConnsPool.Invoke(goroutine.NewConns(connHandle, conn2, flow))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logs.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
package goroutine
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cnlh/nps/lib/common"
 | 
			
		||||
	"github.com/cnlh/nps/lib/file"
 | 
			
		||||
	"github.com/panjf2000/ants/v2"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type connGroup struct {
 | 
			
		||||
	src io.ReadWriteCloser
 | 
			
		||||
	dst io.ReadWriteCloser
 | 
			
		||||
	wg  *sync.WaitGroup
 | 
			
		||||
	n   *int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newConnGroup(dst, src io.ReadWriteCloser, wg *sync.WaitGroup, n *int64) connGroup {
 | 
			
		||||
	return connGroup{
 | 
			
		||||
		src: src,
 | 
			
		||||
		dst: dst,
 | 
			
		||||
		wg:  wg,
 | 
			
		||||
		n:   n,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyConnGroup(group interface{}) {
 | 
			
		||||
	cg, ok := group.(connGroup)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	*cg.n, err = common.CopyBuffer(cg.dst, cg.src)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		cg.src.Close()
 | 
			
		||||
		cg.dst.Close()
 | 
			
		||||
		//logs.Warn("close npc by copy from nps", err, c.connId)
 | 
			
		||||
	}
 | 
			
		||||
	cg.wg.Done()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Conns struct {
 | 
			
		||||
	conn1 io.ReadWriteCloser // mux connection
 | 
			
		||||
	conn2 net.Conn           // outside connection
 | 
			
		||||
	flow  *file.Flow
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewConns(c1 io.ReadWriteCloser, c2 net.Conn, flow *file.Flow) Conns {
 | 
			
		||||
	return Conns{
 | 
			
		||||
		conn1: c1,
 | 
			
		||||
		conn2: c2,
 | 
			
		||||
		flow:  flow,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyConns(group interface{}) {
 | 
			
		||||
	conns := group.(Conns)
 | 
			
		||||
	wg := new(sync.WaitGroup)
 | 
			
		||||
	wg.Add(2)
 | 
			
		||||
	var in, out int64
 | 
			
		||||
	_ = connCopyPool.Invoke(newConnGroup(conns.conn1, conns.conn2, wg, &in))
 | 
			
		||||
	// outside to mux : incoming
 | 
			
		||||
	_ = connCopyPool.Invoke(newConnGroup(conns.conn2, conns.conn1, wg, &out))
 | 
			
		||||
	// mux to outside : outgoing
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	if conns.flow != nil {
 | 
			
		||||
		conns.flow.Add(in, out)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var connCopyPool, _ = ants.NewPoolWithFunc(200000, copyConnGroup, ants.WithNonblocking(false))
 | 
			
		||||
var CopyConnsPool, _ = ants.NewPoolWithFunc(100000, copyConns, ants.WithNonblocking(false))
 | 
			
		||||
							
								
								
									
										247
									
								
								lib/mux/conn.go
								
								
								
								
							
							
						
						
									
										247
									
								
								lib/mux/conn.go
								
								
								
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -215,7 +215,7 @@ func (s *Mux) pingReturn() {
 | 
			
		|||
			if latency < 0.5 && latency > 0 {
 | 
			
		||||
				s.latency = latency
 | 
			
		||||
			}
 | 
			
		||||
			logs.Warn("latency", s.latency)
 | 
			
		||||
			//logs.Warn("latency", s.latency)
 | 
			
		||||
			common.WindowBuff.Put(data)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ import (
 | 
			
		|||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/cnlh/nps/lib/common"
 | 
			
		||||
	"github.com/cnlh/nps/lib/goroutine"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +50,7 @@ func TestNewMux(t *testing.T) {
 | 
			
		|||
			}
 | 
			
		||||
			//c2.(*net.TCPConn).SetReadBuffer(0)
 | 
			
		||||
			//c2.(*net.TCPConn).SetReadBuffer(0)
 | 
			
		||||
			_ = common.CopyConnsPool.Invoke(common.NewConns(c2, c))
 | 
			
		||||
			_ = goroutine.CopyConnsPool.Invoke(goroutine.NewConns(c, c2, nil))
 | 
			
		||||
			//go func(c2 net.Conn, c *conn) {
 | 
			
		||||
			//	wg := new(sync.WaitGroup)
 | 
			
		||||
			//	wg.Add(2)
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +103,7 @@ func TestNewMux(t *testing.T) {
 | 
			
		|||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			//logs.Warn("nps new conn success ", tmpCpnn.connId)
 | 
			
		||||
			_ = common.CopyConnsPool.Invoke(common.NewConns(tmpCpnn, conns))
 | 
			
		||||
			_ = goroutine.CopyConnsPool.Invoke(goroutine.NewConns(tmpCpnn, conns, nil))
 | 
			
		||||
			//go func(tmpCpnn *conn, conns net.Conn) {
 | 
			
		||||
			//	wg := new(sync.WaitGroup)
 | 
			
		||||
			//	wg.Add(2)
 | 
			
		||||
| 
						 | 
				
			
			@ -131,9 +132,9 @@ func TestNewMux(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	//go NewLogServer()
 | 
			
		||||
	time.Sleep(time.Second * 5)
 | 
			
		||||
	for i := 0; i < 1000; i++ {
 | 
			
		||||
		go test_raw(i)
 | 
			
		||||
	}
 | 
			
		||||
	//for i := 0; i < 1; i++ {
 | 
			
		||||
	//	go test_raw(i)
 | 
			
		||||
	//}
 | 
			
		||||
	//test_request()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
| 
						 | 
				
			
			@ -166,7 +167,7 @@ func client() {
 | 
			
		|||
 | 
			
		||||
func test_request() {
 | 
			
		||||
	conn, _ := net.Dial("tcp", "127.0.0.1:7777")
 | 
			
		||||
	for {
 | 
			
		||||
	for i := 0; i < 1000; i++ {
 | 
			
		||||
		conn.Write([]byte(`GET / HTTP/1.1
 | 
			
		||||
Host: 127.0.0.1:7777
 | 
			
		||||
Connection: keep-alive
 | 
			
		||||
| 
						 | 
				
			
			@ -185,19 +186,20 @@ Connection: keep-alive
 | 
			
		|||
			break
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println(string(b[:20]), err)
 | 
			
		||||
		time.Sleep(time.Second)
 | 
			
		||||
		//time.Sleep(time.Second)
 | 
			
		||||
	}
 | 
			
		||||
	logs.Warn("finish")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func test_raw(k int) {
 | 
			
		||||
	for i := 0; i < 1; i++ {
 | 
			
		||||
	for i := 0; i < 1000; i++ {
 | 
			
		||||
		ti := time.Now()
 | 
			
		||||
		conn, err := net.Dial("tcp", "127.0.0.1:7777")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logs.Warn("conn dial err", err)
 | 
			
		||||
		}
 | 
			
		||||
		tid := time.Now()
 | 
			
		||||
		conn.Write([]byte(`GET / HTTP/1.1
 | 
			
		||||
		conn.Write([]byte(`GET /videojs5/video.js HTTP/1.1
 | 
			
		||||
Host: 127.0.0.1:7777
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -227,6 +229,7 @@ Host: 127.0.0.1:7777
 | 
			
		|||
			logs.Warn("n loss", n, string(buf))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	logs.Warn("finish")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestNewConn(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -313,11 +316,12 @@ func TestFIFO(t *testing.T) {
 | 
			
		|||
	d.New()
 | 
			
		||||
	go func() {
 | 
			
		||||
		time.Sleep(time.Second)
 | 
			
		||||
		for i := 0; i < 300100; i++ {
 | 
			
		||||
		for i := 0; i < 1001; i++ {
 | 
			
		||||
			data, err := d.Pop()
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				//fmt.Println(i, string(data.buf), err)
 | 
			
		||||
				logs.Warn(i, string(data.buf), err)
 | 
			
		||||
				logs.Warn(i, string(data.Buf), err)
 | 
			
		||||
				common.ListElementPool.Put(data)
 | 
			
		||||
			} else {
 | 
			
		||||
				//fmt.Println("err", err)
 | 
			
		||||
				logs.Warn("err", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -328,10 +332,9 @@ func TestFIFO(t *testing.T) {
 | 
			
		|||
	}()
 | 
			
		||||
	go func() {
 | 
			
		||||
		time.Sleep(time.Second * 10)
 | 
			
		||||
		for i := 0; i < 300000; i++ {
 | 
			
		||||
			data := new(ListElement)
 | 
			
		||||
		for i := 0; i < 1000; i++ {
 | 
			
		||||
			by := []byte("test " + strconv.Itoa(i) + " ") //
 | 
			
		||||
			_ = data.New(by, uint16(len(by)), true)
 | 
			
		||||
			data, _ := NewListElement(by, uint16(len(by)), true)
 | 
			
		||||
			//fmt.Println(string((*data).buf), data)
 | 
			
		||||
			//logs.Warn(string((*data).buf), data)
 | 
			
		||||
			d.Push(data)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										123
									
								
								lib/mux/queue.go
								
								
								
								
							
							
						
						
									
										123
									
								
								lib/mux/queue.go
								
								
								
								
							| 
						 | 
				
			
			@ -44,7 +44,6 @@ func (Self *PriorityQueue) Push(packager *common.MuxPackager) {
 | 
			
		|||
	default:
 | 
			
		||||
		Self.lowestChain.pushHead(unsafe.Pointer(packager))
 | 
			
		||||
	}
 | 
			
		||||
	//atomic.AddUint32(&Self.count, 1)
 | 
			
		||||
	Self.cond.Signal()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +51,6 @@ func (Self *PriorityQueue) Push(packager *common.MuxPackager) {
 | 
			
		|||
const maxStarving uint8 = 8
 | 
			
		||||
 | 
			
		||||
func (Self *PriorityQueue) Pop() (packager *common.MuxPackager) {
 | 
			
		||||
	// PriorityQueue is empty, notice Push method
 | 
			
		||||
	var iter bool
 | 
			
		||||
	for {
 | 
			
		||||
		packager = Self.pop()
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +62,7 @@ func (Self *PriorityQueue) Pop() (packager *common.MuxPackager) {
 | 
			
		|||
		}
 | 
			
		||||
		if iter {
 | 
			
		||||
			break
 | 
			
		||||
			// trying to pop twice
 | 
			
		||||
		}
 | 
			
		||||
		iter = true
 | 
			
		||||
		runtime.Gosched()
 | 
			
		||||
| 
						 | 
				
			
			@ -74,10 +73,12 @@ func (Self *PriorityQueue) Pop() (packager *common.MuxPackager) {
 | 
			
		|||
		if Self.stop {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		//logs.Warn("queue into wait")
 | 
			
		||||
		Self.cond.Wait()
 | 
			
		||||
		// wait for it with no more iter
 | 
			
		||||
		packager = Self.pop()
 | 
			
		||||
		//logs.Warn("queue wait finish", packager)
 | 
			
		||||
	}
 | 
			
		||||
	//atomic.AddUint32(&Self.count, ^uint32(0))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +89,7 @@ func (Self *PriorityQueue) pop() (packager *common.MuxPackager) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if Self.starving < maxStarving {
 | 
			
		||||
		// not pop too much, lowestChain will wait too long
 | 
			
		||||
		ptr, ok = Self.middleChain.popTail()
 | 
			
		||||
		if ok {
 | 
			
		||||
			packager = (*common.MuxPackager)(ptr)
 | 
			
		||||
| 
						 | 
				
			
			@ -119,29 +121,27 @@ func (Self *PriorityQueue) Stop() {
 | 
			
		|||
	Self.cond.Broadcast()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ListElement struct {
 | 
			
		||||
	buf  []byte
 | 
			
		||||
	l    uint16
 | 
			
		||||
	part bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ListElement) New(buf []byte, l uint16, part bool) (err error) {
 | 
			
		||||
func NewListElement(buf []byte, l uint16, part bool) (element *common.ListElement, err error) {
 | 
			
		||||
	if uint16(len(buf)) != l {
 | 
			
		||||
		return errors.New("ListElement: buf length not match")
 | 
			
		||||
		err = errors.New("ListElement: buf length not match")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	Self.buf = buf
 | 
			
		||||
	Self.l = l
 | 
			
		||||
	Self.part = part
 | 
			
		||||
	return nil
 | 
			
		||||
	//if l == 0 {
 | 
			
		||||
	//	logs.Warn("push zero")
 | 
			
		||||
	//}
 | 
			
		||||
	element = common.ListElementPool.Get()
 | 
			
		||||
	element.Buf = buf
 | 
			
		||||
	element.L = l
 | 
			
		||||
	element.Part = part
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReceiveWindowQueue struct {
 | 
			
		||||
	chain   *bufChain
 | 
			
		||||
	length  uint32
 | 
			
		||||
	stopOp  chan struct{}
 | 
			
		||||
	readOp  chan struct{}
 | 
			
		||||
	popWait uint32
 | 
			
		||||
	timeout time.Time
 | 
			
		||||
	chain      *bufChain
 | 
			
		||||
	stopOp     chan struct{}
 | 
			
		||||
	readOp     chan struct{}
 | 
			
		||||
	lengthWait uint64
 | 
			
		||||
	timeout    time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ReceiveWindowQueue) New() {
 | 
			
		||||
| 
						 | 
				
			
			@ -151,45 +151,45 @@ func (Self *ReceiveWindowQueue) New() {
 | 
			
		|||
	Self.stopOp = make(chan struct{}, 2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ReceiveWindowQueue) Push(element *ListElement) {
 | 
			
		||||
func (Self *ReceiveWindowQueue) Push(element *common.ListElement) {
 | 
			
		||||
	var length, wait uint32
 | 
			
		||||
	for {
 | 
			
		||||
		ptrs := atomic.LoadUint64(&Self.lengthWait)
 | 
			
		||||
		length, wait = Self.chain.head.unpack(ptrs)
 | 
			
		||||
		length += uint32(element.L)
 | 
			
		||||
		if atomic.CompareAndSwapUint64(&Self.lengthWait, ptrs, Self.chain.head.pack(length, 0)) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		// another goroutine change the length or into wait, make sure
 | 
			
		||||
	}
 | 
			
		||||
	//logs.Warn("window push before", Self.Len(), uint32(element.l), len(element.buf))
 | 
			
		||||
	Self.chain.pushHead(unsafe.Pointer(element))
 | 
			
		||||
	atomic.AddUint32(&Self.length, uint32(element.l))
 | 
			
		||||
	Self.allowPop()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ReceiveWindowQueue) pop() (element *ListElement) {
 | 
			
		||||
	ptr, ok := Self.chain.popTail()
 | 
			
		||||
	if ok {
 | 
			
		||||
		element = (*ListElement)(ptr)
 | 
			
		||||
		atomic.AddUint32(&Self.length, ^uint32(element.l-1))
 | 
			
		||||
		return
 | 
			
		||||
	//logs.Warn("window push", Self.Len())
 | 
			
		||||
	if wait == 1 {
 | 
			
		||||
		Self.allowPop()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ReceiveWindowQueue) Pop() (element *ListElement, err error) {
 | 
			
		||||
	var iter bool
 | 
			
		||||
func (Self *ReceiveWindowQueue) Pop() (element *common.ListElement, err error) {
 | 
			
		||||
	var length uint32
 | 
			
		||||
startPop:
 | 
			
		||||
	element = Self.pop()
 | 
			
		||||
	if element != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !iter {
 | 
			
		||||
		iter = true
 | 
			
		||||
		runtime.Gosched()
 | 
			
		||||
		goto startPop
 | 
			
		||||
	}
 | 
			
		||||
	if atomic.CompareAndSwapUint32(&Self.popWait, 0, 1) {
 | 
			
		||||
		iter = false
 | 
			
		||||
	ptrs := atomic.LoadUint64(&Self.lengthWait)
 | 
			
		||||
	length, _ = Self.chain.head.unpack(ptrs)
 | 
			
		||||
	if length == 0 {
 | 
			
		||||
		if !atomic.CompareAndSwapUint64(&Self.lengthWait, ptrs, Self.chain.head.pack(0, 1)) {
 | 
			
		||||
			goto startPop // another goroutine is pushing
 | 
			
		||||
		}
 | 
			
		||||
		t := Self.timeout.Sub(time.Now())
 | 
			
		||||
		if t <= 0 {
 | 
			
		||||
			t = time.Minute
 | 
			
		||||
		}
 | 
			
		||||
		timer := time.NewTimer(t)
 | 
			
		||||
		defer timer.Stop()
 | 
			
		||||
		//logs.Warn("queue into wait")
 | 
			
		||||
		select {
 | 
			
		||||
		case <-Self.readOp:
 | 
			
		||||
			//logs.Warn("queue wait finish")
 | 
			
		||||
			goto startPop
 | 
			
		||||
		case <-Self.stopOp:
 | 
			
		||||
			err = io.EOF
 | 
			
		||||
| 
						 | 
				
			
			@ -199,23 +199,34 @@ startPop:
 | 
			
		|||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	goto startPop
 | 
			
		||||
	// length is not zero, so try to pop
 | 
			
		||||
	for {
 | 
			
		||||
		ptr, ok := Self.chain.popTail()
 | 
			
		||||
		if ok {
 | 
			
		||||
			//logs.Warn("window pop before", Self.Len())
 | 
			
		||||
			element = (*common.ListElement)(ptr)
 | 
			
		||||
			atomic.AddUint64(&Self.lengthWait, ^(uint64(element.L)<<dequeueBits - 1))
 | 
			
		||||
			//logs.Warn("window pop", Self.Len(), uint32(element.l))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		runtime.Gosched() // another goroutine is still pushing
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ReceiveWindowQueue) allowPop() (closed bool) {
 | 
			
		||||
	if atomic.CompareAndSwapUint32(&Self.popWait, 1, 0) {
 | 
			
		||||
		select {
 | 
			
		||||
		case Self.readOp <- struct{}{}:
 | 
			
		||||
			return false
 | 
			
		||||
		case <-Self.stopOp:
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	//logs.Warn("allow pop", Self.Len())
 | 
			
		||||
	select {
 | 
			
		||||
	case Self.readOp <- struct{}{}:
 | 
			
		||||
		return false
 | 
			
		||||
	case <-Self.stopOp:
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ReceiveWindowQueue) Len() (n uint32) {
 | 
			
		||||
	return atomic.LoadUint32(&Self.length)
 | 
			
		||||
	ptrs := atomic.LoadUint64(&Self.lengthWait)
 | 
			
		||||
	n, _ = Self.chain.head.unpack(ptrs)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Self *ReceiveWindowQueue) Stop() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue