From f6de6809ed81554356ca4dd5d2a190c7a5f9b0f0 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 24 May 2023 16:26:57 +0800 Subject: [PATCH 01/29] opt: improve comments on Conn and test cases (#471) Fixes #470 --- client_test.go | 19 +++++++++- client_unix.go | 6 ++- client_windows.go | 3 +- connection_unix.go | 28 ++++++-------- connection_windows.go | 27 ++++++------- engine_unix.go | 19 ++++++---- engine_windows.go | 5 ++- gnet.go | 78 ++++++++++++++++++++------------------ gnet_test.go | 30 ++++++++++++--- listener_windows.go | 45 ++++++++++++++++++---- reactor_default_bsd.go | 1 - reactor_default_linux.go | 1 - reactor_optimized_bsd.go | 1 - reactor_optimized_linux.go | 1 - 14 files changed, 169 insertions(+), 95 deletions(-) diff --git a/client_test.go b/client_test.go index ded86481d..3995aba8a 100644 --- a/client_test.go +++ b/client_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + gerr "github.com/panjf2000/gnet/v2/pkg/errors" "github.com/panjf2000/gnet/v2/pkg/logging" bbPool "github.com/panjf2000/gnet/v2/pkg/pool/bytebuffer" goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine" @@ -21,11 +22,20 @@ import ( type clientEvents struct { *BuiltinEventEngine + tester *testing.T svr *testClientServer packetLen int rspChMap sync.Map } +func (ev *clientEvents) OnBoot(e Engine) Action { + fd, err := e.Dup() + require.ErrorIsf(ev.tester, err, gerr.ErrEmptyEngine, "expected error: %v, but got: %v", + gerr.ErrUnsupportedOp, err) + assert.EqualValuesf(ev.tester, fd, -1, "expected -1, but got: %d", fd) + return None +} + func (ev *clientEvents) OnOpen(c Conn) ([]byte, Action) { c.SetContext([]byte{}) rspCh := make(chan []byte, 1) @@ -68,6 +78,13 @@ func (ev *clientEvents) OnTick() (delay time.Duration, action Action) { return } +func (ev *clientEvents) OnShutdown(e Engine) { + fd, err := e.Dup() + require.ErrorIsf(ev.tester, err, gerr.ErrEmptyEngine, "expected error: %v, but got: %v", + gerr.ErrUnsupportedOp, err) + assert.EqualValuesf(ev.tester, fd, -1, "expected -1, but got: %d", fd) +} + func TestServeWithGnetClient(t *testing.T) { // start an engine // connect 10 clients @@ -279,7 +296,7 @@ func testServeWithGnetClient(t *testing.T, network, addr string, reuseport, reus workerPool: goPool.Default(), } var err error - ts.clientEV = &clientEvents{packetLen: streamLen, svr: ts} + ts.clientEV = &clientEvents{tester: t, packetLen: streamLen, svr: ts} ts.client, err = NewClient( ts.clientEV, WithLogLevel(logging.DebugLevel), diff --git a/client_unix.go b/client_unix.go index bb61d03a9..5ee50cef9 100644 --- a/client_unix.go +++ b/client_unix.go @@ -22,6 +22,7 @@ import ( "errors" "net" "strconv" + "sync" "syscall" "golang.org/x/sync/errgroup" @@ -66,14 +67,15 @@ func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) { shutdownCtx, shutdown := context.WithCancel(context.Background()) eng := engine{ - ln: &listener{network: "udp"}, + ln: &listener{}, opts: options, eventHandler: eh, workerPool: struct { *errgroup.Group shutdownCtx context.Context shutdown context.CancelFunc - }{&errgroup.Group{}, shutdownCtx, shutdown}, + once sync.Once + }{&errgroup.Group{}, shutdownCtx, shutdown, sync.Once{}}, } if options.Ticker { eng.ticker.ctx, eng.ticker.cancel = context.WithCancel(context.Background()) diff --git a/client_windows.go b/client_windows.go index 08cfc2687..d90951b53 100644 --- a/client_windows.go +++ b/client_windows.go @@ -56,7 +56,8 @@ func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) { *errgroup.Group shutdownCtx context.Context shutdown context.CancelFunc - }{&errgroup.Group{}, shutdownCtx, shutdown}, + once sync.Once + }{&errgroup.Group{}, shutdownCtx, shutdown, sync.Once{}}, eventHandler: eh, } cli.el = &eventloop{ diff --git a/connection_unix.go b/connection_unix.go index bd59cc94d..b92aace4e 100644 --- a/connection_unix.go +++ b/connection_unix.go @@ -246,8 +246,6 @@ func (c *conn) resetBuffer() { c.inboundBuffer.Reset() } -// ================================== Non-concurrency-safe API's ================================== - func (c *conn) Read(p []byte) (n int, err error) { if c.inboundBuffer.IsEmpty() { n = copy(p, c.buffer) @@ -395,18 +393,6 @@ func (c *conn) OutboundBuffered() int { return c.outboundBuffer.Buffered() } -func (*conn) SetDeadline(_ time.Time) error { - return gerrors.ErrUnsupportedOp -} - -func (*conn) SetReadDeadline(_ time.Time) error { - return gerrors.ErrUnsupportedOp -} - -func (*conn) SetWriteDeadline(_ time.Time) error { - return gerrors.ErrUnsupportedOp -} - func (c *conn) Context() interface{} { return c.ctx } func (c *conn) SetContext(ctx interface{}) { c.ctx = ctx } func (c *conn) LocalAddr() net.Addr { return c.localAddr } @@ -434,8 +420,6 @@ func (c *conn) SetKeepAlivePeriod(d time.Duration) error { return socket.SetKeepAlivePeriod(c.fd, int(d.Seconds())) } -// ==================================== Concurrency-safe API's ==================================== - func (c *conn) AsyncWrite(buf []byte, callback AsyncCallback) error { if c.isDatagram { defer func() { @@ -481,3 +465,15 @@ func (c *conn) Close() error { return }, nil) } + +func (*conn) SetDeadline(_ time.Time) error { + return gerrors.ErrUnsupportedOp +} + +func (*conn) SetReadDeadline(_ time.Time) error { + return gerrors.ErrUnsupportedOp +} + +func (*conn) SetWriteDeadline(_ time.Time) error { + return gerrors.ErrUnsupportedOp +} diff --git a/connection_windows.go b/connection_windows.go index e08165ed2..f0f20085c 100644 --- a/connection_windows.go +++ b/connection_windows.go @@ -114,8 +114,6 @@ func (c *conn) resetBuffer() { c.inboundBuffer.Reset() } -// ================================== Non-concurrency-safe API's ================================== - func (c *conn) Read(p []byte) (n int, err error) { if c.inboundBuffer.IsEmpty() { n = copy(p, c.buffer.B) @@ -264,17 +262,6 @@ func (c *conn) OutboundBuffered() int { return 0 } -func (*conn) SetDeadline(_ time.Time) error { - return errorx.ErrUnsupportedOp -} - -func (*conn) SetReadDeadline(_ time.Time) error { - return errorx.ErrUnsupportedOp -} - -func (*conn) SetWriteDeadline(_ time.Time) error { - return errorx.ErrUnsupportedOp -} func (c *conn) Context() interface{} { return c.ctx } func (c *conn) SetContext(ctx interface{}) { c.ctx = ctx } func (c *conn) LocalAddr() net.Addr { return c.localAddr } @@ -412,8 +399,6 @@ func (c *conn) SetKeepAlivePeriod(d time.Duration) error { // this method is only implemented for compatibility, don't use it on Windows. // func (c *conn) Gfd() gfd.GFD { return gfd.GFD{} } -// ==================================== Concurrency-safe API's ==================================== - func (c *conn) AsyncWrite(buf []byte, cb AsyncCallback) error { if cb == nil { cb = func(c Conn, err error) error { return nil } @@ -482,3 +467,15 @@ func (c *conn) CloseWithCallback(cb AsyncCallback) error { } return nil } + +func (*conn) SetDeadline(_ time.Time) error { + return errorx.ErrUnsupportedOp +} + +func (*conn) SetReadDeadline(_ time.Time) error { + return errorx.ErrUnsupportedOp +} + +func (*conn) SetWriteDeadline(_ time.Time) error { + return errorx.ErrUnsupportedOp +} diff --git a/engine_unix.go b/engine_unix.go index 9a56732e0..8fbdd7916 100644 --- a/engine_unix.go +++ b/engine_unix.go @@ -20,6 +20,7 @@ package gnet import ( "context" "runtime" + "sync" "sync/atomic" "golang.org/x/sync/errgroup" @@ -44,6 +45,7 @@ type engine struct { shutdownCtx context.Context shutdown context.CancelFunc + once sync.Once } eventHandler EventHandler // user eventHandler } @@ -58,7 +60,9 @@ func (eng *engine) shutdown(err error) { eng.opts.Logger.Errorf("engine is being shutdown with error: %v", err) } - eng.workerPool.shutdown() + eng.workerPool.once.Do(func() { + eng.workerPool.shutdown() + }) } func (eng *engine) startEventLoops() { @@ -70,6 +74,7 @@ func (eng *engine) startEventLoops() { func (eng *engine) closeEventLoops() { eng.lb.iterate(func(i int, el *eventloop) bool { + el.ln.close() _ = el.poller.Close() return true }) @@ -85,7 +90,6 @@ func (eng *engine) startSubReactors() { func (eng *engine) activateEventLoops(numEventLoop int) (err error) { network, address := eng.ln.network, eng.ln.address ln := eng.ln - eng.ln = nil var striker *eventloop // Create loops locally and bind the listeners. for i := 0; i < numEventLoop; i++ { @@ -190,7 +194,7 @@ func (eng *engine) stop(s Engine) { eng.eventHandler.OnShutdown(s) - // Notify all loops to close by closing all listeners + // Notify all event-loops to exit. eng.lb.iterate(func(i int, el *eventloop) bool { err := el.poller.UrgentTrigger(func(_ interface{}) error { return errors.ErrEngineShutdown }, nil) if err != nil { @@ -198,9 +202,7 @@ func (eng *engine) stop(s Engine) { } return true }) - if eng.mainLoop != nil { - eng.ln.close() err := eng.mainLoop.poller.UrgentTrigger(func(_ interface{}) error { return errors.ErrEngineShutdown }, nil) if err != nil { eng.opts.Logger.Errorf("failed to call UrgentTrigger on main event-loop when stopping engine: %v", err) @@ -216,15 +218,17 @@ func (eng *engine) stop(s Engine) { eng.opts.Logger.Errorf("engine shutdown error: %v", err) } + // Close all listeners and pollers of event-loops. eng.closeEventLoops() - if eng.mainLoop != nil { + eng.ln.close() err := eng.mainLoop.poller.Close() if err != nil { eng.opts.Logger.Errorf("failed to close poller when stopping engine: %v", err) } } + // Put the engine into the shutdown state. atomic.StoreInt32(&eng.inShutdown, 1) } @@ -249,7 +253,8 @@ func run(eventHandler EventHandler, listener *listener, options *Options, protoA *errgroup.Group shutdownCtx context.Context shutdown context.CancelFunc - }{&errgroup.Group{}, shutdownCtx, shutdown}, + once sync.Once + }{&errgroup.Group{}, shutdownCtx, shutdown, sync.Once{}}, eventHandler: eventHandler, } switch options.LB { diff --git a/engine_windows.go b/engine_windows.go index 9611001bf..734c461fc 100644 --- a/engine_windows.go +++ b/engine_windows.go @@ -17,6 +17,7 @@ package gnet import ( "context" "runtime" + "sync" "sync/atomic" "golang.org/x/sync/errgroup" @@ -38,6 +39,7 @@ type engine struct { shutdownCtx context.Context shutdown context.CancelFunc + once sync.Once } eventHandler EventHandler // user eventHandler } @@ -123,7 +125,8 @@ func run(eventHandler EventHandler, listener *listener, options *Options, protoA *errgroup.Group shutdownCtx context.Context shutdown context.CancelFunc - }{&errgroup.Group{}, shutdownCtx, shutdown}, + once sync.Once + }{&errgroup.Group{}, shutdownCtx, shutdown, sync.Once{}}, } switch options.LB { diff --git a/gnet.go b/gnet.go index e8e1af672..cda800fb8 100644 --- a/gnet.go +++ b/gnet.go @@ -166,11 +166,12 @@ func (e Engine) Wake(fd gfd.GFD, cb AsyncCallback) error { */ // Reader is an interface that consists of a number of methods for reading that Conn must implement. +// +// Note that the methods in this interface are not goroutine-safe for concurrent use, +// you must invoke them within any method in EventHandler. type Reader interface { - // ================================== Non-concurrency-safe API's ================================== - io.Reader - io.WriterTo // must be non-blocking, otherwise it may block the event-loop. + io.WriterTo // Next returns a slice containing the next n bytes from the buffer, // advancing the buffer as if the bytes had been returned by Read. @@ -207,28 +208,29 @@ type Reader interface { // Writer is an interface that consists of a number of methods for writing that Conn must implement. type Writer interface { - // ================================== Non-concurrency-safe API's ================================== - - io.Writer - io.ReaderFrom // must be non-blocking, otherwise it may block the event-loop. + io.Writer // not goroutine-safe + io.ReaderFrom // not goroutine-safe - // Writev writes multiple byte slices to peer synchronously, you must call it in the current goroutine. + // Writev writes multiple byte slices to peer synchronously, it's not goroutine-safe, + // you must invoke it within any method in EventHandler. Writev(bs [][]byte) (n int, err error) - // Flush writes any buffered data to the underlying connection, you must call it in the current goroutine. + // Flush writes any buffered data to the underlying connection, it's not goroutine-safe, + // you must invoke it within any method in EventHandler. Flush() (err error) // OutboundBuffered returns the number of bytes that can be read from the current buffer. + // it's not goroutine-safe, you must invoke it within any method in EventHandler. OutboundBuffered() (n int) - // ==================================== Concurrency-safe API's ==================================== - - // AsyncWrite writes one byte slice to peer asynchronously, usually you would call it in individual goroutines - // instead of the event-loop goroutines. + // AsyncWrite writes bytes to peer asynchronously, it's goroutine-safe, + // you don't have to invoke it within any method in EventHandler, + // usually you would call it in an individual goroutine. AsyncWrite(buf []byte, callback AsyncCallback) (err error) - // AsyncWritev writes multiple byte slices to peer asynchronously, usually you would call it in individual goroutines - // instead of the event-loop goroutines. + // AsyncWritev writes multiple byte slices to peer asynchronously, + // you don't have to invoke it within any method in EventHandler, + // usually you would call it in an individual goroutine. AsyncWritev(bs [][]byte, callback AsyncCallback) (err error) } @@ -238,6 +240,9 @@ type Writer interface { type AsyncCallback func(c Conn, err error) error // Socket is a set of functions which manipulate the underlying file descriptor of a connection. +// +// Note that the methods in this interface are goroutine-safe for concurrent use, +// you don't have to invoke them within any method in EventHandler. type Socket interface { // Gfd returns the gfd of socket. // Gfd() gfd.GFD @@ -291,24 +296,37 @@ type Socket interface { // Conn is an interface of underlying connection. type Conn interface { - Reader - Writer - Socket + Reader // all methods in Reader are not goroutine-safe. + Writer // some methods in Writer are goroutine-safe, some are not. + Socket // all methods in Socket are goroutine-safe. - // ================================== Non-concurrency-safe API's ================================== - - // Context returns a user-defined context. + // Context returns a user-defined context, it's not goroutine-safe, + // you must invoke it within any method in EventHandler. Context() (ctx interface{}) - // SetContext sets a user-defined context. + // SetContext sets a user-defined context, it's not goroutine-safe, + // you must invoke it within any method in EventHandler. SetContext(ctx interface{}) - // LocalAddr is the connection's local socket address. + // LocalAddr is the connection's local socket address, it's not goroutine-safe, + // you must invoke it within any method in EventHandler. LocalAddr() (addr net.Addr) - // RemoteAddr is the connection's remote peer address. + // RemoteAddr is the connection's remote peer address, it's not goroutine-safe, + // you must invoke it within any method in EventHandler. RemoteAddr() (addr net.Addr) + // Wake triggers a OnTraffic event for the current connection, it's goroutine-safe. + Wake(callback AsyncCallback) (err error) + + // CloseWithCallback closes the current connection, it's goroutine-safe. + // Usually you should provide a non-nil callback for this method, + // otherwise your better choice is Close(). + CloseWithCallback(callback AsyncCallback) (err error) + + // Close closes the current connection, implements net.Conn, it's goroutine-safe. + Close() (err error) + // SetDeadline implements net.Conn. SetDeadline(t time.Time) (err error) @@ -317,18 +335,6 @@ type Conn interface { // SetWriteDeadline implements net.Conn. SetWriteDeadline(t time.Time) (err error) - - // ==================================== Concurrency-safe API's ==================================== - - // Wake triggers a OnTraffic event for the connection. - Wake(callback AsyncCallback) (err error) - - // CloseWithCallback closes the current connection, usually you don't need to pass a non-nil callback - // because you should use OnClose() instead, the callback here is only for compatibility. - CloseWithCallback(callback AsyncCallback) (err error) - - // Close closes the current connection, implements net.Conn. - Close() (err error) } type ( diff --git a/gnet_test.go b/gnet_test.go index 59e5c0f70..f535428d4 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -238,6 +238,10 @@ type testServer struct { func (s *testServer) OnBoot(eng Engine) (action Action) { s.eng = eng + fd, err := s.eng.Dup() + require.NoErrorf(s.tester, err, "dup error") + assert.Greaterf(s.tester, fd, 2, "expected fd: > 2, but got: %d", fd) + assert.NoErrorf(s.tester, SysClose(fd), "close fd error") return } @@ -255,6 +259,13 @@ func (s *testServer) OnOpen(c Conn) (out []byte, action Action) { return } +func (s *testServer) OnShutdown(_ Engine) { + fd, err := s.eng.Dup() + require.NoErrorf(s.tester, err, "dup error") + assert.Greaterf(s.tester, fd, 2, "expected fd: > 2, but got: %d", fd) + assert.NoErrorf(s.tester, SysClose(fd), "close fd error") +} + func (s *testServer) OnClose(c Conn, err error) (action Action) { if err != nil { logging.Debugf("error occurred on closed, %v\n", err) @@ -318,6 +329,7 @@ func (s *testServer) OnTraffic(c Conn) (action Action) { } return } + buf, _ := c.Next(-1) _, _ = c.Write(buf) @@ -326,8 +338,8 @@ func (s *testServer) OnTraffic(c Conn) (action Action) { assert.NoErrorf(s.tester, c.Flush(), "flush error") _ = c.Fd() fd, err := c.Dup() - assert.NoError(s.tester, err) - assert.Greater(s.tester, fd, 0) + require.NoErrorf(s.tester, err, "dup error") + assert.Greaterf(s.tester, fd, 2, "expected fd: > 2, but got: %d", fd) assert.NoErrorf(s.tester, SysClose(fd), "close error") // TODO(panjf2000): somehow these two system calls will fail with Unix Domain Socket, // returning "invalid argument" error on macOS in Github actions intermittently, @@ -759,6 +771,7 @@ type testShutdownActionOnOpenServer struct { tester *testing.T network, addr string action bool + eng Engine } func (t *testShutdownActionOnOpenServer) OnOpen(Conn) (out []byte, action Action) { @@ -766,9 +779,13 @@ func (t *testShutdownActionOnOpenServer) OnOpen(Conn) (out []byte, action Action return } -func (t *testShutdownActionOnOpenServer) OnShutdown(s Engine) { - dupFD, err := s.Dup() - logging.Debugf("dup fd: %d with error: %v\n", dupFD, err) +func (t *testShutdownActionOnOpenServer) OnShutdown(e Engine) { + t.eng = e + fd, err := t.eng.Dup() + assert.Greaterf(t.tester, fd, 2, "expected fd: > 2, but got: %d", fd) + require.NoErrorf(t.tester, err, "dup error") + assert.NoErrorf(t.tester, SysClose(fd), "close error") + logging.Debugf("dup fd: %d with error: %v\n", fd, err) } func (t *testShutdownActionOnOpenServer) OnTick() (delay time.Duration, action Action) { @@ -790,6 +807,9 @@ func testShutdownActionOnOpen(t *testing.T, network, addr string) { events := &testShutdownActionOnOpenServer{tester: t, network: network, addr: addr} err := Run(events, network+"://"+addr, WithTicker(true)) assert.NoError(t, err) + _, err = events.eng.Dup() + assert.ErrorIsf(t, err, gerr.ErrEngineInShutdown, "expected error: %v, but got: %v", + gerr.ErrEngineInShutdown, err) } func TestUDPShutdown(t *testing.T) { diff --git a/listener_windows.go b/listener_windows.go index c31cf5cc3..2d554c361 100644 --- a/listener_windows.go +++ b/listener_windows.go @@ -16,6 +16,7 @@ package gnet import ( "context" + "errors" "net" "os" "sync" @@ -37,19 +38,49 @@ type listener struct { } func (l *listener) dup() (int, string, error) { + if l.ln == nil && l.pc == nil { + return -1, "dup", errorx.ErrUnsupportedOp + } + var ( - file *os.File - err error + sc syscall.Conn + ok bool ) - if l.pc != nil { - file, err = l.pc.(*net.UDPConn).File() + if l.ln != nil { + sc, ok = l.ln.(syscall.Conn) } else { - file, err = l.ln.(interface{ File() (*os.File, error) }).File() + sc, ok = l.pc.(syscall.Conn) + } + + if !ok { + return -1, "dup", errors.New("failed to convert net.Conn to syscall.Conn") + } + rc, err := sc.SyscallConn() + if err != nil { + return -1, "dup", errors.New("failed to get syscall.RawConn from net.Conn") } + + var dupHandle windows.Handle + e := rc.Control(func(fd uintptr) { + process := windows.CurrentProcess() + err = windows.DuplicateHandle( + process, + windows.Handle(fd), + process, + &dupHandle, + 0, + true, + windows.DUPLICATE_SAME_ACCESS, + ) + }) if err != nil { - return 0, "dup", err + return -1, "dup", err } - return int(file.Fd()), "", nil + if e != nil { + return -1, "dup", e + } + + return int(dupHandle), "dup", nil } func (l *listener) close() { diff --git a/reactor_default_bsd.go b/reactor_default_bsd.go index e5afaf840..a3cabd401 100644 --- a/reactor_default_bsd.go +++ b/reactor_default_bsd.go @@ -110,7 +110,6 @@ func (el *eventloop) run() error { } el.closeConns() - el.ln.close() el.engine.shutdown(err) return err diff --git a/reactor_default_linux.go b/reactor_default_linux.go index bf15c5804..70777b6a0 100644 --- a/reactor_default_linux.go +++ b/reactor_default_linux.go @@ -128,7 +128,6 @@ func (el *eventloop) run() error { } el.closeConns() - el.ln.close() el.engine.shutdown(err) return err diff --git a/reactor_optimized_bsd.go b/reactor_optimized_bsd.go index dc292489f..ecb54d886 100644 --- a/reactor_optimized_bsd.go +++ b/reactor_optimized_bsd.go @@ -78,7 +78,6 @@ func (el *eventloop) run() error { } el.closeConns() - el.ln.close() el.engine.shutdown(err) return err diff --git a/reactor_optimized_linux.go b/reactor_optimized_linux.go index 3e9bd6e62..73312d9d1 100644 --- a/reactor_optimized_linux.go +++ b/reactor_optimized_linux.go @@ -77,7 +77,6 @@ func (el *eventloop) run() error { } el.closeConns() - el.ln.close() el.engine.shutdown(err) return err From bdcc30c22b3d70b3cd962249966f8d9c5d574ba0 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 24 May 2023 17:18:14 +0800 Subject: [PATCH 02/29] bug: fix the data race in test on Windows (#472) --- connection_windows.go | 12 +++--------- eventloop_windows.go | 6 +++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/connection_windows.go b/connection_windows.go index f0f20085c..41486ecda 100644 --- a/connection_windows.go +++ b/connection_windows.go @@ -60,10 +60,11 @@ func packTCPConn(c *conn, buf []byte) *tcpConn { func unpackTCPConn(tc *tcpConn) { tc.c.buffer = tc.buf + tc.buf = nil } func resetTCPConn(tc *tcpConn) { - bbPool.Put(tc.buf) + bbPool.Put(tc.c.buffer) tc.c.buffer = nil } @@ -83,7 +84,7 @@ func newTCPConn(nc net.Conn, el *eventloop) (c *conn) { return } -func (c *conn) releaseTCP() { +func (c *conn) release() { c.ctx = nil c.localAddr = nil c.remoteAddr = nil @@ -102,13 +103,6 @@ func newUDPConn(el *eventloop, localAddr, remoteAddr net.Addr) *conn { } } -func (c *conn) releaseUDP() { - c.ctx = nil - c.localAddr = nil - bbPool.Put(c.buffer) - c.buffer = nil -} - func (c *conn) resetBuffer() { c.buffer.Reset() c.inboundBuffer.Reset() diff --git a/eventloop_windows.go b/eventloop_windows.go index ca1a96fd1..a683975cf 100644 --- a/eventloop_windows.go +++ b/eventloop_windows.go @@ -127,7 +127,7 @@ func (el *eventloop) readUDP(c *conn) error { if action == Shutdown { return errors.ErrEngineShutdown } - c.releaseUDP() + c.release() return nil } @@ -187,7 +187,7 @@ func (el *eventloop) close(c *conn, err error) error { el.getLogger().Errorf("failed to close connection(%s), error:%v", c.remoteAddr.String(), err) } } - c.releaseUDP() + c.release() return el.handleAction(c, action) } @@ -201,7 +201,7 @@ func (el *eventloop) close(c *conn, err error) error { } delete(el.connections, c) el.incConn(-1) - c.releaseTCP() + c.release() return el.handleAction(c, action) } From daffb4efa39923be6ee1d6b00c6d21f69cd3b079 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 24 May 2023 17:58:41 +0800 Subject: [PATCH 03/29] bug: fix the data race among asynchronous methods --- connection_windows.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/connection_windows.go b/connection_windows.go index 41486ecda..eae62a1aa 100644 --- a/connection_windows.go +++ b/connection_windows.go @@ -87,8 +87,10 @@ func newTCPConn(nc net.Conn, el *eventloop) (c *conn) { func (c *conn) release() { c.ctx = nil c.localAddr = nil - c.remoteAddr = nil - c.rawConn = nil + if c.rawConn != nil { + c.rawConn = nil + c.remoteAddr = nil + } c.inboundBuffer.Done() bbPool.Put(c.buffer) c.buffer = nil From 5746a1455f1f91320510f94ee124705e39c91c9e Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 24 May 2023 20:06:57 +0800 Subject: [PATCH 04/29] chore: update the section of features in READMEs --- README.md | 9 +++++---- README_ZH.md | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e2843943b..94fbf9031 100644 --- a/README.md +++ b/README.md @@ -28,17 +28,18 @@ English | [中文](README_ZH.md) # 🚀 Features -- [x] [High-performance](#-performance) event-loop under networking model of multiple threads/goroutines +- [x] [High-performance](#-performance) event-driven looping based on a networking model of multiple threads/goroutines - [x] Built-in goroutine pool powered by the library [ants](https://github.com/panjf2000/ants) - [x] Lock-free during the entire runtime - [x] Concise and easy-to-use APIs - [x] Efficient, reusable, and elastic memory buffer: (Elastic-)Ring-Buffer, Linked-List-Buffer and Elastic-Mixed-Buffer -- [x] Supporting multiple protocols/IPC mechanisms: `TCP`, `UDP`, and `Unix Domain Socket` -- [x] Supporting multiple load-balancing algorithms: `Round-Robin`, `Source-Addr-Hash`, and `Least-Connections` -- [x] Supporting two event-driven mechanisms: `epoll` on **Linux** and `kqueue` on **FreeBSD/DragonFly/Darwin** +- [x] Multiple protocols/IPC mechanisms: `TCP`, `UDP`, and `Unix Domain Socket` +- [x] Multiple load-balancing algorithms: `Round-Robin`, `Source-Addr-Hash`, and `Least-Connections` +- [x] Two event-driven mechanisms: `epoll` on **Linux** and `kqueue` on **FreeBSD/DragonFly/Darwin** - [x] Flexible ticker event - [x] Implementation of `gnet` Client - [x] **Windows** platform support (For compatibility in development only, do not use it in production) +- [ ] Multiple network addresses binding - [ ] **TLS** support - [ ] [io_uring](https://kernel.dk/io_uring.pdf) support diff --git a/README_ZH.md b/README_ZH.md index d993f7c50..eafdebcdb 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -28,17 +28,18 @@ # 🚀 功能 -- [x] [高性能](#-性能测试) 的基于多线程/Go程网络模型的 event-loop 事件驱动 +- [x] 基于多线程/协程网络模型的[高性能](#-性能测试)事件驱动循环 - [x] 内置 goroutine 池,由开源库 [ants](https://github.com/panjf2000/ants) 提供支持 - [x] 整个生命周期是无锁的 - [x] 简单易用的 APIs - [x] 高效、可重用而且自动伸缩的内存 buffer:(Elastic-)Ring-Buffer, Linked-List-Buffer and Elastic-Mixed-Buffer -- [x] 支持多种网络协议/IPC 机制:`TCP`、`UDP` 和 `Unix Domain Socket` -- [x] 支持多种负载均衡算法:`Round-Robin(轮询)`、`Source-Addr-Hash(源地址哈希)` 和 `Least-Connections(最少连接数)` -- [x] 支持两种事件驱动机制:**Linux** 里的 `epoll` 以及 **FreeBSD/DragonFly/Darwin** 里的 `kqueue` +- [x] 多种网络协议/IPC 机制:`TCP`、`UDP` 和 `Unix Domain Socket` +- [x] 多种负载均衡算法:`Round-Robin(轮询)`、`Source-Addr-Hash(源地址哈希)` 和 `Least-Connections(最少连接数)` +- [x] 两种事件驱动机制:**Linux** 里的 `epoll` 以及 **FreeBSD/DragonFly/Darwin** 里的 `kqueue` - [x] 灵活的事件定时器 - [x] 实现 `gnet` 客户端 - [x] 支持 **Windows** 平台 (仅用于开发环境的兼容性,不要在生产环境中使用) +- [ ] 多网络地址绑定 - [ ] 支持 **TLS** - [ ] 支持 [io_uring](https://kernel.dk/io_uring.pdf) From 9ff529b8438b60b6580576349f4993db2c46386f Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 24 May 2023 20:40:25 +0800 Subject: [PATCH 05/29] chore: code cleanup --- acceptor_unix.go | 2 +- acceptor_windows.go | 4 ++-- engine_unix.go | 46 +++++++++++++++++++++--------------------- engine_windows.go | 33 ++++++++++++++++-------------- eventloop_unix_test.go | 2 +- gnet.go | 2 +- 6 files changed, 46 insertions(+), 43 deletions(-) diff --git a/acceptor_unix.go b/acceptor_unix.go index a909a6650..26063e4cd 100644 --- a/acceptor_unix.go +++ b/acceptor_unix.go @@ -53,7 +53,7 @@ func (eng *engine) accept(fd int, _ netpoll.IOEvent) error { logging.Error(err) } - el := eng.lb.next(remoteAddr) + el := eng.eventLoops.next(remoteAddr) c := newTCPConn(nfd, el, sa, el.ln.addr, remoteAddr) err = el.poller.UrgentTrigger(el.register, c) if err != nil { diff --git a/acceptor_windows.go b/acceptor_windows.go index b53a4c9a7..6b6d01861 100644 --- a/acceptor_windows.go +++ b/acceptor_windows.go @@ -38,7 +38,7 @@ func (eng *engine) listen() (err error) { return } - el := eng.lb.next(addr) + el := eng.eventLoops.next(addr) c := newUDPConn(el, eng.ln.addr, addr) el.ch <- packUDPConn(c, buffer[:n]) } else { @@ -49,7 +49,7 @@ func (eng *engine) listen() (err error) { eng.opts.Logger.Errorf("Accept() fails due to error: %v", err) return } - el := eng.lb.next(tc.RemoteAddr()) + el := eng.eventLoops.next(tc.RemoteAddr()) c := newTCPConn(tc, el) el.ch <- c go func(c *conn, tc net.Conn, el *eventloop) { diff --git a/engine_unix.go b/engine_unix.go index 8fbdd7916..0f7a776fa 100644 --- a/engine_unix.go +++ b/engine_unix.go @@ -32,9 +32,9 @@ import ( type engine struct { ln *listener // the listener for accepting new connections - lb loadBalancer // event-loops for handling events opts *Options // options with engine - mainLoop *eventloop // main event-loop for accepting connections + acceptor *eventloop // main event-loop for accepting connections + eventLoops loadBalancer // event-loops for handling events inShutdown int32 // whether the engine is in shutdown ticker struct { ctx context.Context // context for ticker @@ -66,22 +66,29 @@ func (eng *engine) shutdown(err error) { } func (eng *engine) startEventLoops() { - eng.lb.iterate(func(i int, el *eventloop) bool { + eng.eventLoops.iterate(func(i int, el *eventloop) bool { eng.workerPool.Go(el.run) return true }) } func (eng *engine) closeEventLoops() { - eng.lb.iterate(func(i int, el *eventloop) bool { + eng.eventLoops.iterate(func(i int, el *eventloop) bool { el.ln.close() _ = el.poller.Close() return true }) + if eng.acceptor != nil { + eng.ln.close() + err := eng.acceptor.poller.Close() + if err != nil { + eng.opts.Logger.Errorf("failed to close poller when stopping engine: %v", err) + } + } } func (eng *engine) startSubReactors() { - eng.lb.iterate(func(i int, el *eventloop) bool { + eng.eventLoops.iterate(func(i int, el *eventloop) bool { eng.workerPool.Go(el.activateSubReactor) return true }) @@ -110,7 +117,7 @@ func (eng *engine) activateEventLoops(numEventLoop int) (err error) { if err = el.poller.AddRead(el.ln.packPollAttachment(el.accept)); err != nil { return } - eng.lb.register(el) + eng.eventLoops.register(el) // Start the ticker. if el.idx == 0 && eng.opts.Ticker { @@ -142,7 +149,7 @@ func (eng *engine) activateReactors(numEventLoop int) error { el.buffer = make([]byte, eng.opts.ReadBufferCap) el.connections.init() el.eventHandler = eng.eventHandler - eng.lb.register(el) + eng.eventLoops.register(el) } else { return err } @@ -161,7 +168,7 @@ func (eng *engine) activateReactors(numEventLoop int) error { if err = el.poller.AddRead(eng.ln.packPollAttachment(eng.accept)); err != nil { return err } - eng.mainLoop = el + eng.acceptor = el // Start main reactor in background. eng.workerPool.Go(el.activateMainReactor) @@ -172,7 +179,7 @@ func (eng *engine) activateReactors(numEventLoop int) error { // Start the ticker. if eng.opts.Ticker { eng.workerPool.Go(func() error { - eng.mainLoop.ticker(eng.ticker.ctx) + eng.acceptor.ticker(eng.ticker.ctx) return nil }) } @@ -195,15 +202,15 @@ func (eng *engine) stop(s Engine) { eng.eventHandler.OnShutdown(s) // Notify all event-loops to exit. - eng.lb.iterate(func(i int, el *eventloop) bool { + eng.eventLoops.iterate(func(i int, el *eventloop) bool { err := el.poller.UrgentTrigger(func(_ interface{}) error { return errors.ErrEngineShutdown }, nil) if err != nil { eng.opts.Logger.Errorf("failed to call UrgentTrigger on sub event-loop when stopping engine: %v", err) } return true }) - if eng.mainLoop != nil { - err := eng.mainLoop.poller.UrgentTrigger(func(_ interface{}) error { return errors.ErrEngineShutdown }, nil) + if eng.acceptor != nil { + err := eng.acceptor.poller.UrgentTrigger(func(_ interface{}) error { return errors.ErrEngineShutdown }, nil) if err != nil { eng.opts.Logger.Errorf("failed to call UrgentTrigger on main event-loop when stopping engine: %v", err) } @@ -220,13 +227,6 @@ func (eng *engine) stop(s Engine) { // Close all listeners and pollers of event-loops. eng.closeEventLoops() - if eng.mainLoop != nil { - eng.ln.close() - err := eng.mainLoop.poller.Close() - if err != nil { - eng.opts.Logger.Errorf("failed to close poller when stopping engine: %v", err) - } - } // Put the engine into the shutdown state. atomic.StoreInt32(&eng.inShutdown, 1) @@ -259,11 +259,11 @@ func run(eventHandler EventHandler, listener *listener, options *Options, protoA } switch options.LB { case RoundRobin: - eng.lb = new(roundRobinLoadBalancer) + eng.eventLoops = new(roundRobinLoadBalancer) case LeastConnections: - eng.lb = new(leastConnectionsLoadBalancer) + eng.eventLoops = new(leastConnectionsLoadBalancer) case SourceAddrHash: - eng.lb = new(sourceAddrHashLoadBalancer) + eng.eventLoops = new(sourceAddrHashLoadBalancer) } if eng.opts.Ticker { @@ -294,7 +294,7 @@ func (eng *engine) sendCmd(cmd *asyncCmd, urgent bool) error { if !gfd.Validate(cmd.fd) { return errors.ErrInvalidConn } - el := eng.lb.index(cmd.fd.EventLoopIndex()) + el := eng.eventLoops.index(cmd.fd.EventLoopIndex()) if el == nil { return errors.ErrInvalidConn } diff --git a/engine_windows.go b/engine_windows.go index 734c461fc..f2142c6c8 100644 --- a/engine_windows.go +++ b/engine_windows.go @@ -26,10 +26,10 @@ import ( ) type engine struct { - ln *listener - lb loadBalancer // event-loops for handling events - opts *Options // options with engine - ticker struct { + ln *listener + opts *Options // options with engine + eventLoops loadBalancer // event-loops for handling events + ticker struct { ctx context.Context cancel context.CancelFunc } @@ -56,6 +56,14 @@ func (eng *engine) shutdown(err error) { eng.workerPool.shutdown() } +func (eng *engine) closeEventLoops() { + eng.eventLoops.iterate(func(i int, el *eventloop) bool { + el.ch <- errorx.ErrEngineShutdown + return true + }) + eng.ln.close() +} + func (eng *engine) start(numEventLoop int) error { for i := 0; i < numEventLoop; i++ { el := eventloop{ @@ -65,7 +73,7 @@ func (eng *engine) start(numEventLoop int) error { connections: make(map[*conn]struct{}), eventHandler: eng.eventHandler, } - eng.lb.register(&el) + eng.eventLoops.register(&el) eng.workerPool.Go(el.run) if i == 0 && eng.opts.Ticker { eng.workerPool.Go(func() error { @@ -86,17 +94,12 @@ func (eng *engine) stop(engine Engine) error { eng.opts.Logger.Infof("engine is being shutdown...") eng.eventHandler.OnShutdown(engine) - eng.ln.close() - - eng.lb.iterate(func(i int, el *eventloop) bool { - el.ch <- errorx.ErrEngineShutdown - return true - }) - if eng.ticker.cancel != nil { eng.ticker.cancel() } + eng.closeEventLoops() + if err := eng.workerPool.Wait(); err != nil { eng.opts.Logger.Errorf("engine shutdown error: %v", err) } @@ -131,11 +134,11 @@ func run(eventHandler EventHandler, listener *listener, options *Options, protoA switch options.LB { case RoundRobin: - eng.lb = new(roundRobinLoadBalancer) + eng.eventLoops = new(roundRobinLoadBalancer) case LeastConnections: - eng.lb = new(leastConnectionsLoadBalancer) + eng.eventLoops = new(leastConnectionsLoadBalancer) case SourceAddrHash: - eng.lb = new(sourceAddrHashLoadBalancer) + eng.eventLoops = new(sourceAddrHashLoadBalancer) } if options.Ticker { diff --git a/eventloop_unix_test.go b/eventloop_unix_test.go index bd649522e..1252cc50e 100644 --- a/eventloop_unix_test.go +++ b/eventloop_unix_test.go @@ -129,7 +129,7 @@ func (s *benchmarkServerGC) OnBoot(eng Engine) (action Action) { s.eng = eng go func() { for { - if s.eng.eng.lb.len() == s.elNum && s.eng.CountConnections() == s.elNum*int(s.initConnCount) { + if s.eng.eng.eventLoops.len() == s.elNum && s.eng.CountConnections() == s.elNum*int(s.initConnCount) { break } time.Sleep(time.Millisecond) diff --git a/gnet.go b/gnet.go index cda800fb8..047b4ef98 100644 --- a/gnet.go +++ b/gnet.go @@ -65,7 +65,7 @@ func (e Engine) CountConnections() (count int) { return -1 } - e.eng.lb.iterate(func(i int, el *eventloop) bool { + e.eng.eventLoops.iterate(func(i int, el *eventloop) bool { count += int(el.countConn()) return true }) From 2c69029e60b50b4b7842f077298254277bfc9404 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 28 May 2023 10:13:33 +0800 Subject: [PATCH 06/29] chore: add comments and improve code --- conn_matrix_gcopt.go | 31 +++++++++++++++++-------------- eventloop_unix.go | 1 + 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/conn_matrix_gcopt.go b/conn_matrix_gcopt.go index 2748b71ed..fdb7dcf3b 100644 --- a/conn_matrix_gcopt.go +++ b/conn_matrix_gcopt.go @@ -83,30 +83,33 @@ func (cm *connMatrix) addConn(c *conn, index int) { } func (cm *connMatrix) delConn(c *conn) { - delete(cm.fd2gfd, c.fd) - cm.incCount(c.gfd.ConnMatrixRow(), -1) - if cm.connCounts[c.gfd.ConnMatrixRow()] == 0 { - cm.table[c.gfd.ConnMatrixRow()] = nil + cfd, cgfd := c.fd, c.gfd + + delete(cm.fd2gfd, cfd) + cm.incCount(cgfd.ConnMatrixRow(), -1) + if cm.connCounts[cgfd.ConnMatrixRow()] == 0 { + cm.table[cgfd.ConnMatrixRow()] = nil } else { - cm.table[c.gfd.ConnMatrixRow()][c.gfd.ConnMatrixColumn()] = nil + cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = nil } - if cm.row > c.gfd.ConnMatrixRow() || cm.column > c.gfd.ConnMatrixColumn() { - cm.row, cm.column = c.gfd.ConnMatrixRow(), c.gfd.ConnMatrixColumn() + if cm.row > cgfd.ConnMatrixRow() || cm.column > cgfd.ConnMatrixColumn() { + cm.row, cm.column = cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn() } // Locate the last *conn in table and move it to the deleted location. - if cm.disableCompact || cm.table[c.gfd.ConnMatrixRow()] == nil { // the deleted *conn is the last one, do nothing here. + if cm.disableCompact || cm.table[cgfd.ConnMatrixRow()] == nil { // the deleted *conn is the last one, do nothing here. return } - for row := gfd.ConnMatrixRowMax - 1; row >= c.gfd.ConnMatrixRow(); row-- { + // Traverse backward to find the first non-empty point in the matrix until we reach the deleted position. + for row := gfd.ConnMatrixRowMax - 1; row >= cgfd.ConnMatrixRow(); row-- { if cm.connCounts[row] == 0 { continue } columnMin := -1 - if row == c.gfd.ConnMatrixRow() { - columnMin = c.gfd.ConnMatrixColumn() + if row == cgfd.ConnMatrixRow() { + columnMin = cgfd.ConnMatrixColumn() } for column := gfd.ConnMatrixColumnMax - 1; column > columnMin; column-- { if cm.table[row][column] == nil { @@ -114,12 +117,12 @@ func (cm *connMatrix) delConn(c *conn) { } gFd := cm.table[row][column].gfd - gFd.UpdateIndexes(c.gfd.ConnMatrixRow(), c.gfd.ConnMatrixColumn()) + gFd.UpdateIndexes(cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn()) cm.table[row][column].gfd = gFd cm.fd2gfd[gFd.Fd()] = gFd - cm.table[c.gfd.ConnMatrixRow()][c.gfd.ConnMatrixColumn()] = cm.table[row][column] + cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = cm.table[row][column] cm.incCount(row, -1) - cm.incCount(c.gfd.ConnMatrixRow(), 1) + cm.incCount(cgfd.ConnMatrixRow(), 1) if cm.connCounts[row] == 0 { cm.table[row] = nil diff --git a/eventloop_unix.go b/eventloop_unix.go index 0f7c64bb2..dd434a520 100644 --- a/eventloop_unix.go +++ b/eventloop_unix.go @@ -122,6 +122,7 @@ func (el *eventloop) read(c *conn) error { return nil } +// The default value of UIO_MAXIOV/IOV_MAX is 1024 on Linux and most BSD-like OSs. const iovMax = 1024 func (el *eventloop) write(c *conn) error { From f55fbde1ad84ea4bd635ba0b89619cac48522dec Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 28 May 2023 11:04:23 +0800 Subject: [PATCH 07/29] chore: add a TODO for a quirky failure of test case --- gnet_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gnet_test.go b/gnet_test.go index f535428d4..068fe1173 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -250,6 +250,9 @@ func (s *testServer) OnOpen(c Conn) (out []byte, action Action) { nclients := atomic.AddInt32(&s.connected, 1) if int(nclients) == s.nclients { connCount := s.eng.CountConnections() + // TODO(panjf2000): this assertion is highly unlikely to fail, + // but it does occur on macOS: https://github.com/panjf2000/gnet/actions/runs/5101902107/jobs/9171108782, + // try to investigate the root cause and fix it. require.EqualValuesf(s.tester, s.nclients, connCount, "expected connected clients: %d, but got: %d", s.nclients, connCount) } From 6136138db550481d2034629d31a865202a6b6158 Mon Sep 17 00:00:00 2001 From: gocurr Date: Fri, 9 Jun 2023 13:34:22 +0800 Subject: [PATCH 08/29] internal/math: correct IsPowerOfTwo (#476) --- internal/math/math.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/math/math.go b/internal/math/math.go index 0fe64c7fa..bdd29e547 100644 --- a/internal/math/math.go +++ b/internal/math/math.go @@ -19,9 +19,9 @@ const ( maxintHeadBit = 1 << (bitSize - 2) ) -// IsPowerOfTwo reports whether given integer is a power of two. +// IsPowerOfTwo reports whether the given n is a power of two. func IsPowerOfTwo(n int) bool { - return n&(n-1) == 0 + return n > 0 && n&(n-1) == 0 } // CeilToPowerOfTwo returns n if it is a power-of-two, otherwise the next-highest power-of-two. From e3083990fb327198e2de3f17f1b6f1acfda73538 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Fri, 9 Jun 2023 23:12:17 +0800 Subject: [PATCH 09/29] opt: rearrange the matrix and map of connections --- conn_map.go | 73 ++++++++++++++++++++ conn_matrix.go | 123 +++++++++++++++++++++++++++------ conn_matrix_gcopt.go | 158 ------------------------------------------- 3 files changed, 177 insertions(+), 177 deletions(-) create mode 100644 conn_map.go delete mode 100644 conn_matrix_gcopt.go diff --git a/conn_map.go b/conn_map.go new file mode 100644 index 000000000..c5caf7103 --- /dev/null +++ b/conn_map.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 The Gnet Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build (linux || freebsd || dragonfly || darwin) && !gc_opt +// +build linux freebsd dragonfly darwin +// +build !gc_opt + +package gnet + +import ( + "sync/atomic" + + "github.com/panjf2000/gnet/v2/internal/gfd" +) + +type connMatrix struct { + connCount int32 + connMap map[int]*conn +} + +func (cm *connMatrix) init() { + cm.connMap = make(map[int]*conn) +} + +func (cm *connMatrix) iterate(f func(*conn) bool) { + for _, c := range cm.connMap { + if c != nil { + if !f(c) { + return + } + } + } +} + +func (cm *connMatrix) incCount(_ int, delta int32) { + atomic.AddInt32(&cm.connCount, delta) +} + +func (cm *connMatrix) loadCount() (n int32) { + return atomic.LoadInt32(&cm.connCount) +} + +func (cm *connMatrix) addConn(c *conn, index int) { + c.gfd = gfd.NewGFD(c.fd, index, 0, 0) + cm.connMap[c.fd] = c + cm.incCount(0, 1) +} + +func (cm *connMatrix) delConn(c *conn) { + delete(cm.connMap, c.fd) + cm.incCount(0, -1) +} + +func (cm *connMatrix) getConn(fd int) *conn { + return cm.connMap[fd] +} + +/* +func (cm *connMatrix) getConnByGFD(fd gfd.GFD) *conn { + return cm.connMap[fd.Fd()] +} +*/ diff --git a/conn_matrix.go b/conn_matrix.go index c5caf7103..fdb7dcf3b 100644 --- a/conn_matrix.go +++ b/conn_matrix.go @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (linux || freebsd || dragonfly || darwin) && !gc_opt +//go:build (linux || freebsd || dragonfly || darwin) && gc_opt // +build linux freebsd dragonfly darwin -// +build !gc_opt +// +build gc_opt package gnet @@ -25,49 +25,134 @@ import ( ) type connMatrix struct { - connCount int32 - connMap map[int]*conn + disableCompact bool // disable compaction when it is true + connCounts [gfd.ConnMatrixRowMax]int32 // number of active connections in event-loop + row int // next available row index + column int // next available column index + table [gfd.ConnMatrixRowMax][]*conn // connection matrix of *conn, multiple slices + fd2gfd map[int]gfd.GFD // fd -> gfd.GFD } func (cm *connMatrix) init() { - cm.connMap = make(map[int]*conn) + cm.fd2gfd = make(map[int]gfd.GFD) } func (cm *connMatrix) iterate(f func(*conn) bool) { - for _, c := range cm.connMap { - if c != nil { - if !f(c) { - return + cm.disableCompact = true + defer func() { cm.disableCompact = false }() + for _, conns := range cm.table { + for _, c := range conns { + if c != nil { + if !f(c) { + return + } } } } } -func (cm *connMatrix) incCount(_ int, delta int32) { - atomic.AddInt32(&cm.connCount, delta) +func (cm *connMatrix) incCount(row int, delta int32) { + atomic.AddInt32(&cm.connCounts[row], delta) } func (cm *connMatrix) loadCount() (n int32) { - return atomic.LoadInt32(&cm.connCount) + for i := 0; i < len(cm.connCounts); i++ { + n += atomic.LoadInt32(&cm.connCounts[i]) + } + return } func (cm *connMatrix) addConn(c *conn, index int) { - c.gfd = gfd.NewGFD(c.fd, index, 0, 0) - cm.connMap[c.fd] = c - cm.incCount(0, 1) + if cm.row >= gfd.ConnMatrixRowMax { + return + } + + if cm.table[cm.row] == nil { + cm.table[cm.row] = make([]*conn, gfd.ConnMatrixColumnMax) + } + + c.gfd = gfd.NewGFD(c.fd, index, cm.row, cm.column) + cm.fd2gfd[c.fd] = c.gfd + cm.table[cm.row][cm.column] = c + cm.incCount(cm.row, 1) + + if cm.column++; cm.column == gfd.ConnMatrixColumnMax { + cm.row++ + cm.column = 0 + } } func (cm *connMatrix) delConn(c *conn) { - delete(cm.connMap, c.fd) - cm.incCount(0, -1) + cfd, cgfd := c.fd, c.gfd + + delete(cm.fd2gfd, cfd) + cm.incCount(cgfd.ConnMatrixRow(), -1) + if cm.connCounts[cgfd.ConnMatrixRow()] == 0 { + cm.table[cgfd.ConnMatrixRow()] = nil + } else { + cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = nil + } + if cm.row > cgfd.ConnMatrixRow() || cm.column > cgfd.ConnMatrixColumn() { + cm.row, cm.column = cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn() + } + + // Locate the last *conn in table and move it to the deleted location. + + if cm.disableCompact || cm.table[cgfd.ConnMatrixRow()] == nil { // the deleted *conn is the last one, do nothing here. + return + } + + // Traverse backward to find the first non-empty point in the matrix until we reach the deleted position. + for row := gfd.ConnMatrixRowMax - 1; row >= cgfd.ConnMatrixRow(); row-- { + if cm.connCounts[row] == 0 { + continue + } + columnMin := -1 + if row == cgfd.ConnMatrixRow() { + columnMin = cgfd.ConnMatrixColumn() + } + for column := gfd.ConnMatrixColumnMax - 1; column > columnMin; column-- { + if cm.table[row][column] == nil { + continue + } + + gFd := cm.table[row][column].gfd + gFd.UpdateIndexes(cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn()) + cm.table[row][column].gfd = gFd + cm.fd2gfd[gFd.Fd()] = gFd + cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = cm.table[row][column] + cm.incCount(row, -1) + cm.incCount(cgfd.ConnMatrixRow(), 1) + + if cm.connCounts[row] == 0 { + cm.table[row] = nil + } else { + cm.table[row][column] = nil + } + + cm.row, cm.column = row, column + + return + } + } } func (cm *connMatrix) getConn(fd int) *conn { - return cm.connMap[fd] + gFD, ok := cm.fd2gfd[fd] + if !ok { + return nil + } + if cm.table[gFD.ConnMatrixRow()] == nil { + return nil + } + return cm.table[gFD.ConnMatrixRow()][gFD.ConnMatrixColumn()] } /* func (cm *connMatrix) getConnByGFD(fd gfd.GFD) *conn { - return cm.connMap[fd.Fd()] + if cm.table[fd.ConnMatrixRow()] == nil { + return nil + } + return cm.table[fd.ConnMatrixRow()][fd.ConnMatrixColumn()] } */ diff --git a/conn_matrix_gcopt.go b/conn_matrix_gcopt.go deleted file mode 100644 index fdb7dcf3b..000000000 --- a/conn_matrix_gcopt.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) 2023 The Gnet Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build (linux || freebsd || dragonfly || darwin) && gc_opt -// +build linux freebsd dragonfly darwin -// +build gc_opt - -package gnet - -import ( - "sync/atomic" - - "github.com/panjf2000/gnet/v2/internal/gfd" -) - -type connMatrix struct { - disableCompact bool // disable compaction when it is true - connCounts [gfd.ConnMatrixRowMax]int32 // number of active connections in event-loop - row int // next available row index - column int // next available column index - table [gfd.ConnMatrixRowMax][]*conn // connection matrix of *conn, multiple slices - fd2gfd map[int]gfd.GFD // fd -> gfd.GFD -} - -func (cm *connMatrix) init() { - cm.fd2gfd = make(map[int]gfd.GFD) -} - -func (cm *connMatrix) iterate(f func(*conn) bool) { - cm.disableCompact = true - defer func() { cm.disableCompact = false }() - for _, conns := range cm.table { - for _, c := range conns { - if c != nil { - if !f(c) { - return - } - } - } - } -} - -func (cm *connMatrix) incCount(row int, delta int32) { - atomic.AddInt32(&cm.connCounts[row], delta) -} - -func (cm *connMatrix) loadCount() (n int32) { - for i := 0; i < len(cm.connCounts); i++ { - n += atomic.LoadInt32(&cm.connCounts[i]) - } - return -} - -func (cm *connMatrix) addConn(c *conn, index int) { - if cm.row >= gfd.ConnMatrixRowMax { - return - } - - if cm.table[cm.row] == nil { - cm.table[cm.row] = make([]*conn, gfd.ConnMatrixColumnMax) - } - - c.gfd = gfd.NewGFD(c.fd, index, cm.row, cm.column) - cm.fd2gfd[c.fd] = c.gfd - cm.table[cm.row][cm.column] = c - cm.incCount(cm.row, 1) - - if cm.column++; cm.column == gfd.ConnMatrixColumnMax { - cm.row++ - cm.column = 0 - } -} - -func (cm *connMatrix) delConn(c *conn) { - cfd, cgfd := c.fd, c.gfd - - delete(cm.fd2gfd, cfd) - cm.incCount(cgfd.ConnMatrixRow(), -1) - if cm.connCounts[cgfd.ConnMatrixRow()] == 0 { - cm.table[cgfd.ConnMatrixRow()] = nil - } else { - cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = nil - } - if cm.row > cgfd.ConnMatrixRow() || cm.column > cgfd.ConnMatrixColumn() { - cm.row, cm.column = cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn() - } - - // Locate the last *conn in table and move it to the deleted location. - - if cm.disableCompact || cm.table[cgfd.ConnMatrixRow()] == nil { // the deleted *conn is the last one, do nothing here. - return - } - - // Traverse backward to find the first non-empty point in the matrix until we reach the deleted position. - for row := gfd.ConnMatrixRowMax - 1; row >= cgfd.ConnMatrixRow(); row-- { - if cm.connCounts[row] == 0 { - continue - } - columnMin := -1 - if row == cgfd.ConnMatrixRow() { - columnMin = cgfd.ConnMatrixColumn() - } - for column := gfd.ConnMatrixColumnMax - 1; column > columnMin; column-- { - if cm.table[row][column] == nil { - continue - } - - gFd := cm.table[row][column].gfd - gFd.UpdateIndexes(cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn()) - cm.table[row][column].gfd = gFd - cm.fd2gfd[gFd.Fd()] = gFd - cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = cm.table[row][column] - cm.incCount(row, -1) - cm.incCount(cgfd.ConnMatrixRow(), 1) - - if cm.connCounts[row] == 0 { - cm.table[row] = nil - } else { - cm.table[row][column] = nil - } - - cm.row, cm.column = row, column - - return - } - } -} - -func (cm *connMatrix) getConn(fd int) *conn { - gFD, ok := cm.fd2gfd[fd] - if !ok { - return nil - } - if cm.table[gFD.ConnMatrixRow()] == nil { - return nil - } - return cm.table[gFD.ConnMatrixRow()][gFD.ConnMatrixColumn()] -} - -/* -func (cm *connMatrix) getConnByGFD(fd gfd.GFD) *conn { - if cm.table[fd.ConnMatrixRow()] == nil { - return nil - } - return cm.table[fd.ConnMatrixRow()][fd.ConnMatrixColumn()] -} -*/ From c7a67c5b0c58903cb23a7270d956689f679a0bc7 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Fri, 9 Jun 2023 23:30:38 +0800 Subject: [PATCH 10/29] chore: add a comment for logging.Flusher --- pkg/logging/logger.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go index c7d7852e5..784aba2ec 100644 --- a/pkg/logging/logger.go +++ b/pkg/logging/logger.go @@ -58,6 +58,8 @@ import ( "gopkg.in/natefinch/lumberjack.v2" ) +// Flusher is the callback function which flushes any buffered log entries to the underlying writer. +// It is usually called before the gnet process exits. type Flusher = func() error var ( From 0f6b014ef270bb5c3b585243ba39c251c4fb0bb4 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 11 Jun 2023 22:42:44 +0800 Subject: [PATCH 11/29] chore: update some test cases --- gnet_test.go | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/gnet_test.go b/gnet_test.go index 068fe1173..d323a6ac9 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -246,16 +246,8 @@ func (s *testServer) OnBoot(eng Engine) (action Action) { } func (s *testServer) OnOpen(c Conn) (out []byte, action Action) { + require.GreaterOrEqual(s.tester, s.eng.CountConnections(), int(atomic.AddInt32(&s.connected, 1))) c.SetContext(c) - nclients := atomic.AddInt32(&s.connected, 1) - if int(nclients) == s.nclients { - connCount := s.eng.CountConnections() - // TODO(panjf2000): this assertion is highly unlikely to fail, - // but it does occur on macOS: https://github.com/panjf2000/gnet/actions/runs/5101902107/jobs/9171108782, - // try to investigate the root cause and fix it. - require.EqualValuesf(s.tester, s.nclients, connCount, "expected connected clients: %d, but got: %d", - s.nclients, connCount) - } out = []byte("sweetness\r\n") require.NotNil(s.tester, c.LocalAddr(), "nil local addr") require.NotNil(s.tester, c.RemoteAddr(), "nil remote addr") @@ -277,9 +269,8 @@ func (s *testServer) OnClose(c Conn, err error) (action Action) { require.Equal(s.tester, c.Context(), c, "invalid context") } - atomic.AddInt32(&s.disconnected, 1) - if atomic.LoadInt32(&s.connected) == atomic.LoadInt32(&s.disconnected) && - atomic.LoadInt32(&s.disconnected) == int32(s.nclients) { + if disconnected := atomic.AddInt32(&s.disconnected, 1); disconnected == atomic.LoadInt32(&s.connected) && disconnected == int32(s.nclients) { //nolint:gocritic + require.EqualValues(s.tester, 0, s.eng.CountConnections()) action = Shutdown s.workerPool.Release() } @@ -581,20 +572,26 @@ func TestShutdown(t *testing.T) { type testShutdownServer struct { *BuiltinEventEngine tester *testing.T + eng Engine network string addr string count int - clients int64 + clients int32 N int } +func (t *testShutdownServer) OnBoot(eng Engine) (action Action) { + t.eng = eng + return +} + func (t *testShutdownServer) OnOpen(Conn) (out []byte, action Action) { - atomic.AddInt64(&t.clients, 1) + require.EqualValues(t.tester, atomic.AddInt32(&t.clients, 1), t.eng.CountConnections()) return } func (t *testShutdownServer) OnClose(Conn, error) (action Action) { - atomic.AddInt64(&t.clients, -1) + atomic.AddInt32(&t.clients, -1) return } @@ -610,7 +607,7 @@ func (t *testShutdownServer) OnTick() (delay time.Duration, action Action) { require.Error(t.tester, err) }() } - } else if int(atomic.LoadInt64(&t.clients)) == t.N { + } else if int(atomic.LoadInt32(&t.clients)) == t.N { action = Shutdown } t.count++ @@ -619,7 +616,7 @@ func (t *testShutdownServer) OnTick() (delay time.Duration, action Action) { } func testShutdown(t *testing.T, network, addr string) { - events := &testShutdownServer{tester: t, network: network, addr: addr, N: 10} + events := &testShutdownServer{tester: t, network: network, addr: addr, N: 256} err := Run(events, network+"://"+addr, WithTicker(true), WithReadBufferCap(512), WithWriteBufferCap(512)) assert.NoError(t, err) require.Equal(t, 0, int(events.clients), "did not close all clients") From 9136b4406250a700dbebcd5c4be28131324cee68 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 11 Jun 2023 22:54:55 +0800 Subject: [PATCH 12/29] test: fix the inconsistency results between Linux/BSD and Windows --- eventloop_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eventloop_windows.go b/eventloop_windows.go index a683975cf..5ae95ca3d 100644 --- a/eventloop_windows.go +++ b/eventloop_windows.go @@ -195,12 +195,12 @@ func (el *eventloop) close(c *conn, err error) error { return nil // ignore stale wakes. } + delete(el.connections, c) + el.incConn(-1) action := el.eventHandler.OnClose(c, err) if err := c.rawConn.Close(); err != nil { el.getLogger().Errorf("failed to close connection(%s), error:%v", c.remoteAddr.String(), err) } - delete(el.connections, c) - el.incConn(-1) c.release() return el.handleAction(c, action) From c3df2fb420de920d1e43f1aea78e05dc01a933e8 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 11 Jun 2023 23:19:25 +0800 Subject: [PATCH 13/29] test: eliminate the flaky assertion --- gnet_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gnet_test.go b/gnet_test.go index d323a6ac9..2a935e71b 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -246,7 +246,6 @@ func (s *testServer) OnBoot(eng Engine) (action Action) { } func (s *testServer) OnOpen(c Conn) (out []byte, action Action) { - require.GreaterOrEqual(s.tester, s.eng.CountConnections(), int(atomic.AddInt32(&s.connected, 1))) c.SetContext(c) out = []byte("sweetness\r\n") require.NotNil(s.tester, c.LocalAddr(), "nil local addr") From eca1d0c5ee2e1902e76248a2242a967fc343741f Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Mon, 12 Jun 2023 11:57:48 +0800 Subject: [PATCH 14/29] test: fix the hanging issue --- gnet_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/gnet_test.go b/gnet_test.go index 2a935e71b..2b422866c 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -247,6 +247,7 @@ func (s *testServer) OnBoot(eng Engine) (action Action) { func (s *testServer) OnOpen(c Conn) (out []byte, action Action) { c.SetContext(c) + atomic.AddInt32(&s.connected, 1) out = []byte("sweetness\r\n") require.NotNil(s.tester, c.LocalAddr(), "nil local addr") require.NotNil(s.tester, c.RemoteAddr(), "nil remote addr") From c839bfba734dd460e464b756979a1189b5171037 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Mon, 12 Jun 2023 12:45:23 +0800 Subject: [PATCH 15/29] test: fix a flaky test case of TestShutdown Fixes #477 --- gnet_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnet_test.go b/gnet_test.go index 2b422866c..22eae807b 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -616,7 +616,7 @@ func (t *testShutdownServer) OnTick() (delay time.Duration, action Action) { } func testShutdown(t *testing.T, network, addr string) { - events := &testShutdownServer{tester: t, network: network, addr: addr, N: 256} + events := &testShutdownServer{tester: t, network: network, addr: addr, N: 100} err := Run(events, network+"://"+addr, WithTicker(true), WithReadBufferCap(512), WithWriteBufferCap(512)) assert.NoError(t, err) require.Equal(t, 0, int(events.clients), "did not close all clients") From d98706e6175d9f547d57d4f1a295173328483876 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 14 Jun 2023 22:01:56 +0800 Subject: [PATCH 16/29] opt: make use of the inheritance of file status flags on BSD-like OS --- acceptor_bsd.go | 25 +++++++++++++++++++++++++ acceptor_linux.go | 24 ++++++++++++++++++++++++ acceptor_unix.go | 4 ++-- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 acceptor_bsd.go create mode 100644 acceptor_linux.go diff --git a/acceptor_bsd.go b/acceptor_bsd.go new file mode 100644 index 000000000..df5de320c --- /dev/null +++ b/acceptor_bsd.go @@ -0,0 +1,25 @@ +// Copyright (c) 2023 The Gnet Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build freebsd || dragonfly || darwin +// +build freebsd dragonfly darwin + +package gnet + +// The canonical BSD sockets implementation will inherit file status flags +// from the listening socket, so we don't need to set the non-blocking flag +// for the accepted sockets explicitly. +func setNonBlock(_ int, _ bool) error { + return nil +} diff --git a/acceptor_linux.go b/acceptor_linux.go new file mode 100644 index 000000000..bde964912 --- /dev/null +++ b/acceptor_linux.go @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 The Gnet Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package gnet + +import "golang.org/x/sys/unix" + +func setNonBlock(fd int, nonBlocking bool) error { + return unix.SetNonblock(fd, nonBlocking) +} diff --git a/acceptor_unix.go b/acceptor_unix.go index 26063e4cd..5bba090af 100644 --- a/acceptor_unix.go +++ b/acceptor_unix.go @@ -44,7 +44,7 @@ func (eng *engine) accept(fd int, _ netpoll.IOEvent) error { } } - if err = os.NewSyscallError("fcntl nonblock", unix.SetNonblock(nfd, true)); err != nil { + if err = os.NewSyscallError("fcntl nonblock", setNonBlock(nfd, true)); err != nil { return err } remoteAddr := socket.SockaddrToTCPOrUnixAddr(sa) @@ -83,7 +83,7 @@ func (el *eventloop) accept(fd int, ev netpoll.IOEvent) error { } } - if err = os.NewSyscallError("fcntl nonblock", unix.SetNonblock(nfd, true)); err != nil { + if err = os.NewSyscallError("fcntl nonblock", setNonBlock(nfd, true)); err != nil { return err } remoteAddr := socket.SockaddrToTCPOrUnixAddr(sa) From a147af25df7c7b90064a8fb3655cec501941c0f2 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 14 Jun 2023 22:09:56 +0800 Subject: [PATCH 17/29] chore: clean up the build constraints of linux --- connection_linux.go | 32 +++++++++--------- internal/io/io_linux.go | 32 +++++++++--------- .../netpoll/syscall_epoll_generic_linux.go | 33 ++++++++++--------- internal/netpoll/syscall_epoll_linux.go | 33 ++++++++++--------- 4 files changed, 68 insertions(+), 62 deletions(-) diff --git a/connection_linux.go b/connection_linux.go index a479061e9..b7d6e40a4 100644 --- a/connection_linux.go +++ b/connection_linux.go @@ -1,19 +1,19 @@ -// Copyright (c) 2021 The Gnet Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build linux -// +build linux +/* + * Copyright (c) 2021 The Gnet Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ package gnet diff --git a/internal/io/io_linux.go b/internal/io/io_linux.go index 2f3668a2f..f077d87ff 100644 --- a/internal/io/io_linux.go +++ b/internal/io/io_linux.go @@ -1,19 +1,19 @@ -// Copyright (c) 2021 The Gnet Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build linux -// +build linux +/* + * Copyright (c) 2021 The Gnet Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ package io diff --git a/internal/netpoll/syscall_epoll_generic_linux.go b/internal/netpoll/syscall_epoll_generic_linux.go index bf28d9dda..94496f151 100644 --- a/internal/netpoll/syscall_epoll_generic_linux.go +++ b/internal/netpoll/syscall_epoll_generic_linux.go @@ -1,19 +1,22 @@ -// Copyright (c) 2021 The Gnet Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * Copyright (c) 2021 The Gnet Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -//go:build linux && !arm64 && !riscv64 && poll_opt -// +build linux,!arm64,!riscv64,poll_opt +//go:build !arm64 && !riscv64 && poll_opt +// +build !arm64,!riscv64,poll_opt package netpoll diff --git a/internal/netpoll/syscall_epoll_linux.go b/internal/netpoll/syscall_epoll_linux.go index fcfe4cc47..77937f648 100644 --- a/internal/netpoll/syscall_epoll_linux.go +++ b/internal/netpoll/syscall_epoll_linux.go @@ -1,19 +1,22 @@ -// Copyright (c) 2021 The Gnet Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * Copyright (c) 2021 The Gnet Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -//go:build linux && poll_opt -// +build linux,poll_opt +//go:build poll_opt +// +build poll_opt package netpoll From 9790927c31313d945d39866fba6fc1aa448138c4 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 14 Jun 2023 22:30:06 +0800 Subject: [PATCH 18/29] build: add NetBSD and OpenBSD supports --- README.md | 2 +- README_ZH.md | 2 +- acceptor_bsd.go | 4 ++-- acceptor_unix.go | 4 ++-- client_test.go | 4 ++-- client_unix.go | 4 ++-- conn_map.go | 4 ++-- conn_matrix.go | 4 ++-- conn_matrix_test.go | 4 ++-- connection_bsd.go | 4 ++-- connection_unix.go | 4 ++-- engine_unix.go | 4 ++-- eventloop_unix.go | 4 ++-- eventloop_unix_test.go | 4 ++-- internal/io/io_bsd.go | 4 ++-- internal/netpoll/defs_bsd_32bit.go | 4 +++- internal/netpoll/defs_bsd_64bit.go | 4 +++- internal/netpoll/fd_unix.go | 4 ++-- internal/netpoll/kqueue_default_poller.go | 4 ++-- internal/netpoll/kqueue_events.go | 4 ++-- internal/netpoll/kqueue_optimized_poller.go | 4 ++-- internal/netpoll/poll_data_bsd.go | 4 ++-- internal/netpoll/poll_data_unix.go | 4 ++-- internal/socket/sock_bsd.go | 4 ++-- internal/socket/sock_cloexec.go | 4 ++-- internal/socket/sock_posix.go | 4 ++-- internal/socket/socket.go | 4 ++-- internal/socket/sockopts_posix.go | 4 ++-- internal/socket/sockopts_unix.go | 4 ++-- internal/socket/socktoaddr.go | 4 ++-- internal/socket/tcp_socket.go | 4 ++-- internal/socket/udp_socket.go | 4 ++-- internal/socket/unix_socket.go | 4 ++-- listener_unix.go | 4 ++-- os_unix_test.go | 4 ++-- pkg/buffer/ring/ring_buffer_unix.go | 4 ++-- reactor_default_bsd.go | 4 ++-- reactor_optimized_bsd.go | 4 ++-- 38 files changed, 76 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 94fbf9031..8f3e3fd2b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@
- +
diff --git a/README_ZH.md b/README_ZH.md index eafdebcdb..ebaa307f0 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -3,7 +3,7 @@
- +
diff --git a/acceptor_bsd.go b/acceptor_bsd.go index df5de320c..56b0b6548 100644 --- a/acceptor_bsd.go +++ b/acceptor_bsd.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build freebsd || dragonfly || darwin -// +build freebsd dragonfly darwin +//go:build freebsd || dragonfly || netbsd || openbsd || darwin +// +build freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/acceptor_unix.go b/acceptor_unix.go index 5bba090af..f86b51c1d 100644 --- a/acceptor_unix.go +++ b/acceptor_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/client_test.go b/client_test.go index 3995aba8a..57b0b87c3 100644 --- a/client_test.go +++ b/client_test.go @@ -1,5 +1,5 @@ -//go:build linux || freebsd || dragonfly || darwin || windows -// +build linux freebsd dragonfly darwin windows +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin || windows +// +build linux freebsd dragonfly netbsd openbsd darwin windows package gnet diff --git a/client_unix.go b/client_unix.go index 5ee50cef9..61d2a710e 100644 --- a/client_unix.go +++ b/client_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/conn_map.go b/conn_map.go index c5caf7103..e6e45a050 100644 --- a/conn_map.go +++ b/conn_map.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (linux || freebsd || dragonfly || darwin) && !gc_opt -// +build linux freebsd dragonfly darwin +//go:build (linux || freebsd || dragonfly || netbsd || openbsd || darwin) && !gc_opt +// +build linux freebsd dragonfly netbsd openbsd darwin // +build !gc_opt package gnet diff --git a/conn_matrix.go b/conn_matrix.go index fdb7dcf3b..7293641ab 100644 --- a/conn_matrix.go +++ b/conn_matrix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (linux || freebsd || dragonfly || darwin) && gc_opt -// +build linux freebsd dragonfly darwin +//go:build (linux || freebsd || dragonfly || netbsd || openbsd || darwin) && gc_opt +// +build linux freebsd dragonfly netbsd openbsd darwin // +build gc_opt package gnet diff --git a/conn_matrix_test.go b/conn_matrix_test.go index 5c3fd69d6..17487bb50 100644 --- a/conn_matrix_test.go +++ b/conn_matrix_test.go @@ -1,5 +1,5 @@ -//go:build (linux || freebsd || dragonfly || darwin) && gc_opt -// +build linux freebsd dragonfly darwin +//go:build (linux || freebsd || dragonfly || netbsd || openbsd || darwin) && gc_opt +// +build linux freebsd dragonfly netbsd openbsd darwin // +build gc_opt package gnet diff --git a/connection_bsd.go b/connection_bsd.go index 60c3504d4..36aaabd66 100644 --- a/connection_bsd.go +++ b/connection_bsd.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build freebsd || dragonfly || darwin -// +build freebsd dragonfly darwin +//go:build freebsd || dragonfly || netbsd || openbsd || darwin +// +build freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/connection_unix.go b/connection_unix.go index b92aace4e..23555c906 100644 --- a/connection_unix.go +++ b/connection_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/engine_unix.go b/engine_unix.go index 0f7a776fa..26af89c99 100644 --- a/engine_unix.go +++ b/engine_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/eventloop_unix.go b/eventloop_unix.go index dd434a520..e10b12fbc 100644 --- a/eventloop_unix.go +++ b/eventloop_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/eventloop_unix_test.go b/eventloop_unix_test.go index 1252cc50e..25e8ae64e 100644 --- a/eventloop_unix_test.go +++ b/eventloop_unix_test.go @@ -1,5 +1,5 @@ -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/internal/io/io_bsd.go b/internal/io/io_bsd.go index 69ca6c566..661bb1606 100644 --- a/internal/io/io_bsd.go +++ b/internal/io/io_bsd.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build freebsd || dragonfly || darwin -// +build freebsd dragonfly darwin +//go:build freebsd || dragonfly || netbsd || openbsd || darwin +// +build freebsd dragonfly netbsd openbsd darwin package io diff --git a/internal/netpoll/defs_bsd_32bit.go b/internal/netpoll/defs_bsd_32bit.go index c5f2e6962..2b0fa0b66 100644 --- a/internal/netpoll/defs_bsd_32bit.go +++ b/internal/netpoll/defs_bsd_32bit.go @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || dragonfly || darwin) && (386 || arm || mips || mipsle) +//go:build (freebsd || dragonfly || netbsd || openbsd || darwin) && (386 || arm || mips || mipsle) +// +build freebsd dragonfly netbsd openbsd darwin +// +build 386 arm mips mipsle package netpoll diff --git a/internal/netpoll/defs_bsd_64bit.go b/internal/netpoll/defs_bsd_64bit.go index be97fe600..1bcab7c35 100644 --- a/internal/netpoll/defs_bsd_64bit.go +++ b/internal/netpoll/defs_bsd_64bit.go @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || dragonfly || darwin) && (amd64 || arm64 || ppc64 || ppc64le || mips64 || mips64le || riscv64) +//go:build (freebsd || dragonfly || netbsd || openbsd || darwin) && (amd64 || arm64 || ppc64 || ppc64le || mips64 || mips64le || riscv64) +// +build freebsd dragonfly netbsd openbsd darwin +// +build amd64 arm64 ppc64 ppc64le mips64 mips64le riscv64 package netpoll diff --git a/internal/netpoll/fd_unix.go b/internal/netpoll/fd_unix.go index 5d6a01f24..65fb730a0 100644 --- a/internal/netpoll/fd_unix.go +++ b/internal/netpoll/fd_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package netpoll diff --git a/internal/netpoll/kqueue_default_poller.go b/internal/netpoll/kqueue_default_poller.go index 3f61b5546..59259192d 100644 --- a/internal/netpoll/kqueue_default_poller.go +++ b/internal/netpoll/kqueue_default_poller.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || dragonfly || darwin) && !poll_opt -// +build freebsd dragonfly darwin +//go:build (freebsd || dragonfly || netbsd || openbsd || darwin) && !poll_opt +// +build freebsd dragonfly netbsd openbsd darwin // +build !poll_opt package netpoll diff --git a/internal/netpoll/kqueue_events.go b/internal/netpoll/kqueue_events.go index 3787e569c..52e6f7bdc 100644 --- a/internal/netpoll/kqueue_events.go +++ b/internal/netpoll/kqueue_events.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build freebsd || dragonfly || darwin -// +build freebsd dragonfly darwin +//go:build freebsd || dragonfly || netbsd || openbsd || darwin +// +build freebsd dragonfly netbsd openbsd darwin package netpoll diff --git a/internal/netpoll/kqueue_optimized_poller.go b/internal/netpoll/kqueue_optimized_poller.go index c7efca475..cf8afbef6 100644 --- a/internal/netpoll/kqueue_optimized_poller.go +++ b/internal/netpoll/kqueue_optimized_poller.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || dragonfly || darwin) && poll_opt -// +build freebsd dragonfly darwin +//go:build (freebsd || dragonfly || netbsd || openbsd || darwin) && poll_opt +// +build freebsd dragonfly netbsd openbsd darwin // +build poll_opt package netpoll diff --git a/internal/netpoll/poll_data_bsd.go b/internal/netpoll/poll_data_bsd.go index 25af72d7c..73ce48780 100644 --- a/internal/netpoll/poll_data_bsd.go +++ b/internal/netpoll/poll_data_bsd.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build freebsd || dragonfly || darwin -// +build freebsd dragonfly darwin +//go:build freebsd || dragonfly || netbsd || openbsd || darwin +// +build freebsd dragonfly netbsd openbsd darwin package netpoll diff --git a/internal/netpoll/poll_data_unix.go b/internal/netpoll/poll_data_unix.go index 14373271c..3156d89bf 100644 --- a/internal/netpoll/poll_data_unix.go +++ b/internal/netpoll/poll_data_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package netpoll diff --git a/internal/socket/sock_bsd.go b/internal/socket/sock_bsd.go index 41709e607..6c9de5627 100644 --- a/internal/socket/sock_bsd.go +++ b/internal/socket/sock_bsd.go @@ -13,8 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build freebsd || dragonfly || darwin -// +build freebsd dragonfly darwin +//go:build freebsd || dragonfly || netbsd || openbsd || darwin +// +build freebsd dragonfly netbsd openbsd darwin package socket diff --git a/internal/socket/sock_cloexec.go b/internal/socket/sock_cloexec.go index 656bcc1b0..1c861f95c 100644 --- a/internal/socket/sock_cloexec.go +++ b/internal/socket/sock_cloexec.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly -// +build linux freebsd dragonfly +//go:build linux || freebsd || dragonfly || netbsd || openbsd +// +build linux freebsd dragonfly netbsd openbsd package socket diff --git a/internal/socket/sock_posix.go b/internal/socket/sock_posix.go index 51f81c261..5235c6406 100644 --- a/internal/socket/sock_posix.go +++ b/internal/socket/sock_posix.go @@ -13,8 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package socket diff --git a/internal/socket/socket.go b/internal/socket/socket.go index 3f049df9c..4215304fe 100644 --- a/internal/socket/socket.go +++ b/internal/socket/socket.go @@ -13,8 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin // Package socket provides functions that return fd and net.Addr based on // given the protocol and address with a SO_REUSEPORT option set to the socket. diff --git a/internal/socket/sockopts_posix.go b/internal/socket/sockopts_posix.go index bf77be648..321ac8a1b 100644 --- a/internal/socket/sockopts_posix.go +++ b/internal/socket/sockopts_posix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package socket diff --git a/internal/socket/sockopts_unix.go b/internal/socket/sockopts_unix.go index 2b215c4a6..95184a12a 100644 --- a/internal/socket/sockopts_unix.go +++ b/internal/socket/sockopts_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly -// +build linux freebsd dragonfly +//go:build linux || freebsd || dragonfly || netbsd || openbsd +// +build linux freebsd dragonfly netbsd openbsd package socket diff --git a/internal/socket/socktoaddr.go b/internal/socket/socktoaddr.go index fe54ec17f..36caa6f9c 100644 --- a/internal/socket/socktoaddr.go +++ b/internal/socket/socktoaddr.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package socket diff --git a/internal/socket/tcp_socket.go b/internal/socket/tcp_socket.go index fc30591bf..337b13c65 100644 --- a/internal/socket/tcp_socket.go +++ b/internal/socket/tcp_socket.go @@ -13,8 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package socket diff --git a/internal/socket/udp_socket.go b/internal/socket/udp_socket.go index 978d82183..10c3ba304 100644 --- a/internal/socket/udp_socket.go +++ b/internal/socket/udp_socket.go @@ -13,8 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package socket diff --git a/internal/socket/unix_socket.go b/internal/socket/unix_socket.go index e5848ab6d..b999aaf06 100644 --- a/internal/socket/unix_socket.go +++ b/internal/socket/unix_socket.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package socket diff --git a/listener_unix.go b/listener_unix.go index 4cb5941f2..ceb734187 100644 --- a/listener_unix.go +++ b/listener_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/os_unix_test.go b/os_unix_test.go index b9e1df64e..c8acc8bc3 100644 --- a/os_unix_test.go +++ b/os_unix_test.go @@ -1,5 +1,5 @@ -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package gnet diff --git a/pkg/buffer/ring/ring_buffer_unix.go b/pkg/buffer/ring/ring_buffer_unix.go index a58c85b7a..300e3aca5 100644 --- a/pkg/buffer/ring/ring_buffer_unix.go +++ b/pkg/buffer/ring/ring_buffer_unix.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux || freebsd || dragonfly || darwin -// +build linux freebsd dragonfly darwin +//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin +// +build linux freebsd dragonfly netbsd openbsd darwin package ring diff --git a/reactor_default_bsd.go b/reactor_default_bsd.go index a3cabd401..ab1286337 100644 --- a/reactor_default_bsd.go +++ b/reactor_default_bsd.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || dragonfly || darwin) && !poll_opt -// +build freebsd dragonfly darwin +//go:build (freebsd || dragonfly || netbsd || openbsd || darwin) && !poll_opt +// +build freebsd dragonfly netbsd openbsd darwin // +build !poll_opt package gnet diff --git a/reactor_optimized_bsd.go b/reactor_optimized_bsd.go index ecb54d886..2938fa553 100644 --- a/reactor_optimized_bsd.go +++ b/reactor_optimized_bsd.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build (freebsd || dragonfly || darwin) && poll_opt -// +build freebsd dragonfly darwin +//go:build (freebsd || dragonfly || netbsd || openbsd || darwin) && poll_opt +// +build freebsd dragonfly netbsd openbsd darwin // +build poll_opt package gnet From bdbb661682104076216de19ff743e2d327de5dc2 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 14 Jun 2023 22:41:38 +0800 Subject: [PATCH 19/29] chore: delete the unused code in pkg/buffer/ring --- pkg/buffer/ring/ring_buffer_unix.go | 87 ----------------------------- 1 file changed, 87 deletions(-) delete mode 100644 pkg/buffer/ring/ring_buffer_unix.go diff --git a/pkg/buffer/ring/ring_buffer_unix.go b/pkg/buffer/ring/ring_buffer_unix.go deleted file mode 100644 index 300e3aca5..000000000 --- a/pkg/buffer/ring/ring_buffer_unix.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2021 The Gnet Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build linux || freebsd || dragonfly || netbsd || openbsd || darwin -// +build linux freebsd dragonfly netbsd openbsd darwin - -package ring - -import ( - "golang.org/x/sys/unix" - - "github.com/panjf2000/gnet/v2/internal/io" -) - -// ========================= gnet specific APIs ========================= - -// CopyFromSocket copies data from a socket fd into ring-buffer. -func (rb *Buffer) CopyFromSocket(fd int) (n int, err error) { - if rb.r == rb.w { - if !rb.isEmpty { - rb.grow(rb.size + rb.size/2) - n, err = unix.Read(fd, rb.buf[rb.w:]) - if n > 0 { - rb.w = (rb.w + n) % rb.size - } - return - } - rb.r, rb.w = 0, 0 - n, err = unix.Read(fd, rb.buf) - if n > 0 { - rb.w = (rb.w + n) % rb.size - rb.isEmpty = false - } - return - } - if rb.w < rb.r { - n, err = unix.Read(fd, rb.buf[rb.w:rb.r]) - if n > 0 { - rb.w = (rb.w + n) % rb.size - } - return - } - rb.bs[0] = rb.buf[rb.w:] - rb.bs[1] = rb.buf[:rb.r] - n, err = io.Readv(fd, rb.bs) - if n > 0 { - rb.w = (rb.w + n) % rb.size - } - return -} - -// Rewind moves the data from its tail to head and rewind its pointers of read and write. -func (rb *Buffer) Rewind() (n int) { - if rb.IsEmpty() { - rb.Reset() - return - } - if rb.w == 0 { - if rb.r < rb.size-rb.r { - rb.grow(rb.size + rb.size - rb.r) - return rb.size - rb.r - } - n = copy(rb.buf, rb.buf[rb.r:]) - rb.r = 0 - rb.w = n - } else if rb.size-rb.w < DefaultBufferSize { - if rb.r < rb.w-rb.r { - rb.grow(rb.size + rb.w - rb.r) - return rb.w - rb.r - } - n = copy(rb.buf, rb.buf[rb.r:rb.w]) - rb.r = 0 - rb.w = n - } - return -} From 52eaf886d0c6f16d3429c6a3234d2944c2032cf4 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 14 Jun 2023 22:49:24 +0800 Subject: [PATCH 20/29] chore: fix the misplacement of +build comment --- .../netpoll/syscall_epoll_generic_linux.go | 29 +++++++++---------- internal/netpoll/syscall_epoll_linux.go | 29 +++++++++---------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/internal/netpoll/syscall_epoll_generic_linux.go b/internal/netpoll/syscall_epoll_generic_linux.go index 94496f151..edc88bb1b 100644 --- a/internal/netpoll/syscall_epoll_generic_linux.go +++ b/internal/netpoll/syscall_epoll_generic_linux.go @@ -1,19 +1,16 @@ -/* - * Copyright (c) 2021 The Gnet Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +// Copyright (c) 2021 The Gnet Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //go:build !arm64 && !riscv64 && poll_opt // +build !arm64,!riscv64,poll_opt diff --git a/internal/netpoll/syscall_epoll_linux.go b/internal/netpoll/syscall_epoll_linux.go index 77937f648..4b28ae70d 100644 --- a/internal/netpoll/syscall_epoll_linux.go +++ b/internal/netpoll/syscall_epoll_linux.go @@ -1,19 +1,16 @@ -/* - * Copyright (c) 2021 The Gnet Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +// Copyright (c) 2021 The Gnet Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //go:build poll_opt // +build poll_opt From 3b0ee52061a8bd7d2efe192c47fb3a0550a3b14c Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Tue, 27 Jun 2023 15:34:17 +0800 Subject: [PATCH 21/29] chore: improve some test cases for connMatrix --- conn_matrix.go | 1 + conn_matrix_test.go | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conn_matrix.go b/conn_matrix.go index 7293641ab..70ba4affa 100644 --- a/conn_matrix.go +++ b/conn_matrix.go @@ -107,6 +107,7 @@ func (cm *connMatrix) delConn(c *conn) { if cm.connCounts[row] == 0 { continue } + columnMin := -1 if row == cgfd.ConnMatrixRow() { columnMin = cgfd.ConnMatrixColumn() diff --git a/conn_matrix_test.go b/conn_matrix_test.go index 17487bb50..c081cfa67 100644 --- a/conn_matrix_test.go +++ b/conn_matrix_test.go @@ -105,9 +105,8 @@ func testConnMatrix(t *testing.T, n int) { if !ok { t.Fatalf("missing gfd for fd %d", c.fd) } - if i != gfd.ConnMatrixRow() || j != gfd.ConnMatrixColumn() { - t.Fatalf("unexpected row %d, column %d, expected row %d, column %d", - gfd.ConnMatrixRow(), gfd.ConnMatrixColumn(), i, j) + if gfd != c.gfd { + t.Fatalf("expected gfd: %v, but got gfd: %v", c.gfd, gfd) } } } From 13542872cfd5d30741dce661e43077556d88f765 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Thu, 29 Jun 2023 13:22:03 +0800 Subject: [PATCH 22/29] chore: update the issue template --- .github/ISSUE_TEMPLATE/bug-report.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 8d982d1f9..6d4b0676c 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -65,6 +65,22 @@ body: - BSD validations: required: true + - type: input + id: os-version + attributes: + label: OS version + description: What's the specific version of OS? + placeholder: "Run `uname -srm` command to get the info, for example: Darwin 21.5.0 arm64, Linux 5.4.0-137-generic x86_64" + validations: + required: true + - type: input + id: go-version + attributes: + label: Go version + description: What's the specific version of Go? + placeholder: "Run `go version` command to get the info, for example: go1.20.5 linux/amd64" + validations: + required: true - type: textarea id: logs attributes: From dafe46d72194c3b12f927f96ca50008e364935bf Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Thu, 29 Jun 2023 15:16:47 +0800 Subject: [PATCH 23/29] chore: ignore some cases for GitHub actions of testing --- .github/workflows/test.yml | 8 ++++++++ .github/workflows/test_gc_opt.yml | 8 ++++++++ .github/workflows/test_poll_opt.yml | 8 ++++++++ .github/workflows/test_poll_opt_gc_opt.yml | 8 ++++++++ 4 files changed, 32 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5a9b98950..36a0bc2b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' pull_request: branches: - master @@ -15,6 +19,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' env: GO111MODULE: on diff --git a/.github/workflows/test_gc_opt.yml b/.github/workflows/test_gc_opt.yml index d81287b78..ec9df56d3 100644 --- a/.github/workflows/test_gc_opt.yml +++ b/.github/workflows/test_gc_opt.yml @@ -8,6 +8,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' pull_request: branches: - master @@ -15,6 +19,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' env: GO111MODULE: on diff --git a/.github/workflows/test_poll_opt.yml b/.github/workflows/test_poll_opt.yml index 665077ee9..7527b6ba3 100644 --- a/.github/workflows/test_poll_opt.yml +++ b/.github/workflows/test_poll_opt.yml @@ -8,6 +8,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' pull_request: branches: - master @@ -15,6 +19,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' env: GO111MODULE: on diff --git a/.github/workflows/test_poll_opt_gc_opt.yml b/.github/workflows/test_poll_opt_gc_opt.yml index 7c0a09487..731cddf9b 100644 --- a/.github/workflows/test_poll_opt_gc_opt.yml +++ b/.github/workflows/test_poll_opt_gc_opt.yml @@ -8,6 +8,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' pull_request: branches: - master @@ -15,6 +19,10 @@ on: - 1.x paths-ignore: - '**.md' + - '.github/FUNDING.yml' + - '.github/release-drafter.yml' + - '.github/ISSUE_TEMPLATE/*' + - '.github/workflows/release-drafter.yaml' env: GO111MODULE: on From 2f8dfe515757566916b937f9bc861b6b15d2a60c Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sat, 1 Jul 2023 13:00:00 +0800 Subject: [PATCH 24/29] chore: refine paths-ignore for all GitHub actions --- .github/workflows/codeql.yml | 6 ++++++ .github/workflows/pull-request.yml | 19 +++++++++++++++++++ ...lease-drafter.yaml => release-drafter.yml} | 0 .github/workflows/test.yml | 14 ++++++-------- .github/workflows/test_gc_opt.yml | 14 ++++++-------- .github/workflows/test_poll_opt.yml | 14 ++++++-------- .github/workflows/test_poll_opt_gc_opt.yml | 14 ++++++-------- 7 files changed, 49 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/pull-request.yml rename .github/workflows/{release-drafter.yaml => release-drafter.yml} (100%) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index dc36bf2d4..b2c12d4f4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -8,6 +8,9 @@ on: - 1.x paths-ignore: - '**.md' + - '**.yml' + - '**.yaml' + - '!.github/workflows/codeql.yml' pull_request: branches: - master @@ -15,6 +18,9 @@ on: - 1.x paths-ignore: - '**.md' + - '**.yml' + - '**.yaml' + - '!.github/workflows/codeql.yml' schedule: # ┌───────────── minute (0 - 59) # │ ┌───────────── hour (0 - 23) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 000000000..f9bf06351 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,19 @@ +name: Check pull request target +on: + pull_request: + types: + - opened + - reopened + - synchronize + branches: + - master +jobs: + check-branches: + runs-on: ubuntu-latest + steps: + - name: Check target branch + run: | + if [ ${{ github.head_ref }} != "dev" ]; then + echo "Only pull requests from dev branch are only allowed to be merged into master branch." + exit 1 + fi diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yml similarity index 100% rename from .github/workflows/release-drafter.yaml rename to .github/workflows/release-drafter.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36a0bc2b3..776c3f0f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,10 +8,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test.yml' pull_request: branches: - master @@ -19,10 +18,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test.yml' env: GO111MODULE: on diff --git a/.github/workflows/test_gc_opt.yml b/.github/workflows/test_gc_opt.yml index ec9df56d3..c18da9b61 100644 --- a/.github/workflows/test_gc_opt.yml +++ b/.github/workflows/test_gc_opt.yml @@ -8,10 +8,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test_gc_opt.yml' pull_request: branches: - master @@ -19,10 +18,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test_gc_opt.yml' env: GO111MODULE: on diff --git a/.github/workflows/test_poll_opt.yml b/.github/workflows/test_poll_opt.yml index 7527b6ba3..8f1337d77 100644 --- a/.github/workflows/test_poll_opt.yml +++ b/.github/workflows/test_poll_opt.yml @@ -8,10 +8,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test_poll_opt.yml' pull_request: branches: - master @@ -19,10 +18,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test_poll_opt.yml' env: GO111MODULE: on diff --git a/.github/workflows/test_poll_opt_gc_opt.yml b/.github/workflows/test_poll_opt_gc_opt.yml index 731cddf9b..5af9f93aa 100644 --- a/.github/workflows/test_poll_opt_gc_opt.yml +++ b/.github/workflows/test_poll_opt_gc_opt.yml @@ -8,10 +8,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test_poll_opt_gc_opt.yml' pull_request: branches: - master @@ -19,10 +18,9 @@ on: - 1.x paths-ignore: - '**.md' - - '.github/FUNDING.yml' - - '.github/release-drafter.yml' - - '.github/ISSUE_TEMPLATE/*' - - '.github/workflows/release-drafter.yaml' + - '**.yml' + - '**.yaml' + - '!.github/workflows/test_poll_opt_gc_opt.yml' env: GO111MODULE: on From 6534df29adbd0e51724a7c4acb99bcaf6da42dbc Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 12 Jul 2023 15:45:20 +0800 Subject: [PATCH 25/29] chore: bump ants to v2.8.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 69962a960..f47d80576 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/panjf2000/gnet/v2 require ( - github.com/panjf2000/ants/v2 v2.7.4 + github.com/panjf2000/ants/v2 v2.8.1 github.com/stretchr/testify v1.8.2 github.com/valyala/bytebufferpool v1.0.0 go.uber.org/zap v1.21.0 diff --git a/go.sum b/go.sum index a1055fd27..f10e6e250 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/panjf2000/ants/v2 v2.7.4 h1:mJqMDtMckZltyL458pq81IGNfiDhEgzX5s/lhjwPWIM= -github.com/panjf2000/ants/v2 v2.7.4/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8= +github.com/panjf2000/ants/v2 v2.8.1 h1:C+n/f++aiW8kHCExKlpX6X+okmxKXP7DWLutxuAPuwQ= +github.com/panjf2000/ants/v2 v2.8.1/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From 8b78affe6b4495e04b1b226b4176768dac156ca5 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Thu, 20 Jul 2023 22:17:37 +0800 Subject: [PATCH 26/29] bug: make logging package concurrent-safe Fixes #486 --- gnet_test.go | 28 ++++++++++++++++++++++++++++ pkg/logging/logger.go | 34 ++++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/gnet_test.go b/gnet_test.go index 22eae807b..91391fa65 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" + "golang.org/x/sync/errgroup" gerr "github.com/panjf2000/gnet/v2/pkg/errors" "github.com/panjf2000/gnet/v2/pkg/logging" @@ -1135,6 +1136,33 @@ func (s *testClosedWakeUpServer) OnClose(Conn, error) (action Action) { return } +type testMultiInstLoggerRaceServer struct { + *BuiltinEventEngine +} + +func (t *testMultiInstLoggerRaceServer) OnBoot(_ Engine) (action Action) { + return Shutdown +} + +func TestMultiInstLoggerRace(t *testing.T) { + logger1, _ := zap.NewDevelopment() + events1 := new(testMultiInstLoggerRaceServer) + g := errgroup.Group{} + g.Go(func() error { + err := Run(events1, "tulip://howdy", WithLogger(logger1.Sugar())) + return err + }) + + logger2, _ := zap.NewDevelopment() + events2 := new(testMultiInstLoggerRaceServer) + g.Go(func() error { + err := Run(events2, "tulip://howdy", WithLogger(logger2.Sugar())) + return err + }) + + assert.Error(t, g.Wait()) +} + var errIncompletePacket = errors.New("incomplete packet") type simServer struct { diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go index 784aba2ec..86ad17e93 100644 --- a/pkg/logging/logger.go +++ b/pkg/logging/logger.go @@ -50,6 +50,7 @@ import ( "errors" "os" "strconv" + "strings" "sync" "go.uber.org/zap" @@ -63,6 +64,7 @@ import ( type Flusher = func() error var ( + mu sync.RWMutex defaultLogger Logger defaultLoggingLevel Level defaultFlusher Flusher @@ -171,30 +173,28 @@ func getProdEncoder() zapcore.Encoder { // GetDefaultLogger returns the default logger. func GetDefaultLogger() Logger { + mu.RLock() + defer mu.RUnlock() return defaultLogger } // GetDefaultFlusher returns the default flusher. func GetDefaultFlusher() Flusher { + mu.RLock() + defer mu.RUnlock() return defaultFlusher } -var setupOnce sync.Once - // SetDefaultLoggerAndFlusher sets the default logger and its flusher. -// -// Note that this function should only be called once at the -// start of the program and not thereafter for the entire runtime, -// otherwise it will only keep the first setup. func SetDefaultLoggerAndFlusher(logger Logger, flusher Flusher) { - setupOnce.Do(func() { - defaultLogger, defaultFlusher = logger, flusher - }) + mu.Lock() + defaultLogger, defaultFlusher = logger, flusher + mu.Unlock() } // LogLevel tells what the default logging level is. func LogLevel() string { - return defaultLoggingLevel.String() + return strings.ToUpper(defaultLoggingLevel.String()) } // CreateLoggerAsLocalFile setups the logger by local file path. @@ -227,41 +227,55 @@ func CreateLoggerAsLocalFile(localFilePath string, logLevel Level) (logger Logge // Cleanup does something windup for logger, like closing, flushing, etc. func Cleanup() { + mu.RLock() if defaultFlusher != nil { _ = defaultFlusher() } + mu.RUnlock() } // Error prints err if it's not nil. func Error(err error) { if err != nil { + mu.RLock() defaultLogger.Errorf("error occurs during runtime, %v", err) + mu.RUnlock() } } // Debugf logs messages at DEBUG level. func Debugf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Debugf(format, args...) + mu.RUnlock() } // Infof logs messages at INFO level. func Infof(format string, args ...interface{}) { + mu.RLock() defaultLogger.Infof(format, args...) + mu.RUnlock() } // Warnf logs messages at WARN level. func Warnf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Warnf(format, args...) + mu.RUnlock() } // Errorf logs messages at ERROR level. func Errorf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Errorf(format, args...) + mu.RUnlock() } // Fatalf logs messages at FATAL level. func Fatalf(format string, args ...interface{}) { + mu.RLock() defaultLogger.Fatalf(format, args...) + mu.RUnlock() } // Logger is used for logging formatted messages. From e7a6e8467754b0fa53016cdcda6badb35ad79327 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Thu, 20 Jul 2023 22:53:17 +0800 Subject: [PATCH 27/29] chore: assert with specific error --- gnet_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnet_test.go b/gnet_test.go index 91391fa65..abf5ee569 100644 --- a/gnet_test.go +++ b/gnet_test.go @@ -1160,7 +1160,7 @@ func TestMultiInstLoggerRace(t *testing.T) { return err }) - assert.Error(t, g.Wait()) + assert.ErrorIs(t, g.Wait(), gerr.ErrUnsupportedProtocol) } var errIncompletePacket = errors.New("incomplete packet") From b41adddd470dc822c2c8e30230363f8a709df165 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 13 Aug 2023 17:08:14 +0800 Subject: [PATCH 28/29] chore: bump up modules (#493) * chore: bump up modules * chore: only run tests against the minimum supported version and latest stable version of Go --- .github/workflows/test.yml | 2 +- .github/workflows/test_gc_opt.yml | 2 +- .github/workflows/test_poll_opt.yml | 2 +- .github/workflows/test_poll_opt_gc_opt.yml | 2 +- go.mod | 6 +++--- go.sum | 12 ++++++------ 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 776c3f0f4..97531f944 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,7 +55,7 @@ jobs: strategy: fail-fast: false matrix: - go: ['1.17', '1.18', '1.19', '1.20'] + go: ['1.17', '1.21'] os: - ubuntu-latest - macos-latest diff --git a/.github/workflows/test_gc_opt.yml b/.github/workflows/test_gc_opt.yml index c18da9b61..318736650 100644 --- a/.github/workflows/test_gc_opt.yml +++ b/.github/workflows/test_gc_opt.yml @@ -55,7 +55,7 @@ jobs: strategy: fail-fast: false matrix: - go: ['1.17', '1.18', '1.19', '1.20'] + go: ['1.17', '1.21'] os: - ubuntu-latest - macos-latest diff --git a/.github/workflows/test_poll_opt.yml b/.github/workflows/test_poll_opt.yml index 8f1337d77..53f908da1 100644 --- a/.github/workflows/test_poll_opt.yml +++ b/.github/workflows/test_poll_opt.yml @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - go: ['1.17', '1.18', '1.19', '1.20'] + go: ['1.17', '1.21'] os: [ubuntu-latest, macos-latest] name: Go ${{ matrix.go }} @ ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/.github/workflows/test_poll_opt_gc_opt.yml b/.github/workflows/test_poll_opt_gc_opt.yml index 5af9f93aa..e4d8f2565 100644 --- a/.github/workflows/test_poll_opt_gc_opt.yml +++ b/.github/workflows/test_poll_opt_gc_opt.yml @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - go: ['1.17', '1.18', '1.19', '1.20'] + go: ['1.17', '1.21'] os: [ubuntu-latest, macos-latest] name: Go ${{ matrix.go }} @ ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/go.mod b/go.mod index f47d80576..819ecbc73 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,11 @@ module github.com/panjf2000/gnet/v2 require ( github.com/panjf2000/ants/v2 v2.8.1 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 github.com/valyala/bytebufferpool v1.0.0 go.uber.org/zap v1.21.0 - golang.org/x/sync v0.2.0 - golang.org/x/sys v0.8.0 + golang.org/x/sync v0.3.0 + golang.org/x/sys v0.11.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index f10e6e250..ba271dfd5 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -46,15 +46,15 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 89672dccd5fcbac8a789773c122b6dd4e64c9472 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Fri, 18 Aug 2023 16:49:15 +0800 Subject: [PATCH 29/29] chore: add a TODO about removing the callback in Conn.AsyncWrite with UDP (#494) Also eliminate the defer for callback in Conn.AsyncWrite with UDP --- connection_unix.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/connection_unix.go b/connection_unix.go index 23555c906..053729273 100644 --- a/connection_unix.go +++ b/connection_unix.go @@ -422,12 +422,15 @@ func (c *conn) SetKeepAlivePeriod(d time.Duration) error { func (c *conn) AsyncWrite(buf []byte, callback AsyncCallback) error { if c.isDatagram { - defer func() { - if callback != nil { - _ = callback(nil, nil) - } - }() - return c.sendTo(buf) + err := c.sendTo(buf) + // TODO: it will not go asynchronously with UDP, so calling a callback is needless, + // we may remove this branch in the future, please don't rely on the callback + // to do something important under UDP, if you're working with UDP, just call Conn.Write + // to send back your data. + if callback != nil { + _ = callback(nil, nil) + } + return err } return c.loop.poller.Trigger(c.asyncWrite, &asyncWriteHook{callback, buf}) }