From 280922b49b76fdf343f52674f1b5ab9ccaf89299 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Sun, 16 Jun 2024 15:17:29 +0800
Subject: [PATCH 01/29] chore: Update READMEs
---
README.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++++----
README_ZH.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 168 insertions(+), 14 deletions(-)
diff --git a/README.md b/README.md
index 71469bbc9..e7ebf4f95 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ English | [中文](README_ZH.md)
`gnet` is available as a Go module and we highly recommend that you use `gnet` via [Go Modules](https://go.dev/blog/using-go-modules), with Go 1.11 Modules enabled (Go 1.11+), you can just simply add `import "github.com/panjf2000/gnet/v2"` to the codebase and run `go mod download/go mod tidy` or `go [build|run|test]` to download the necessary dependencies automatically.
-## With v2
+## With v2
```bash
go get -u github.com/panjf2000/gnet/v2
@@ -68,7 +68,44 @@ go get -u github.com/panjf2000/gnet
The following companies/organizations use `gnet` as the underlying network service in production.
-
+
If you have `gnet` integrated into projects, feel free to open a pull request refreshing this list.
@@ -160,7 +197,7 @@ The source code of `gnet` should be distributed under the Apache-2.0 license.
Please read the [Contributing Guidelines](CONTRIBUTING.md) before opening a PR and thank you to all the developers who already made contributions to `gnet`!
-
+
# ⚓ Relevant Articles
@@ -193,7 +230,47 @@ Become a bronze sponsor with a monthly donation of $10 and get your logo on our
# 💴 Patrons
-
+
# 🔑 JetBrains OS licenses
@@ -204,7 +281,7 @@ Become a bronze sponsor with a monthly donation of $10 and get your logo on our
# 🔋 Sponsorship
-
This project is supported by:
-
-
+ This project is supported by:
+
+
\ No newline at end of file
diff --git a/README_ZH.md b/README_ZH.md
index 70f1c2c4d..a6a2b2e7d 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -52,7 +52,7 @@
`gnet` 是一个 Go module,而且我们也强烈推荐通过 [Go Modules](https://go.dev/blog/using-go-modules) 来使用 `gnet`,在开启 Go Modules 支持(Go 1.11+)之后可以通过简单地在代码中写 `import "github.com/panjf2000/gnet/v2"` 来引入 `gnet`,然后执行 `go mod download/go mod tidy` 或者 `go [build|run|test]` 这些命令来自动下载所依赖的包。
-## 使用 v2
+## 使用 v2
```bash
go get -u github.com/panjf2000/gnet/v2
@@ -68,7 +68,44 @@ go get -u github.com/panjf2000/gnet
以下公司/组织在生产环境上使用了 `gnet` 作为底层网络服务。
-
+
如果你的项目也在使用 `gnet`,欢迎给我提 Pull Request 来更新这份列表。
@@ -160,7 +197,7 @@ Test duration : 15s
请在提 PR 之前仔细阅读 [Contributing Guidelines](CONTRIBUTING.md),感谢那些为 `gnet` 贡献过代码的开发者!
-
+
# ⚓ 相关文章
@@ -193,7 +230,47 @@ Test duration : 15s
# 💴 资助者
-
+
# 🔑 JetBrains 开源证书支持
@@ -204,7 +281,7 @@ Test duration : 15s
# 🔋 赞助商
-
本项目由以下机构赞助:
-
-
+ 本项目由以下机构赞助:
+
+
\ No newline at end of file
From 5346527602153e314b558e308d484f9d78c27bf9 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Tue, 18 Jun 2024 18:22:48 +0800
Subject: [PATCH 02/29] chore: upgrade modules and update .gitignore (#613)
---
.gitignore | 3 ++-
go.mod | 6 +++---
go.sum | 12 +++++++-----
3 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/.gitignore b/.gitignore
index acd5ee6c4..8e37a6b8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
-# env
+# IDEs
.idea/
+.vscode/
# dependencies
/node_modules
diff --git a/go.mod b/go.mod
index 02ad938c6..178d229c9 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,12 @@
module github.com/panjf2000/gnet/v2
require (
- github.com/panjf2000/ants/v2 v2.9.0
- github.com/stretchr/testify v1.8.4
+ github.com/panjf2000/ants/v2 v2.10.0
+ github.com/stretchr/testify v1.9.0
github.com/valyala/bytebufferpool v1.0.0
go.uber.org/zap v1.21.0 // don't upgrade this one
golang.org/x/sync v0.7.0
- golang.org/x/sys v0.19.0
+ golang.org/x/sys v0.21.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
diff --git a/go.sum b/go.sum
index 0823447e5..7ba46041c 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.9.0 h1:SztCLkVxBRigbg+vt0S5QvF5vxAbxbKt09/YfAJ0tEo=
-github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
+github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
+github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
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=
@@ -17,13 +17,15 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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.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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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=
@@ -53,8 +55,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
-golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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 36df9ef65a4c9e856d00814888f028dc1a05b309 Mon Sep 17 00:00:00 2001
From: serious-snow <30310631+serious-snow@users.noreply.github.com>
Date: Sat, 22 Jun 2024 18:27:14 +0800
Subject: [PATCH 03/29] bug: fix Conn.Next and Conn.Peek panic on Unix (#616)
Co-authored-by: wangjian
---
connection_unix.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/connection_unix.go b/connection_unix.go
index 68699f16b..cfce3a928 100644
--- a/connection_unix.go
+++ b/connection_unix.go
@@ -325,13 +325,13 @@ func (c *conn) Next(n int) (buf []byte, err error) {
}
head, tail := c.inboundBuffer.Peek(n)
defer c.inboundBuffer.Discard(n) //nolint:errcheck
- if len(head) == n {
+ if len(head) >= n {
return head[:n], err
}
c.loop.cache.Reset()
c.loop.cache.Write(head)
c.loop.cache.Write(tail)
- if inBufferLen == n {
+ if inBufferLen >= n {
return c.loop.cache.Bytes(), err
}
@@ -352,13 +352,13 @@ func (c *conn) Peek(n int) (buf []byte, err error) {
return c.buffer[:n], err
}
head, tail := c.inboundBuffer.Peek(n)
- if len(head) == n {
+ if len(head) >= n {
return head[:n], err
}
c.loop.cache.Reset()
c.loop.cache.Write(head)
c.loop.cache.Write(tail)
- if inBufferLen == n {
+ if inBufferLen >= n {
return c.loop.cache.Bytes(), err
}
From 5205c5f8c18b0897a5d3d8e76491785b3fcd1ab2 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Tue, 25 Jun 2024 18:50:41 +0800
Subject: [PATCH 04/29] test: increase the test code coverage a little bit
(#618)
---
acceptor_unix.go | 46 ++++++++++++++++++++++------------------------
client_test.go | 32 +++++++++++++++++++++++++++++---
client_unix.go | 9 ++++++---
gnet_test.go | 14 +++++++++++++-
4 files changed, 70 insertions(+), 31 deletions(-)
diff --git a/acceptor_unix.go b/acceptor_unix.go
index ae6e9a35e..7622f1a70 100644
--- a/acceptor_unix.go
+++ b/acceptor_unix.go
@@ -31,19 +31,18 @@ import (
func (el *eventloop) accept0(fd int, _ netpoll.IOEvent, _ netpoll.IOFlags) error {
for {
nfd, sa, err := socket.Accept(fd)
- if err != nil {
- switch err {
- case unix.EAGAIN: // the Accept queue has been drained out, we can return now
- return nil
- case unix.EINTR, unix.ECONNRESET, unix.ECONNABORTED:
- // ECONNRESET or ECONNABORTED could indicate that a socket
- // in the Accept queue was closed before we Accept()ed it.
- // It's a silly error, let's retry it.
- continue
- default:
- el.getLogger().Errorf("Accept() failed due to error: %v", err)
- return errors.ErrAcceptSocket
- }
+ switch err {
+ case nil:
+ case unix.EAGAIN: // the Accept queue has been drained out, we can return now
+ return nil
+ case unix.EINTR, unix.ECONNRESET, unix.ECONNABORTED:
+ // ECONNRESET or ECONNABORTED could indicate that a socket
+ // in the Accept queue was closed before we Accept()ed it.
+ // It's a silly error, let's retry it.
+ continue
+ default:
+ el.getLogger().Errorf("Accept() failed due to error: %v", err)
+ return errors.ErrAcceptSocket
}
remoteAddr := socket.SockaddrToTCPOrUnixAddr(sa)
@@ -71,17 +70,16 @@ func (el *eventloop) accept(fd int, ev netpoll.IOEvent, flags netpoll.IOFlags) e
}
nfd, sa, err := socket.Accept(fd)
- if err != nil {
- switch err {
- case unix.EINTR, unix.EAGAIN, unix.ECONNRESET, unix.ECONNABORTED:
- // ECONNRESET or ECONNABORTED could indicate that a socket
- // in the Accept queue was closed before we Accept()ed it.
- // It's a silly error, let's retry it.
- return nil
- default:
- el.getLogger().Errorf("Accept() failed due to error: %v", err)
- return errors.ErrAcceptSocket
- }
+ switch err {
+ case nil:
+ case unix.EINTR, unix.EAGAIN, unix.ECONNRESET, unix.ECONNABORTED:
+ // ECONNRESET or ECONNABORTED could indicate that a socket
+ // in the Accept queue was closed before we Accept()ed it.
+ // It's a silly error, let's retry it.
+ return nil
+ default:
+ el.getLogger().Errorf("Accept() failed due to error: %v", err)
+ return errors.ErrAcceptSocket
}
remoteAddr := socket.SockaddrToTCPOrUnixAddr(sa)
diff --git a/client_test.go b/client_test.go
index 8b56f5494..d1c89f458 100644
--- a/client_test.go
+++ b/client_test.go
@@ -8,6 +8,7 @@ import (
"io"
"math/rand"
"net"
+ "path/filepath"
"sync"
"sync/atomic"
"testing"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "go.uber.org/zap"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
@@ -437,7 +439,6 @@ func runClient(t *testing.T, network, addr string, et, reuseport, multicore, asy
clientEV := &clientEvents{tester: t, packetLen: streamLen, svr: ts}
ts.client, err = NewClient(
clientEV,
- WithLogLevel(logging.DebugLevel),
WithLockOSThread(true),
WithTicker(true),
)
@@ -599,9 +600,22 @@ func testConnWakeImmediately(t *testing.T, client *Client, clientEV *clientEvent
}
func TestWakeConnImmediately(t *testing.T) {
+ currentLogger, currentFlusher := logging.GetDefaultLogger(), logging.GetDefaultFlusher()
+ t.Cleanup(func() {
+ logging.SetDefaultLoggerAndFlusher(currentLogger, currentFlusher) // restore
+ })
+
clientEV := &clientEventsForWake{tester: t}
- client, err := NewClient(clientEV, WithLogLevel(logging.DebugLevel))
+ logPath := filepath.Join(t.TempDir(), "gnet-test-wake-conn-immediately.log")
+ client, err := NewClient(clientEV,
+ WithSocketRecvBuffer(4*1024),
+ WithSocketSendBuffer(4*1024),
+ WithLogPath(logPath),
+ WithLogLevel(logging.WarnLevel),
+ WithReadBufferCap(512),
+ WithWriteBufferCap(512))
assert.NoError(t, err)
+ logging.Cleanup()
err = client.Start()
assert.NoError(t, err)
@@ -614,6 +628,11 @@ func TestWakeConnImmediately(t *testing.T) {
}
func TestClientReadOnEOF(t *testing.T) {
+ currentLogger, currentFlusher := logging.GetDefaultLogger(), logging.GetDefaultFlusher()
+ t.Cleanup(func() {
+ logging.SetDefaultLoggerAndFlusher(currentLogger, currentFlusher) // restore
+ })
+
ln, err := net.Listen("tcp", "127.0.0.1:9999")
assert.NoError(t, err)
defer ln.Close()
@@ -635,7 +654,14 @@ func TestClientReadOnEOF(t *testing.T) {
}, 1),
data: []byte("test"),
}
- cli, err := NewClient(ev)
+ cli, err := NewClient(ev,
+ WithSocketRecvBuffer(4*1024),
+ WithSocketSendBuffer(4*1024),
+ WithTCPNoDelay(TCPDelay),
+ WithTCPKeepAlive(time.Minute),
+ WithLogger(zap.NewExample().Sugar()),
+ WithReadBufferCap(32*1024),
+ WithWriteBufferCap(32*1024))
assert.NoError(t, err)
defer cli.Stop() //nolint:errcheck
diff --git a/client_unix.go b/client_unix.go
index 597d73592..0ce24f463 100644
--- a/client_unix.go
+++ b/client_unix.go
@@ -200,7 +200,8 @@ func (cli *Client) EnrollContext(c net.Conn, ctx interface{}) (Conn, error) {
)
switch c.(type) {
case *net.UnixConn:
- if sockAddr, _, _, err = socket.GetUnixSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String()); err != nil {
+ sockAddr, _, _, err = socket.GetUnixSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
+ if err != nil {
return nil, err
}
ua := c.LocalAddr().(*net.UnixAddr)
@@ -217,12 +218,14 @@ func (cli *Client) EnrollContext(c net.Conn, ctx interface{}) (Conn, error) {
return nil, err
}
}
- if sockAddr, _, _, _, err = socket.GetTCPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String()); err != nil {
+ sockAddr, _, _, _, err = socket.GetTCPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
+ if err != nil {
return nil, err
}
gc = newTCPConn(dupFD, cli.el, sockAddr, c.LocalAddr(), c.RemoteAddr())
case *net.UDPConn:
- if sockAddr, _, _, _, err = socket.GetUDPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String()); err != nil {
+ sockAddr, _, _, _, err = socket.GetUDPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
+ if err != nil {
return nil, err
}
gc = newUDPConn(dupFD, cli.el, c.LocalAddr(), sockAddr, true)
diff --git a/gnet_test.go b/gnet_test.go
index 11c7df997..7c341d45b 100644
--- a/gnet_test.go
+++ b/gnet_test.go
@@ -9,6 +9,7 @@ import (
"io"
"math/rand"
"net"
+ "path/filepath"
"runtime"
"strings"
"sync/atomic"
@@ -883,8 +884,19 @@ func (t *testShutdownServer) OnTick() (delay time.Duration, action Action) {
}
func testShutdown(t *testing.T, network, addr string) {
+ currentLogger, currentFlusher := logging.GetDefaultLogger(), logging.GetDefaultFlusher()
+ t.Cleanup(func() {
+ logging.SetDefaultLoggerAndFlusher(currentLogger, currentFlusher) // restore
+ })
+
events := &testShutdownServer{tester: t, network: network, addr: addr, N: 100}
- err := Run(events, network+"://"+addr, WithTicker(true), WithReadBufferCap(512), WithWriteBufferCap(512))
+ logPath := filepath.Join(t.TempDir(), "gnet-test-shutdown.log")
+ err := Run(events, network+"://"+addr,
+ WithLogPath(logPath),
+ WithLogLevel(logging.WarnLevel),
+ WithTicker(true),
+ WithReadBufferCap(512),
+ WithWriteBufferCap(512))
assert.NoError(t, err)
require.Equal(t, 0, int(events.clients), "did not close all clients")
}
From 66c2259fee470e3cbe2279c2ad7e9d62f372c743 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Tue, 25 Jun 2024 19:38:02 +0800
Subject: [PATCH 05/29] bug: fix the wrong default behavior of TCPNoDelay on
client side (#619)
---
client_test.go | 3 +--
client_unix.go | 4 ++--
gnet_test.go | 2 +-
options.go | 7 +++++--
4 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/client_test.go b/client_test.go
index d1c89f458..1f446501c 100644
--- a/client_test.go
+++ b/client_test.go
@@ -439,6 +439,7 @@ func runClient(t *testing.T, network, addr string, et, reuseport, multicore, asy
clientEV := &clientEvents{tester: t, packetLen: streamLen, svr: ts}
ts.client, err = NewClient(
clientEV,
+ WithTCPNoDelay(TCPNoDelay),
WithLockOSThread(true),
WithTicker(true),
)
@@ -456,7 +457,6 @@ func runClient(t *testing.T, network, addr string, et, reuseport, multicore, asy
WithReusePort(reuseport),
WithTicker(true),
WithTCPKeepAlive(time.Minute*1),
- WithTCPNoDelay(TCPDelay),
WithLoadBalancing(lb))
assert.NoError(t, err)
}
@@ -657,7 +657,6 @@ func TestClientReadOnEOF(t *testing.T) {
cli, err := NewClient(ev,
WithSocketRecvBuffer(4*1024),
WithSocketSendBuffer(4*1024),
- WithTCPNoDelay(TCPDelay),
WithTCPKeepAlive(time.Minute),
WithLogger(zap.NewExample().Sugar()),
WithReadBufferCap(32*1024),
diff --git a/client_unix.go b/client_unix.go
index 0ce24f463..2d82f8e98 100644
--- a/client_unix.go
+++ b/client_unix.go
@@ -208,8 +208,8 @@ func (cli *Client) EnrollContext(c net.Conn, ctx interface{}) (Conn, error) {
ua.Name = c.RemoteAddr().String() + "." + strconv.Itoa(dupFD)
gc = newTCPConn(dupFD, cli.el, sockAddr, c.LocalAddr(), c.RemoteAddr())
case *net.TCPConn:
- if cli.opts.TCPNoDelay == TCPDelay {
- if err = socket.SetNoDelay(dupFD, 0); err != nil {
+ if cli.opts.TCPNoDelay == TCPNoDelay {
+ if err = socket.SetNoDelay(dupFD, 1); err != nil {
return nil, err
}
}
diff --git a/gnet_test.go b/gnet_test.go
index 7c341d45b..3147bb70f 100644
--- a/gnet_test.go
+++ b/gnet_test.go
@@ -643,7 +643,7 @@ func runServer(t *testing.T, addrs []string, et, reuseport, multicore, async, wr
WithReusePort(reuseport),
WithTicker(true),
WithTCPKeepAlive(time.Minute),
- WithTCPNoDelay(TCPDelay),
+ WithTCPNoDelay(TCPNoDelay),
WithLoadBalancing(lb))
} else {
err = Run(ts,
diff --git a/options.go b/options.go
index 86caef7fb..ef50639d1 100644
--- a/options.go
+++ b/options.go
@@ -98,9 +98,12 @@ type Options struct {
// TCPNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's algorithm).
+ // When this option is assign to TCPNoDelay, TCP_NODELAY socket option will
+ // be turned on, on the contrary, if it is assigned to TCPDelay, the socket
+ // option will be turned off.
//
- // The default is true (no delay), meaning that data is sent
- // as soon as possible after a write operation.
+ // The default is TCPNoDelay, meaning that TCP_NODELAY is turned on and data
+ // will not be buffered but sent as soon as possible after a write operation.
TCPNoDelay TCPSocketOpt
// SocketRecvBuffer sets the maximum socket receive buffer in bytes.
From 8a80aaf09d71364a09c4c51dee62628d6f1d6b66 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Wed, 26 Jun 2024 11:24:43 +0800
Subject: [PATCH 06/29] doc: fix a few typos and update the comments for
Options
---
options.go | 76 ++++++++++++++++++++++++++++--------------------------
1 file changed, 40 insertions(+), 36 deletions(-)
diff --git a/options.go b/options.go
index ef50639d1..ed1f8e2ec 100644
--- a/options.go
+++ b/options.go
@@ -46,21 +46,23 @@ type Options struct {
// Multicore indicates whether the engine will be effectively created with multi-cores, if so,
// then you must take care with synchronizing memory between all event callbacks, otherwise,
- // it will run the engine with single thread. The number of threads in the engine will be automatically
- // assigned to the value of logical CPUs usable by the current process.
+ // it will run the engine with single thread. The number of threads in the engine will be
+ // automatically assigned to the number of usable logical CPUs that can be leveraged by the
+ // current process.
Multicore bool
- // NumEventLoop is set up to start the given number of event-loop goroutine.
- // Note: Setting up NumEventLoop will override Multicore.
+ // NumEventLoop is set up to start the given number of event-loop goroutines.
+ // Note that a non-negative NumEventLoop will override Multicore.
NumEventLoop int
- // LB represents the load-balancing algorithm used when assigning new connections.
+ // LB represents the load-balancing algorithm used when assigning new connections
+ // to event loops.
LB LoadBalancing
- // ReuseAddr indicates whether to set up the SO_REUSEADDR socket option.
+ // ReuseAddr indicates whether to set the SO_REUSEADDR socket option.
ReuseAddr bool
- // ReusePort indicates whether to set up the SO_REUSEPORT socket option.
+ // ReusePort indicates whether to set the SO_REUSEPORT socket option.
ReusePort bool
// MulticastInterfaceIndex is the index of the interface name where the multicast UDP addresses will be bound to.
@@ -77,28 +79,29 @@ type Options struct {
ReadBufferCap int
// WriteBufferCap is the maximum number of bytes that a static outbound buffer can hold,
- // if the data exceeds this value, the overflow will be stored in the elastic linked list buffer.
+ // if the data exceeds this value, the overflow bytes will be stored in the elastic linked list buffer.
// The default value is 64KB.
//
// Note that WriteBufferCap will always be converted to the least power of two integer value greater than
// or equal to its real amount.
WriteBufferCap int
- // LockOSThread is used to determine whether each I/O event-loop is associated to an OS thread, it is useful when you
- // need some kind of mechanisms like thread local storage, or invoke certain C libraries (such as graphics lib: GLib)
- // that require thread-level manipulation via cgo, or want all I/O event-loops to actually run in parallel for a
- // potential higher performance.
+ // LockOSThread is used to determine whether each I/O event-loop should be associated to an OS thread,
+ // it is useful when you need some kind of mechanisms like thread local storage, or invoke certain C
+ // libraries (such as graphics lib: GLib) that require thread-level manipulation via cgo, or want all I/O
+ // event-loops to actually run in parallel for a potential higher performance.
LockOSThread bool
// Ticker indicates whether the ticker has been set up.
Ticker bool
- // TCPKeepAlive sets up a duration for (SO_KEEPALIVE) socket option.
+ // TCPKeepAlive enable the TCP keep-alive mechanism (SO_KEEPALIVE) and set its value
+ // on TCP_KEEPIDLE, 1/5 of its value on TCP_KEEPINTVL, and 5 on TCP_KEEPCNT.
TCPKeepAlive time.Duration
// TCPNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's algorithm).
- // When this option is assign to TCPNoDelay, TCP_NODELAY socket option will
+ // When this option is assigned to TCPNoDelay, TCP_NODELAY socket option will
// be turned on, on the contrary, if it is assigned to TCPDelay, the socket
// option will be turned off.
//
@@ -106,20 +109,21 @@ type Options struct {
// will not be buffered but sent as soon as possible after a write operation.
TCPNoDelay TCPSocketOpt
- // SocketRecvBuffer sets the maximum socket receive buffer in bytes.
+ // SocketRecvBuffer sets the maximum socket receive buffer of kernel in bytes.
SocketRecvBuffer int
- // SocketSendBuffer sets the maximum socket send buffer in bytes.
+ // SocketSendBuffer sets the maximum socket send buffer of kernel in bytes.
SocketSendBuffer int
- // LogPath the local path where logs will be written, this is the easiest way to set up logging,
- // gnet instantiates a default uber-go/zap logger with this given log path, you are also allowed to employ
- // you own logger during the lifetime by implementing the following log.Logger interface.
+ // LogPath specifies a local path where logs will be written, this is the easiest
+ // way to set up logging, gnet instantiates a default uber-go/zap logger with this
+ // given log path, you are also allowed to employ your own logger during the lifetime
+ // by implementing the following logging.Logger interface.
//
- // Note that this option can be overridden by the option Logger.
+ // Note that this option can be overridden by a non-nil option Logger.
LogPath string
- // LogLevel indicates the logging level, it should be used along with LogPath.
+ // LogLevel specifies the logging level, it should be used along with LogPath.
LogLevel logging.Level
// Logger is the customized logger for logging info, if it is not set,
@@ -139,63 +143,63 @@ func WithOptions(options Options) Option {
}
}
-// WithMulticore sets up multi-cores in gnet engine.
+// WithMulticore enables multi-cores mode for gnet engine.
func WithMulticore(multicore bool) Option {
return func(opts *Options) {
opts.Multicore = multicore
}
}
-// WithLockOSThread sets up LockOSThread mode for I/O event-loops.
+// WithLockOSThread enables LockOSThread mode for I/O event-loops.
func WithLockOSThread(lockOSThread bool) Option {
return func(opts *Options) {
opts.LockOSThread = lockOSThread
}
}
-// WithReadBufferCap sets up ReadBufferCap for reading bytes.
+// WithReadBufferCap sets ReadBufferCap for reading bytes.
func WithReadBufferCap(readBufferCap int) Option {
return func(opts *Options) {
opts.ReadBufferCap = readBufferCap
}
}
-// WithWriteBufferCap sets up WriteBufferCap for pending bytes.
+// WithWriteBufferCap sets WriteBufferCap for pending bytes.
func WithWriteBufferCap(writeBufferCap int) Option {
return func(opts *Options) {
opts.WriteBufferCap = writeBufferCap
}
}
-// WithLoadBalancing sets up the load-balancing algorithm in gnet engine.
+// WithLoadBalancing picks the load-balancing algorithm for gnet engine.
func WithLoadBalancing(lb LoadBalancing) Option {
return func(opts *Options) {
opts.LB = lb
}
}
-// WithNumEventLoop sets up NumEventLoop in gnet engine.
+// WithNumEventLoop sets the number of event loops for gnet engine.
func WithNumEventLoop(numEventLoop int) Option {
return func(opts *Options) {
opts.NumEventLoop = numEventLoop
}
}
-// WithReusePort sets up SO_REUSEPORT socket option.
+// WithReusePort sets SO_REUSEPORT socket option.
func WithReusePort(reusePort bool) Option {
return func(opts *Options) {
opts.ReusePort = reusePort
}
}
-// WithReuseAddr sets up SO_REUSEADDR socket option.
+// WithReuseAddr sets SO_REUSEADDR socket option.
func WithReuseAddr(reuseAddr bool) Option {
return func(opts *Options) {
opts.ReuseAddr = reuseAddr
}
}
-// WithTCPKeepAlive sets up the SO_KEEPALIVE socket option with duration.
+// WithTCPKeepAlive enables the TCP keep-alive mechanism and sets its values.
func WithTCPKeepAlive(tcpKeepAlive time.Duration) Option {
return func(opts *Options) {
opts.TCPKeepAlive = tcpKeepAlive
@@ -209,42 +213,42 @@ func WithTCPNoDelay(tcpNoDelay TCPSocketOpt) Option {
}
}
-// WithSocketRecvBuffer sets the maximum socket receive buffer in bytes.
+// WithSocketRecvBuffer sets the maximum socket receive buffer of kernel in bytes.
func WithSocketRecvBuffer(recvBuf int) Option {
return func(opts *Options) {
opts.SocketRecvBuffer = recvBuf
}
}
-// WithSocketSendBuffer sets the maximum socket send buffer in bytes.
+// WithSocketSendBuffer sets the maximum socket send buffer of kernel in bytes.
func WithSocketSendBuffer(sendBuf int) Option {
return func(opts *Options) {
opts.SocketSendBuffer = sendBuf
}
}
-// WithTicker indicates that a ticker is set.
+// WithTicker indicates whether a ticker is currently set.
func WithTicker(ticker bool) Option {
return func(opts *Options) {
opts.Ticker = ticker
}
}
-// WithLogPath is an option to set up the local path of log file.
+// WithLogPath specifies a local path for logging file.
func WithLogPath(fileName string) Option {
return func(opts *Options) {
opts.LogPath = fileName
}
}
-// WithLogLevel is an option to set up the logging level.
+// WithLogLevel specifies the logging level for the local logging file.
func WithLogLevel(lvl logging.Level) Option {
return func(opts *Options) {
opts.LogLevel = lvl
}
}
-// WithLogger sets up a customized logger.
+// WithLogger specifies a customized logger.
func WithLogger(logger logging.Logger) Option {
return func(opts *Options) {
opts.Logger = logger
From 1ed4d08a988375011ad893690e7f18ba6d3065fa Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Wed, 3 Jul 2024 10:54:09 +0800
Subject: [PATCH 07/29] opt: close file descriptor after OnClose() (#622)
Fixes #621
---
eventloop_unix.go | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/eventloop_unix.go b/eventloop_unix.go
index 71361100d..617cf9162 100644
--- a/eventloop_unix.go
+++ b/eventloop_unix.go
@@ -244,6 +244,11 @@ func (el *eventloop) close(c *conn, err error) (rerr error) {
return // ignore stale connections
}
+ el.connections.delConn(c)
+ if el.eventHandler.OnClose(c, err) == Shutdown {
+ rerr = errorx.ErrEngineShutdown
+ }
+
// Send residual data in buffer back to the remote before actually closing the connection.
for !c.outboundBuffer.IsEmpty() {
iov, _ := c.outboundBuffer.Peek(0)
@@ -258,22 +263,26 @@ func (el *eventloop) close(c *conn, err error) (rerr error) {
}
err0, err1 := el.poller.Delete(c.fd), unix.Close(c.fd)
+ var errStr strings.Builder
if err0 != nil {
- rerr = fmt.Errorf("failed to delete fd=%d from poller in event-loop(%d): %v", c.fd, el.idx, err0)
+ err0 = fmt.Errorf("failed to delete fd=%d from poller in event-loop(%d): %v",
+ c.fd, el.idx, os.NewSyscallError("delete", err0))
+ errStr.WriteString(err0.Error())
+ errStr.WriteString(" | ")
}
if err1 != nil {
- err1 = fmt.Errorf("failed to close fd=%d in event-loop(%d): %v", c.fd, el.idx, os.NewSyscallError("close", err1))
+ err1 = fmt.Errorf("failed to close fd=%d in event-loop(%d): %v",
+ c.fd, el.idx, os.NewSyscallError("close", err1))
+ errStr.WriteString(err1.Error())
+ }
+ if errStr.Len() > 0 {
if rerr != nil {
- rerr = errors.New(rerr.Error() + " & " + err1.Error())
+ el.getLogger().Errorf(strings.TrimSuffix(errStr.String(), " | "))
} else {
- rerr = err1
+ rerr = errors.New(strings.TrimSuffix(errStr.String(), " | "))
}
}
- el.connections.delConn(c)
- if el.eventHandler.OnClose(c, err) == Shutdown {
- rerr = errorx.ErrEngineShutdown
- }
c.release()
return
From 70472bf1e73eb7460a47243aa509f5d368e427be Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Wed, 3 Jul 2024 20:36:47 +0800
Subject: [PATCH 08/29] opt: close file descriptor after OnClose() for UDP
(#624)
Updates #621
Follows up #622
This PR also refines `eventloop.close()` and fixes the potential UDP socket leaks for Windows clients.
---
client_windows.go | 2 +-
connection_windows.go | 1 -
eventloop_unix.go | 61 +++++++++++++++----------------------------
eventloop_windows.go | 29 +++++++-------------
4 files changed, 32 insertions(+), 61 deletions(-)
diff --git a/client_windows.go b/client_windows.go
index 33e0566ff..d9cbbde4b 100644
--- a/client_windows.go
+++ b/client_windows.go
@@ -205,7 +205,7 @@ func (cli *Client) EnrollContext(nc net.Conn, ctx interface{}) (gc Conn, err err
c := newUDPConn(cli.el, nil, nc.LocalAddr(), nc.RemoteAddr())
c.SetContext(ctx)
c.rawConn = nc
- cli.el.ch <- &openConn{c: c, isDatagram: true, cb: func() { close(connOpened) }}
+ cli.el.ch <- &openConn{c: c, cb: func() { close(connOpened) }}
go func(uc net.Conn, el *eventloop) {
var buffer [0x10000]byte
for {
diff --git a/connection_windows.go b/connection_windows.go
index e46498f9d..efdfc22c9 100644
--- a/connection_windows.go
+++ b/connection_windows.go
@@ -45,7 +45,6 @@ type udpConn struct {
type openConn struct {
c *conn
cb func()
- isDatagram bool
}
type conn struct {
diff --git a/eventloop_unix.go b/eventloop_unix.go
index 617cf9162..97475302c 100644
--- a/eventloop_unix.go
+++ b/eventloop_unix.go
@@ -226,28 +226,13 @@ loop:
return nil
}
-func (el *eventloop) close(c *conn, err error) (rerr error) {
- if addr := c.localAddr; addr != nil && strings.HasPrefix(c.localAddr.Network(), "udp") {
- rerr = el.poller.Delete(c.fd)
- if _, ok := el.listeners[c.fd]; !ok {
- rerr = unix.Close(c.fd)
- el.connections.delConn(c)
- }
- if el.eventHandler.OnClose(c, err) == Shutdown {
- return errorx.ErrEngineShutdown
- }
- c.release()
- return
- }
-
+func (el *eventloop) close(c *conn, err error) error {
if !c.opened || el.connections.getConn(c.fd) == nil {
- return // ignore stale connections
+ return nil // ignore stale connections
}
el.connections.delConn(c)
- if el.eventHandler.OnClose(c, err) == Shutdown {
- rerr = errorx.ErrEngineShutdown
- }
+ action := el.eventHandler.OnClose(c, err)
// Send residual data in buffer back to the remote before actually closing the connection.
for !c.outboundBuffer.IsEmpty() {
@@ -262,8 +247,10 @@ func (el *eventloop) close(c *conn, err error) (rerr error) {
}
}
- err0, err1 := el.poller.Delete(c.fd), unix.Close(c.fd)
+ c.release()
+
var errStr strings.Builder
+ err0, err1 := el.poller.Delete(c.fd), unix.Close(c.fd)
if err0 != nil {
err0 = fmt.Errorf("failed to delete fd=%d from poller in event-loop(%d): %v",
c.fd, el.idx, os.NewSyscallError("delete", err0))
@@ -276,16 +263,10 @@ func (el *eventloop) close(c *conn, err error) (rerr error) {
errStr.WriteString(err1.Error())
}
if errStr.Len() > 0 {
- if rerr != nil {
- el.getLogger().Errorf(strings.TrimSuffix(errStr.String(), " | "))
- } else {
- rerr = errors.New(strings.TrimSuffix(errStr.String(), " | "))
- }
+ return errors.New(strings.TrimSuffix(errStr.String(), " | "))
}
- c.release()
-
- return
+ return el.handleAction(c, action)
}
func (el *eventloop) wake(c *conn) error {
@@ -333,19 +314,6 @@ func (el *eventloop) ticker(ctx context.Context) {
}
}
-func (el *eventloop) handleAction(c *conn, action Action) error {
- switch action {
- case None:
- return nil
- case Close:
- return el.close(c, nil)
- case Shutdown:
- return errorx.ErrEngineShutdown
- default:
- return nil
- }
-}
-
func (el *eventloop) readUDP(fd int, _ netpoll.IOEvent, _ netpoll.IOFlags) error {
n, sa, err := unix.Recvfrom(fd, el.buffer, 0)
if err != nil {
@@ -372,6 +340,19 @@ func (el *eventloop) readUDP(fd int, _ netpoll.IOEvent, _ netpoll.IOFlags) error
return nil
}
+func (el *eventloop) handleAction(c *conn, action Action) error {
+ switch action {
+ case None:
+ return nil
+ case Close:
+ return el.close(c, nil)
+ case Shutdown:
+ return errorx.ErrEngineShutdown
+ default:
+ return nil
+ }
+}
+
/*
func (el *eventloop) execCmd(itf interface{}) (err error) {
cmd := itf.(*asyncCmd)
diff --git a/eventloop_windows.go b/eventloop_windows.go
index ea0f87377..93cadabf3 100644
--- a/eventloop_windows.go
+++ b/eventloop_windows.go
@@ -19,7 +19,6 @@ import (
"context"
"errors"
"runtime"
- "strings"
"sync/atomic"
"time"
@@ -97,10 +96,8 @@ func (el *eventloop) open(oc *openConn) error {
}
c := oc.c
- if !oc.isDatagram {
- el.connections[c] = struct{}{}
- el.incConn(1)
- }
+ el.connections[c] = struct{}{}
+ el.incConn(1)
out, action := el.eventHandler.OnOpen(c)
if out != nil {
@@ -188,28 +185,22 @@ func (el *eventloop) wake(c *conn) error {
}
func (el *eventloop) close(c *conn, err error) error {
- if addr := c.localAddr; addr != nil && strings.HasPrefix(addr.Network(), "udp") {
- action := el.eventHandler.OnClose(c, err)
- if c.rawConn != nil {
- if err := c.rawConn.Close(); err != nil {
- el.getLogger().Errorf("failed to close connection(%s), error:%v", c.remoteAddr.String(), err)
- }
- }
- c.release()
- return el.handleAction(c, action)
- }
-
- if _, ok := el.connections[c]; !ok {
+ if _, ok := el.connections[c]; c.localAddr == nil || !ok {
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)
+ err = nil
+
+ if c.rawConn != nil {
+ err = c.rawConn.Close()
}
c.release()
+ if err != nil {
+ return err
+ }
return el.handleAction(c, action)
}
From 42cc9a80607e27216f310457cff3fee643f10b61 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Wed, 3 Jul 2024 20:44:17 +0800
Subject: [PATCH 09/29] opt: prevent server-side UDP sockets in
eventloop.close() on Windows
Follows up #624
---
eventloop_windows.go | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/eventloop_windows.go b/eventloop_windows.go
index 93cadabf3..74bea1a46 100644
--- a/eventloop_windows.go
+++ b/eventloop_windows.go
@@ -185,7 +185,7 @@ func (el *eventloop) wake(c *conn) error {
}
func (el *eventloop) close(c *conn, err error) error {
- if _, ok := el.connections[c]; c.localAddr == nil || !ok {
+ if _, ok := el.connections[c]; c.rawConn == nil || !ok {
return nil // ignore stale wakes.
}
@@ -194,9 +194,7 @@ func (el *eventloop) close(c *conn, err error) error {
action := el.eventHandler.OnClose(c, err)
err = nil
- if c.rawConn != nil {
- err = c.rawConn.Close()
- }
+ err = c.rawConn.Close()
c.release()
if err != nil {
return err
From e91e9b344b10f8eea9696697ef2a63a8ec700e90 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Wed, 3 Jul 2024 21:50:10 +0800
Subject: [PATCH 10/29] windows: eliminate the redundant assignment in
eventloop.close()
---
eventloop_windows.go | 2 --
1 file changed, 2 deletions(-)
diff --git a/eventloop_windows.go b/eventloop_windows.go
index 74bea1a46..ea053a87d 100644
--- a/eventloop_windows.go
+++ b/eventloop_windows.go
@@ -192,8 +192,6 @@ func (el *eventloop) close(c *conn, err error) error {
delete(el.connections, c)
el.incConn(-1)
action := el.eventHandler.OnClose(c, err)
- err = nil
-
err = c.rawConn.Close()
c.release()
if err != nil {
From c7fa1458463ab421f5dd93bbc77f4269faa32283 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Wed, 3 Jul 2024 22:00:47 +0800
Subject: [PATCH 11/29] windows: wrap the error with context info in
eventloop.close()
---
eventloop_windows.go | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/eventloop_windows.go b/eventloop_windows.go
index ea053a87d..565f3ef30 100644
--- a/eventloop_windows.go
+++ b/eventloop_windows.go
@@ -18,6 +18,7 @@ import (
"bytes"
"context"
"errors"
+ "fmt"
"runtime"
"sync/atomic"
"time"
@@ -195,7 +196,7 @@ func (el *eventloop) close(c *conn, err error) error {
err = c.rawConn.Close()
c.release()
if err != nil {
- return err
+ return fmt.Errorf("failed to close connection=%s in event-loop(%d): %v", c.remoteAddr, el.idx, err)
}
return el.handleAction(c, action)
From a0d1ed75bf6bb8da01a36e9426b5118761b1a46e Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Thu, 4 Jul 2024 17:17:22 +0800
Subject: [PATCH 12/29] doc: update the roadmap
For #318
---
README.md | 8 +++++++-
README_ZH.md | 10 ++++++++--
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index e7ebf4f95..e90ef78b0 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,8 @@ English | [中文](README_ZH.md)
# 🚀 Features
+## 🦖 Milestone
+
- [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
@@ -43,8 +45,12 @@ English | [中文](README_ZH.md)
- [x] Running on `Linux`, `macOS`, `Windows`, and *BSD: `Darwin`/`DragonFlyBSD`/`FreeBSD`/`NetBSD`/`OpenBSD`
- [x] **Edge-triggered** I/O support
- [x] Multiple network addresses binding
+
+## 🕊 Roadmap
+
- [ ] **TLS** support
-- [ ] [io_uring](https://kernel.dk/io_uring.pdf) support
+- [ ] [io_uring](https://github.com/axboe/liburing/wiki/io_uring-and-networking-in-2023) support
+- [ ] **KCP** support
***Windows version of `gnet` should only be used in development for developing and testing, it shouldn't be used in production.***
diff --git a/README_ZH.md b/README_ZH.md
index a6a2b2e7d..aec90e2cd 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -31,6 +31,8 @@
# 🚀 功能
+## 🦖 当前支持
+
- [x] 基于多线程/协程网络模型的[高性能](#-性能测试)事件驱动循环
- [x] 内置 goroutine 池,由开源库 [ants](https://github.com/panjf2000/ants) 提供支持
- [x] 整个生命周期是无锁的
@@ -43,8 +45,12 @@
- [x] 支持 `Linux`, `macOS`, `Windows` 和 *BSD 操作系统: `Darwin`/`DragonFlyBSD`/`FreeBSD`/`NetBSD`/`OpenBSD`
- [x] **Edge-triggered** I/O 支持
- [x] 多网络地址绑定
-- [ ] **TLS** 支持
-- [ ] [io_uring](https://kernel.dk/io_uring.pdf) 支持
+
+## 🕊 未来计划
+
+- [ ] 支持 **TLS**
+- [ ] 支持 [io_uring](https://github.com/axboe/liburing/wiki/io_uring-and-networking-in-2023)
+- [ ] 支持 **KCP**
***`gnet` 的 Windows 版本应该仅用于开发阶段的开发和测试,切勿用于生产环境***。
From 7162941088af404fb71b3a9452cfd072a581ee03 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Fri, 5 Jul 2024 14:22:51 +0800
Subject: [PATCH 13/29] doc: add new use case
---
README.md | 23 ++++++++++++++---------
README_ZH.md | 23 ++++++++++++++---------
2 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
index e90ef78b0..1466ef73b 100644
--- a/README.md
+++ b/README.md
@@ -78,27 +78,27 @@ The following companies/organizations use `gnet` as the underlying network servi
-
-
+
+
|
-
+
|
-
-
+
+
|
-
-
-
+
|
+
+
@@ -109,11 +109,16 @@ The following companies/organizations use `gnet` as the underlying network servi
|
+
+
+
+
+ |
-If you have `gnet` integrated into projects, feel free to open a pull request refreshing this list.
+If your company is also using `gnet` in production, please help us enrich this list by opening a pull request.
# 📊 Performance
diff --git a/README_ZH.md b/README_ZH.md
index aec90e2cd..0ac69c15e 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -78,27 +78,27 @@ go get -u github.com/panjf2000/gnet
-
-
+
+
|
-
+
|
-
-
+
+
|
-
-
-
+
|
+
+
@@ -109,11 +109,16 @@ go get -u github.com/panjf2000/gnet
|
+
+
+
+
+ |
-如果你的项目也在使用 `gnet`,欢迎给我提 Pull Request 来更新这份列表。
+如果你的公司也在生产环境上使用 `gnet`,欢迎提 Pull Request 来丰富这份列表。
# 📊 性能测试
From 31cbb950833d3d02af0363dc56cb7efc517e16e9 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Fri, 5 Jul 2024 15:16:55 +0800
Subject: [PATCH 14/29] Fix a few broken image links
---
README.md | 2 +-
README_ZH.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 1466ef73b..3cf81cf2c 100644
--- a/README.md
+++ b/README.md
@@ -111,7 +111,7 @@ The following companies/organizations use `gnet` as the underlying network servi
-
+
|
diff --git a/README_ZH.md b/README_ZH.md
index 0ac69c15e..7be4f5236 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -111,7 +111,7 @@ go get -u github.com/panjf2000/gnet
-
+
|
From 66ec945d617e85d65e1debaf1e51ad1d674b3d62 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Thu, 11 Jul 2024 09:27:28 +0800
Subject: [PATCH 15/29] doc: add new use case
---
README.md | 5 +++++
README_ZH.md | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/README.md b/README.md
index 3cf81cf2c..01fd29412 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,11 @@ The following companies/organizations use `gnet` as the underlying network servi
+
+
+
+
+ |
diff --git a/README_ZH.md b/README_ZH.md
index 7be4f5236..347ed58bf 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -114,6 +114,11 @@ go get -u github.com/panjf2000/gnet
+
+
+
+
+ |
From ca1713031a1032154f32b90cce516740805769eb Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Tue, 23 Jul 2024 07:06:57 +0800
Subject: [PATCH 16/29] chore: update READMEs
---
README.md | 4 ++--
README_ZH.md | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 01fd29412..b64b68bf5 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ go get -u github.com/panjf2000/gnet
# 🎡 Use cases
-The following companies/organizations use `gnet` as the underlying network service in production.
+The following corporations/organizations use `gnet` as the underlying network service in production.
@@ -123,7 +123,7 @@ The following companies/organizations use `gnet` as the underlying network servi
-If your company is also using `gnet` in production, please help us enrich this list by opening a pull request.
+If you're also using `gnet` in production, please help us enrich this list by opening a pull request.
# 📊 Performance
diff --git a/README_ZH.md b/README_ZH.md
index 347ed58bf..96143a775 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -123,7 +123,7 @@ go get -u github.com/panjf2000/gnet
-如果你的公司也在生产环境上使用 `gnet`,欢迎提 Pull Request 来丰富这份列表。
+如果你也正在生产环境上使用 `gnet`,欢迎提 Pull Request 来丰富这份列表。
# 📊 性能测试
From 2750d402b7df42e81805aa2e6ed965be3a6d6df3 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Tue, 30 Jul 2024 12:13:09 +0800
Subject: [PATCH 17/29] chore: update READMEs
---
README.md | 16 ++++++++--------
README_ZH.md | 16 ++++++++--------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index b64b68bf5..439338a05 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,11 @@ The following corporations/organizations use `gnet` as the underlying network se
+
+
+
+
+ |
@@ -92,30 +97,25 @@ The following corporations/organizations use `gnet` as the underlying network se
|
+
+
|
-
-
|
-
-
-
-
- |
|
-
+
|
diff --git a/README_ZH.md b/README_ZH.md
index 96143a775..f5f22c867 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -82,6 +82,11 @@ go get -u github.com/panjf2000/gnet
+
+
+
+
+ |
@@ -92,30 +97,25 @@ go get -u github.com/panjf2000/gnet
|
+
+
|
-
-
|
-
-
-
-
- |
|
-
+
|
From 7b23091b035d0d6a2ea15732ed271023d56302ca Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Sun, 18 Aug 2024 09:55:03 +0800
Subject: [PATCH 18/29] chore: clean up needless test code
---
gnet_test.go | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/gnet_test.go b/gnet_test.go
index 3147bb70f..50abb6e0c 100644
--- a/gnet_test.go
+++ b/gnet_test.go
@@ -501,9 +501,8 @@ func (s *testServer) OnClose(c Conn, err error) (action Action) {
if err != nil {
logging.Debugf("error occurred on closed, %v\n", err)
}
- if c.LocalAddr().Network() != "udp" {
- require.Equal(s.tester, c.Context(), c, "invalid context")
- }
+
+ require.Equal(s.tester, c.Context(), c, "invalid context")
atomic.AddInt32(&s.disconnected, 1)
return
From b210186c29032b3c42f9920c3e987252163ae31a Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Mon, 19 Aug 2024 11:16:10 +0800
Subject: [PATCH 19/29] Move the list of patrons elsewhere
Relocated to https://andypan.me/donation/#-patrons
---
README.md | 44 --------------------------------------------
README_ZH.md | 44 --------------------------------------------
2 files changed, 88 deletions(-)
diff --git a/README.md b/README.md
index 439338a05..bdba1a6f3 100644
--- a/README.md
+++ b/README.md
@@ -244,50 +244,6 @@ Become a bronze sponsor with a monthly donation of $10 and get your logo on our
-# 💴 Patrons
-
-
-
# 🔑 JetBrains OS licenses
`gnet` had been being developed with `GoLand` IDE under the **free JetBrains Open Source license(s)** granted by JetBrains s.r.o., hence I would like to express my thanks here.
diff --git a/README_ZH.md b/README_ZH.md
index f5f22c867..fe5a4f28f 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -244,50 +244,6 @@ Test duration : 15s
-# 💴 资助者
-
-
-
# 🔑 JetBrains 开源证书支持
`gnet` 项目一直以来都是在 JetBrains 公司旗下的 GoLand 集成开发环境中进行开发,基于 **free JetBrains Open Source license(s)** 正版免费授权,在此表达我的谢意。
From 8c129aeb226d1758e6f85d33a6f1b289652e1356 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Sat, 21 Sep 2024 20:21:21 +0800
Subject: [PATCH 20/29] chore: clean up unused code and deprecated function
calls
---
eventloop_unix_test.go | 2 +-
gnet.go | 2 --
gnet_test.go | 12 +++++++++---
pkg/errors/errors.go | 2 --
4 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/eventloop_unix_test.go b/eventloop_unix_test.go
index e21bb83f1..c166dde0a 100644
--- a/eventloop_unix_test.go
+++ b/eventloop_unix_test.go
@@ -275,7 +275,7 @@ func (s *testServerGC) GC(secs int) {
runtime.GC()
gcTime := time.Since(now)
gcAllTime += gcTime
- s.tester.Log(s.tester.Name(), s.network, " server gc:", gcTime, ", average gc time:", gcAllTime/gcAllCount)
+ s.tester.Log(s.tester.Name(), s.network, "server gc:", gcTime, "average gc time:", gcAllTime/gcAllCount)
if time.Since(gcStart) >= time.Second*time.Duration(secs) {
break
}
diff --git a/gnet.go b/gnet.go
index 3e2d9ab9a..99f6f4038 100644
--- a/gnet.go
+++ b/gnet.go
@@ -293,8 +293,6 @@ type Socket interface {
// algorithm).
// The default is true (no delay), meaning that data is sent as soon as possible after a Write.
SetNoDelay(noDelay bool) error
- // CloseRead() error
- // CloseWrite() error
}
// Conn is an interface of underlying connection.
diff --git a/gnet_test.go b/gnet_test.go
index 50abb6e0c..e26f085fb 100644
--- a/gnet_test.go
+++ b/gnet_test.go
@@ -1206,9 +1206,15 @@ type testStopServer struct {
*BuiltinEventEngine
tester *testing.T
network, addr, protoAddr string
+ eng Engine
action bool
}
+func (t *testStopServer) OnBoot(eng Engine) (action Action) {
+ t.eng = eng
+ return
+}
+
func (t *testStopServer) OnClose(Conn, error) (action Action) {
logging.Debugf("closing connection...")
return
@@ -1237,7 +1243,7 @@ func (t *testStopServer) OnTick() (delay time.Duration, action Action) {
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
- logging.Debugf("stop engine...", Stop(ctx, t.protoAddr))
+ logging.Debugf("stop engine...", t.eng.Stop(ctx))
}()
// waiting the engine shutdown.
@@ -1365,7 +1371,7 @@ type testClosedWakeUpServer struct {
clientClosed chan struct{}
}
-func (s *testClosedWakeUpServer) OnBoot(_ Engine) (action Action) {
+func (s *testClosedWakeUpServer) OnBoot(eng Engine) (action Action) {
go func() {
c, err := net.Dial(s.network, s.addr)
require.NoError(s.tester, err)
@@ -1380,7 +1386,7 @@ func (s *testClosedWakeUpServer) OnBoot(_ Engine) (action Action) {
close(s.clientClosed)
<-s.serverClosed
- logging.Debugf("stop engine...", Stop(context.TODO(), s.protoAddr))
+ logging.Debugf("stop engine...", eng.Stop(context.TODO()))
}()
return None
diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go
index 835ddedf0..3aaf1b6ba 100644
--- a/pkg/errors/errors.go
+++ b/pkg/errors/errors.go
@@ -35,8 +35,6 @@ var (
ErrUnsupportedUDPProtocol = errors.New("gnet: only udp/udp4/udp6 are supported")
// ErrUnsupportedUDSProtocol occurs when trying to use an unsupported Unix protocol.
ErrUnsupportedUDSProtocol = errors.New("gnet: only unix is supported")
- // ErrUnsupportedPlatform occurs when running gnet on an unsupported platform.
- ErrUnsupportedPlatform = errors.New("gnet: unsupported platform in gnet")
// ErrUnsupportedOp occurs when calling some methods that has not been implemented yet.
ErrUnsupportedOp = errors.New("gnet: unsupported operation")
// ErrNegativeSize occurs when trying to pass a negative size to a buffer.
From 894263821665ed6dfe3cb38ab9e461823be15f0b Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Sat, 21 Sep 2024 20:48:17 +0800
Subject: [PATCH 21/29] test: deprecate math/rand.Seed and math/rand.Read
---
.github/workflows/test.yml | 4 +--
.github/workflows/test_gc_opt.yml | 4 +--
.github/workflows/test_poll_opt.yml | 4 +--
.github/workflows/test_poll_opt_gc_opt.yml | 4 +--
client_test.go | 4 +--
gnet_test.go | 7 ++--
os_unix_test.go | 4 +--
pkg/buffer/elastic/elastic_buffer_test.go | 23 +++++++------
pkg/buffer/linkedlist/llbuffer_test.go | 29 +++++++++-------
pkg/buffer/ring/ring_buffer_test.go | 40 +++++++++++++---------
10 files changed, 68 insertions(+), 55 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 202ac0c89..752c62135 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -47,9 +47,9 @@ jobs:
cache: false
- name: Setup and run golangci-lint
- uses: golangci/golangci-lint-action@v4
+ uses: golangci/golangci-lint-action@v6
with:
- version: v1.56.2
+ version: v1.61.0
args: -v -E gofumpt -E gocritic -E misspell -E revive -E godot --timeout 5m
test:
needs: lint
diff --git a/.github/workflows/test_gc_opt.yml b/.github/workflows/test_gc_opt.yml
index 36e592aa5..a494a1589 100644
--- a/.github/workflows/test_gc_opt.yml
+++ b/.github/workflows/test_gc_opt.yml
@@ -47,9 +47,9 @@ jobs:
cache: false
- name: Setup and run golangci-lint
- uses: golangci/golangci-lint-action@v4
+ uses: golangci/golangci-lint-action@v6
with:
- version: v1.56.2
+ version: v1.61.0
args: -v -E gofumpt -E gocritic -E misspell -E revive -E godot --timeout 5m
test:
needs: lint
diff --git a/.github/workflows/test_poll_opt.yml b/.github/workflows/test_poll_opt.yml
index 91092992a..22991b967 100644
--- a/.github/workflows/test_poll_opt.yml
+++ b/.github/workflows/test_poll_opt.yml
@@ -46,9 +46,9 @@ jobs:
cache: false
- name: Setup and run golangci-lint
- uses: golangci/golangci-lint-action@v4
+ uses: golangci/golangci-lint-action@v6
with:
- version: v1.56.2
+ version: v1.61.0
args: -v -E gofumpt -E gocritic -E misspell -E revive -E godot
test:
needs: lint
diff --git a/.github/workflows/test_poll_opt_gc_opt.yml b/.github/workflows/test_poll_opt_gc_opt.yml
index 3cefb09a5..1d658f449 100644
--- a/.github/workflows/test_poll_opt_gc_opt.yml
+++ b/.github/workflows/test_poll_opt_gc_opt.yml
@@ -46,9 +46,9 @@ jobs:
cache: false
- name: Setup and run golangci-lint
- uses: golangci/golangci-lint-action@v4
+ uses: golangci/golangci-lint-action@v6
with:
- version: v1.56.2
+ version: v1.61.0
args: -v -E gofumpt -E gocritic -E misspell -E revive -E godot
test:
needs: lint
diff --git a/client_test.go b/client_test.go
index 1f446501c..3f270ba0e 100644
--- a/client_test.go
+++ b/client_test.go
@@ -5,6 +5,7 @@ package gnet
import (
"bytes"
+ crand "crypto/rand"
"io"
"math/rand"
"net"
@@ -462,7 +463,6 @@ func runClient(t *testing.T, network, addr string, et, reuseport, multicore, asy
}
func startGnetClient(t *testing.T, cli *Client, network, addr string, multicore, async, netDial bool) {
- rand.Seed(time.Now().UnixNano())
var (
c Conn
err error
@@ -492,7 +492,7 @@ func startGnetClient(t *testing.T, cli *Client, network, addr string, multicore,
if network == "udp" {
reqData = reqData[:datagramLen]
}
- _, err = rand.Read(reqData)
+ _, err = crand.Read(reqData)
require.NoError(t, err)
err = c.AsyncWrite(reqData, nil)
require.NoError(t, err)
diff --git a/gnet_test.go b/gnet_test.go
index e26f085fb..3e20b0c00 100644
--- a/gnet_test.go
+++ b/gnet_test.go
@@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"context"
+ crand "crypto/rand"
"encoding/binary"
"errors"
"io"
@@ -660,7 +661,6 @@ func runServer(t *testing.T, addrs []string, et, reuseport, multicore, async, wr
}
func startClient(t *testing.T, network, addr string, multicore, async bool) {
- rand.Seed(time.Now().UnixNano())
c, err := net.Dial(network, addr)
require.NoError(t, err)
defer c.Close()
@@ -678,7 +678,7 @@ func startClient(t *testing.T, network, addr string, multicore, async bool) {
if network == "udp" {
reqData = reqData[:datagramLen]
}
- _, err = rand.Read(reqData)
+ _, err = crand.Read(reqData)
require.NoError(t, err)
_, err = c.Write(reqData)
require.NoError(t, err)
@@ -1659,7 +1659,6 @@ func runSimServer(t *testing.T, addr string, et bool, nclients, packetSize, pack
}
func runSimClient(t *testing.T, network, addr string, packetSize, batch int) {
- rand.Seed(time.Now().UnixNano())
c, err := net.Dial(network, addr)
require.NoError(t, err)
defer c.Close()
@@ -1695,7 +1694,7 @@ func batchSendAndRecv(t *testing.T, c net.Conn, rd *bufio.Reader, packetSize, ba
)
for i := 0; i < batch; i++ {
req := make([]byte, packetSize)
- _, err := rand.Read(req)
+ _, err := crand.Read(req)
require.NoError(t, err)
requests = append(requests, req)
packet, _ := codec.Encode(req)
diff --git a/os_unix_test.go b/os_unix_test.go
index 0612cfd06..696ec1514 100644
--- a/os_unix_test.go
+++ b/os_unix_test.go
@@ -5,6 +5,7 @@ package gnet
import (
"context"
+ crand "crypto/rand"
"errors"
"fmt"
"math/rand"
@@ -104,7 +105,6 @@ type testMcastServer struct {
}
func (s *testMcastServer) startMcastClient() {
- rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
c, err := net.Dial("udp", s.addr)
@@ -117,7 +117,7 @@ func (s *testMcastServer) startMcastClient() {
start := time.Now()
for time.Since(start) < duration {
reqData := make([]byte, 1024)
- _, err = rand.Read(reqData)
+ _, err = crand.Read(reqData)
require.NoError(s.t, err)
_, err = c.Write(reqData)
require.NoError(s.t, err)
diff --git a/pkg/buffer/elastic/elastic_buffer_test.go b/pkg/buffer/elastic/elastic_buffer_test.go
index f000ac00d..80c219fac 100644
--- a/pkg/buffer/elastic/elastic_buffer_test.go
+++ b/pkg/buffer/elastic/elastic_buffer_test.go
@@ -2,10 +2,10 @@ package elastic
import (
"bytes"
+ crand "crypto/rand"
"math/rand"
"runtime"
"testing"
- "time"
"github.com/stretchr/testify/require"
)
@@ -15,8 +15,8 @@ func TestMixedBuffer_Basic(t *testing.T) {
mb, _ := New(maxStaticSize)
const dataLen = 5 * 1024
data := make([]byte, dataLen)
- rand.Seed(time.Now().Unix())
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
n, err := mb.Write(data)
require.NoError(t, err)
require.EqualValues(t, dataLen, n)
@@ -27,7 +27,8 @@ func TestMixedBuffer_Basic(t *testing.T) {
mb.Reset(-1)
newDataLen := rbn + 2*1024
data = make([]byte, newDataLen)
- rand.Read(data)
+ _, err = crand.Read(data)
+ require.NoError(t, err)
n, err = mb.Write(data)
require.NoError(t, err)
require.EqualValues(t, newDataLen, n)
@@ -71,7 +72,8 @@ func TestMixedBuffer_Basic(t *testing.T) {
n := rand.Intn(512) + 128
cum += n
data := make([]byte, n)
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
buf.Write(data)
if i < 3 {
headCum += n
@@ -107,15 +109,16 @@ func TestMixedBuffer_ReadFrom(t *testing.T) {
mb, _ := New(maxStaticSize)
const dataLen = 2 * 1024
data := make([]byte, dataLen)
- rand.Seed(time.Now().Unix())
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
r := bytes.NewReader(data)
n, err := mb.ReadFrom(r)
require.NoError(t, err)
require.EqualValues(t, dataLen, n)
require.EqualValues(t, dataLen, mb.Buffered())
newData := make([]byte, dataLen)
- rand.Read(newData)
+ _, err = crand.Read(newData)
+ require.NoError(t, err)
r.Reset(newData)
n, err = mb.ReadFrom(r)
require.NoError(t, err)
@@ -155,12 +158,12 @@ func TestMixedBuffer_WriteTo(t *testing.T) {
buf bytes.Buffer
)
- rand.Seed(time.Now().Unix())
for i := 0; i < maxBlocks; i++ {
n := rand.Intn(512) + 128
cum += n
data := make([]byte, n)
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
buf.Write(data)
if i < 3 {
headCum += n
diff --git a/pkg/buffer/linkedlist/llbuffer_test.go b/pkg/buffer/linkedlist/llbuffer_test.go
index 1a755ca0d..4caabaa20 100644
--- a/pkg/buffer/linkedlist/llbuffer_test.go
+++ b/pkg/buffer/linkedlist/llbuffer_test.go
@@ -2,9 +2,9 @@ package linkedlist
import (
"bytes"
+ crand "crypto/rand"
"math/rand"
"testing"
- "time"
"github.com/stretchr/testify/require"
)
@@ -16,12 +16,12 @@ func TestLinkedListBuffer_Basic(t *testing.T) {
cum int
buf bytes.Buffer
)
- rand.Seed(time.Now().Unix())
for i := 0; i < maxBlocks; i++ {
n := rand.Intn(1024) + 128
cum += n
data := make([]byte, n)
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
llb.PushBack(data)
buf.Write(data)
}
@@ -39,8 +39,10 @@ func TestLinkedListBuffer_Basic(t *testing.T) {
require.EqualValues(t, buf.Bytes()[:pn], p)
tmpA := make([]byte, cum/16)
tmpB := make([]byte, cum/16)
- rand.Read(tmpA)
- rand.Read(tmpB)
+ _, err = crand.Read(tmpA)
+ require.NoError(t, err)
+ _, err = crand.Read(tmpB)
+ require.NoError(t, err)
bs, err = llb.PeekWithBytes(cum/4, tmpA, tmpB)
require.NoError(t, err)
p = p[:0]
@@ -69,8 +71,8 @@ func TestLinkedListBuffer_ReadFrom(t *testing.T) {
var llb Buffer
const dataLen = 4 * 1024
data := make([]byte, dataLen)
- rand.Seed(time.Now().Unix())
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
r := bytes.NewReader(data)
n, err := llb.ReadFrom(r)
require.NoError(t, err)
@@ -80,9 +82,11 @@ func TestLinkedListBuffer_ReadFrom(t *testing.T) {
llb.Reset()
const headLen = 256
head := make([]byte, headLen)
- rand.Read(head)
+ _, err = crand.Read(head)
+ require.NoError(t, err)
llb.PushBack(head)
- rand.Read(data)
+ _, err = crand.Read(data)
+ require.NoError(t, err)
r.Reset(data)
n, err = llb.ReadFrom(r)
require.NoError(t, err)
@@ -104,12 +108,12 @@ func TestLinkedListBuffer_WriteTo(t *testing.T) {
cum int
buf bytes.Buffer
)
- rand.Seed(time.Now().Unix())
for i := 0; i < maxBlocks; i++ {
n := rand.Intn(1024) + 128
cum += n
data := make([]byte, n)
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
llb.PushBack(data)
buf.Write(data)
}
@@ -130,7 +134,8 @@ func TestLinkedListBuffer_WriteTo(t *testing.T) {
n := rand.Intn(1024) + 128
cum += n
data := make([]byte, n)
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
llb.PushBack(data)
buf.Write(data)
}
diff --git a/pkg/buffer/ring/ring_buffer_test.go b/pkg/buffer/ring/ring_buffer_test.go
index e323342b1..2efb15509 100644
--- a/pkg/buffer/ring/ring_buffer_test.go
+++ b/pkg/buffer/ring/ring_buffer_test.go
@@ -2,10 +2,9 @@ package ring
import (
"bytes"
- "math/rand"
+ crand "crypto/rand"
"strings"
"testing"
- "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -125,7 +124,7 @@ func TestRingBufferGrow(t *testing.T) {
assert.Empty(t, head, "head should be empty")
assert.Empty(t, tail, "tail should be empty")
data := make([]byte, DefaultBufferSize+1)
- n, err := rand.Read(data)
+ n, err := crand.Read(data)
assert.NoError(t, err, "failed to generate random data")
assert.EqualValuesf(t, DefaultBufferSize+1, n, "expect random data length is %d but got %d", DefaultBufferSize+1, n)
n, err = rb.Write(data)
@@ -139,7 +138,7 @@ func TestRingBufferGrow(t *testing.T) {
rb = New(DefaultBufferSize)
newData := make([]byte, 3*512)
- n, err = rand.Read(newData)
+ n, err = crand.Read(newData)
assert.NoError(t, err, "failed to generate random data")
assert.EqualValuesf(t, 3*512, n, "expect random data length is %d but got %d", 3*512, n)
n, err = rb.Write(newData)
@@ -153,7 +152,7 @@ func TestRingBufferGrow(t *testing.T) {
rb.Reset()
data = make([]byte, bufferGrowThreshold)
- n, err = rand.Read(data)
+ n, err = crand.Read(data)
assert.NoError(t, err, "failed to generate random data")
assert.EqualValuesf(t, bufferGrowThreshold, n, "expect random data length is %d but got %d", bufferGrowThreshold, n)
n, err = rb.Write(data)
@@ -165,7 +164,7 @@ func TestRingBufferGrow(t *testing.T) {
assert.EqualValues(t, 0, rb.Available())
assert.EqualValues(t, data, rb.Bytes())
newData = make([]byte, bufferGrowThreshold/2+1)
- n, err = rand.Read(newData)
+ n, err = crand.Read(newData)
assert.NoError(t, err, "failed to generate random data")
assert.EqualValuesf(t, bufferGrowThreshold/2+1, n, "expect random data length is %d but got %d", bufferGrowThreshold, n)
n, err = rb.Write(newData)
@@ -307,8 +306,8 @@ func TestRingBuffer_ReadFrom(t *testing.T) {
rb := New(0)
const dataLen = 4 * 1024
data := make([]byte, dataLen)
- rand.Seed(time.Now().Unix())
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
r := bytes.NewReader(data)
n, err := rb.ReadFrom(r)
require.NoError(t, err)
@@ -329,8 +328,10 @@ func TestRingBuffer_ReadFrom(t *testing.T) {
rb = New(0)
const prefixLen = 2 * 1024
prefix := make([]byte, prefixLen)
- rand.Read(prefix)
- rand.Read(data)
+ _, err = crand.Read(prefix)
+ require.NoError(t, err)
+ _, err = crand.Read(data)
+ require.NoError(t, err)
r.Reset(data)
m, err = rb.Write(prefix)
require.NoError(t, err)
@@ -354,8 +355,10 @@ func TestRingBuffer_ReadFrom(t *testing.T) {
const initLen = 5 * 1024
rb = New(initLen)
- rand.Read(prefix)
- rand.Read(data)
+ _, err = crand.Read(prefix)
+ require.NoError(t, err)
+ _, err = crand.Read(data)
+ require.NoError(t, err)
r.Reset(data)
m, err = rb.Write(prefix)
require.NoError(t, err)
@@ -380,8 +383,8 @@ func TestRingBuffer_WriteTo(t *testing.T) {
rb := New(5 * 1024)
const dataLen = 4 * 1024
data := make([]byte, dataLen)
- rand.Seed(time.Now().Unix())
- rand.Read(data)
+ _, err := crand.Read(data)
+ require.NoError(t, err)
n, err := rb.Write(data)
require.NoError(t, err)
require.EqualValuesf(t, dataLen, n, "ringbuffer should write %d bytes, but got %d", dataLen, n)
@@ -394,7 +397,8 @@ func TestRingBuffer_WriteTo(t *testing.T) {
require.EqualValues(t, data, buf.Bytes())
buf.Reset()
- rand.Read(data)
+ _, err = crand.Read(data)
+ require.NoError(t, err)
rb = New(dataLen)
n, err = rb.Write(data)
require.NoError(t, err)
@@ -408,7 +412,8 @@ func TestRingBuffer_WriteTo(t *testing.T) {
buf.Reset()
rb.Reset()
- rand.Read(data)
+ _, err = crand.Read(data)
+ require.NoError(t, err)
n, err = rb.Write(data)
require.NoError(t, err)
require.EqualValuesf(t, dataLen, n, "ringbuffer should write %d bytes, but got %d", dataLen, n)
@@ -419,7 +424,8 @@ func TestRingBuffer_WriteTo(t *testing.T) {
require.EqualValues(t, data[:partLen], head)
_, _ = rb.Discard(partLen)
partData := make([]byte, partLen/2)
- rand.Read(partData)
+ _, err = crand.Read(partData)
+ require.NoError(t, err)
n, err = rb.Write(partData)
require.NoError(t, err)
require.EqualValuesf(t, partLen/2, n, "ringbuffer should write %d bytes, but got %d", dataLen, n)
From 18f54e311551af7e2f01603f3a0758111e826e70 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Sat, 21 Sep 2024 21:52:03 +0800
Subject: [PATCH 22/29] lint: disable lint checks for string<->bytes conversion
temporarily
---
internal/bs/bs.go | 8 ++++++--
pkg/pool/byteslice/byteslice.go | 2 +-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/internal/bs/bs.go b/internal/bs/bs.go
index d23cb34a4..16b3861f7 100644
--- a/internal/bs/bs.go
+++ b/internal/bs/bs.go
@@ -19,6 +19,10 @@ import (
"unsafe"
)
+// TODO(panjf2000): rework the implementation of BytesToString and StringToBytes by
+// using unsafe.String/unsafe.StringData and unsafe.Slice/unsafe.SliceData when we
+// bump up the minimum required Go version to 1.20.
+
// BytesToString converts byte slice to a string without memory allocation.
//
// Note it may break if the implementation of string or slice header changes in the future go versions.
@@ -32,9 +36,9 @@ func BytesToString(b []byte) string {
// Note it may break if the implementation of string or slice header changes in the future go versions.
func StringToBytes(s string) (b []byte) {
/* #nosec G103 */
- sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
+ sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) //nolint:staticcheck
/* #nosec G103 */
- bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+ bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) //nolint:staticcheck
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
return b
diff --git a/pkg/pool/byteslice/byteslice.go b/pkg/pool/byteslice/byteslice.go
index 3fd9ac89b..efd063375 100644
--- a/pkg/pool/byteslice/byteslice.go
+++ b/pkg/pool/byteslice/byteslice.go
@@ -53,7 +53,7 @@ func (p *Pool) Get(size int) (buf []byte) {
if ptr == nil {
return make([]byte, 1<
Date: Sun, 22 Sep 2024 00:00:24 +0800
Subject: [PATCH 23/29] opt: bump up minimum required Go version to 1.20 (#638)
Employ the standard approach of string<->bytes conversion
---
.github/workflows/cross-compile-bsd.yml | 2 +-
.github/workflows/test.yml | 4 ++--
.github/workflows/test_gc_opt.yml | 4 ++--
.github/workflows/test_poll_opt.yml | 6 ++---
.github/workflows/test_poll_opt_gc_opt.yml | 6 ++---
README.md | 2 +-
README_ZH.md | 2 +-
go.mod | 6 ++---
go.sum | 10 ++++-----
internal/bs/bs.go | 26 +++++-----------------
pkg/pool/byteslice/byteslice.go | 20 ++++++-----------
11 files changed, 32 insertions(+), 56 deletions(-)
diff --git a/.github/workflows/cross-compile-bsd.yml b/.github/workflows/cross-compile-bsd.yml
index 0e782a704..e91a22672 100644
--- a/.github/workflows/cross-compile-bsd.yml
+++ b/.github/workflows/cross-compile-bsd.yml
@@ -31,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- go: ['1.17', '1.22']
+ go: ['1.20', '1.23']
os:
- ubuntu-latest
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 752c62135..3e964a415 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -43,7 +43,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
- go-version: '^1.17'
+ go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
@@ -56,7 +56,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- go: ['1.17', '1.22']
+ go: ['1.20', '1.23']
os:
- ubuntu-latest
- macos-latest
diff --git a/.github/workflows/test_gc_opt.yml b/.github/workflows/test_gc_opt.yml
index a494a1589..df99ae8e2 100644
--- a/.github/workflows/test_gc_opt.yml
+++ b/.github/workflows/test_gc_opt.yml
@@ -43,7 +43,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
- go-version: '^1.17'
+ go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
@@ -56,7 +56,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- go: ['1.17', '1.22']
+ go: ['1.20', '1.23']
os:
- ubuntu-latest
- macos-latest
diff --git a/.github/workflows/test_poll_opt.yml b/.github/workflows/test_poll_opt.yml
index 22991b967..5d5ae19fc 100644
--- a/.github/workflows/test_poll_opt.yml
+++ b/.github/workflows/test_poll_opt.yml
@@ -42,7 +42,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
- go-version: '^1.17'
+ go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
@@ -55,7 +55,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- go: ['1.17', '1.22']
+ go: ['1.20', '1.23']
os: [ubuntu-latest, macos-latest]
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
runs-on: ${{ matrix.os }}
@@ -68,7 +68,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
- go-version: '^1.17'
+ go-version: '^1.20'
- name: Print Go environment
id: go-env
diff --git a/.github/workflows/test_poll_opt_gc_opt.yml b/.github/workflows/test_poll_opt_gc_opt.yml
index 1d658f449..a5aef4062 100644
--- a/.github/workflows/test_poll_opt_gc_opt.yml
+++ b/.github/workflows/test_poll_opt_gc_opt.yml
@@ -42,7 +42,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
- go-version: '^1.17'
+ go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
@@ -55,7 +55,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- go: ['1.17', '1.22']
+ go: ['1.20', '1.23']
os: [ubuntu-latest, macos-latest]
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
runs-on: ${{ matrix.os }}
@@ -68,7 +68,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
- go-version: '^1.17'
+ go-version: '^1.20'
- name: Print Go environment
id: go-env
diff --git a/README.md b/README.md
index bdba1a6f3..8c85967b3 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-
+
diff --git a/README_ZH.md b/README_ZH.md
index fe5a4f28f..d166d1b76 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -4,7 +4,7 @@
-
+
diff --git a/go.mod b/go.mod
index 178d229c9..0ab63f761 100644
--- a/go.mod
+++ b/go.mod
@@ -5,8 +5,8 @@ require (
github.com/stretchr/testify v1.9.0
github.com/valyala/bytebufferpool v1.0.0
go.uber.org/zap v1.21.0 // don't upgrade this one
- golang.org/x/sync v0.7.0
- golang.org/x/sys v0.21.0
+ golang.org/x/sync v0.8.0
+ golang.org/x/sys v0.25.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
@@ -18,4 +18,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)
-go 1.17
+go 1.20
diff --git a/go.sum b/go.sum
index 7ba46041c..6c36aa9be 100644
--- a/go.sum
+++ b/go.sum
@@ -17,13 +17,11 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -48,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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
+golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
diff --git a/internal/bs/bs.go b/internal/bs/bs.go
index 16b3861f7..7afb5136e 100644
--- a/internal/bs/bs.go
+++ b/internal/bs/bs.go
@@ -15,31 +15,15 @@
package bs
import (
- "reflect"
"unsafe"
)
-// TODO(panjf2000): rework the implementation of BytesToString and StringToBytes by
-// using unsafe.String/unsafe.StringData and unsafe.Slice/unsafe.SliceData when we
-// bump up the minimum required Go version to 1.20.
-
-// BytesToString converts byte slice to a string without memory allocation.
-//
-// Note it may break if the implementation of string or slice header changes in the future go versions.
+// BytesToString converts byte slice to a string without any memory allocation.
func BytesToString(b []byte) string {
- /* #nosec G103 */
- return *(*string)(unsafe.Pointer(&b))
+ return unsafe.String(unsafe.SliceData(b), len(b))
}
-// StringToBytes converts string to a byte slice without memory allocation.
-//
-// Note it may break if the implementation of string or slice header changes in the future go versions.
-func StringToBytes(s string) (b []byte) {
- /* #nosec G103 */
- sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) //nolint:staticcheck
- /* #nosec G103 */
- bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) //nolint:staticcheck
-
- bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
- return b
+// StringToBytes converts string to a byte slice without any memory allocation.
+func StringToBytes(s string) []byte {
+ return unsafe.Slice(unsafe.StringData(s), len(s))
}
diff --git a/pkg/pool/byteslice/byteslice.go b/pkg/pool/byteslice/byteslice.go
index efd063375..53397dac6 100644
--- a/pkg/pool/byteslice/byteslice.go
+++ b/pkg/pool/byteslice/byteslice.go
@@ -17,8 +17,6 @@ package byteslice
import (
"math"
"math/bits"
- "reflect"
- "runtime"
"sync"
"unsafe"
)
@@ -41,7 +39,7 @@ func Put(buf []byte) {
}
// Get retrieves a byte slice of the length requested by the caller from pool or allocates a new one.
-func (p *Pool) Get(size int) (buf []byte) {
+func (p *Pool) Get(size int) []byte {
if size <= 0 {
return nil
}
@@ -49,16 +47,11 @@ func (p *Pool) Get(size int) (buf []byte) {
return make([]byte, size)
}
idx := index(uint32(size))
- ptr, _ := p.pools[idx].Get().(unsafe.Pointer)
+ ptr, _ := p.pools[idx].Get().(*byte)
if ptr == nil {
- return make([]byte, 1<
Date: Tue, 24 Sep 2024 17:12:58 +0800
Subject: [PATCH 24/29] chore: update the JetBrains logo
---
README.md | 4 ++--
README_ZH.md | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 8c85967b3..fd9b0f4d9 100644
--- a/README.md
+++ b/README.md
@@ -246,9 +246,9 @@ Become a bronze sponsor with a monthly donation of $10 and get your logo on our
# 🔑 JetBrains OS licenses
-`gnet` had been being developed with `GoLand` IDE under the **free JetBrains Open Source license(s)** granted by JetBrains s.r.o., hence I would like to express my thanks here.
+`gnet` has been being developed with `GoLand` IDE under the ***free JetBrains Open Source license(s)*** granted by JetBrains s.r.o., hence I would like to express my thanks here.
-
+
# 🔋 Sponsorship
diff --git a/README_ZH.md b/README_ZH.md
index d166d1b76..04fae4bee 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -246,9 +246,9 @@ Test duration : 15s
# 🔑 JetBrains 开源证书支持
-`gnet` 项目一直以来都是在 JetBrains 公司旗下的 GoLand 集成开发环境中进行开发,基于 **free JetBrains Open Source license(s)** 正版免费授权,在此表达我的谢意。
+`gnet` 项目一直以来都是在 JetBrains 公司旗下的 GoLand 集成开发环境中进行开发,基于 ***free JetBrains Open Source license(s)*** 正版免费授权,在此表达我的谢意。
-
+
# 🔋 赞助商
From e52039928c53495d6b5d3e30ae54d3d6331c0c5c Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Thu, 26 Sep 2024 14:00:16 +0800
Subject: [PATCH 25/29] chore: use errors.Is and errors.Join for customized
errors (#640)
* chore: use errors.Join of go1.20 on Windows
* chore: use errors.Is for customized errors
---
acceptor_windows.go | 10 ++-------
engine_unix.go | 9 ++++----
internal/netpoll/poller_epoll_default.go | 24 ++++++++--------------
internal/netpoll/poller_epoll_ultimate.go | 24 ++++++++--------------
internal/netpoll/poller_kqueue_default.go | 24 ++++++++--------------
internal/netpoll/poller_kqueue_ultimate.go | 24 ++++++++--------------
6 files changed, 39 insertions(+), 76 deletions(-)
diff --git a/acceptor_windows.go b/acceptor_windows.go
index ce6005c0e..d9d6339cd 100644
--- a/acceptor_windows.go
+++ b/acceptor_windows.go
@@ -39,11 +39,7 @@ func (eng *engine) listenStream(ln net.Listener) (err error) {
if atomic.LoadInt32(&eng.beingShutdown) == 0 {
eng.opts.Logger.Errorf("Accept() fails due to error: %v", err)
} else if errors.Is(err, net.ErrClosed) {
- err = errorx.ErrEngineShutdown
- // TODO: errors.Join() is not supported until Go 1.20,
- // we will uncomment this line after we bump up the
- // minimal supported go version to 1.20.
- // err = errors.Join(err, errorx.ErrEngineShutdown)
+ err = errors.Join(err, errorx.ErrEngineShutdown)
}
return
}
@@ -81,9 +77,7 @@ func (eng *engine) ListenUDP(pc net.PacketConn) (err error) {
if atomic.LoadInt32(&eng.beingShutdown) == 0 {
eng.opts.Logger.Errorf("failed to receive data from UDP fd due to error:%v", err)
} else if errors.Is(err, net.ErrClosed) {
- err = errorx.ErrEngineShutdown
- // TODO: ditto.
- // err = errors.Join(err, errorx.ErrEngineShutdown)
+ err = errors.Join(err, errorx.ErrEngineShutdown)
}
return
}
diff --git a/engine_unix.go b/engine_unix.go
index 83a1ae864..3bc488f95 100644
--- a/engine_unix.go
+++ b/engine_unix.go
@@ -19,6 +19,7 @@ package gnet
import (
"context"
+ "errors"
"runtime"
"strings"
"sync"
@@ -29,7 +30,7 @@ import (
"github.com/panjf2000/gnet/v2/internal/gfd"
"github.com/panjf2000/gnet/v2/internal/netpoll"
"github.com/panjf2000/gnet/v2/internal/queue"
- "github.com/panjf2000/gnet/v2/pkg/errors"
+ errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
)
@@ -59,7 +60,7 @@ func (eng *engine) isInShutdown() bool {
// shutdown signals the engine to shut down.
func (eng *engine) shutdown(err error) {
- if err != nil && err != errors.ErrEngineShutdown {
+ if err != nil && !errors.Is(err, errorx.ErrEngineShutdown) {
eng.opts.Logger.Errorf("engine is being shutdown with error: %v", err)
}
@@ -211,14 +212,14 @@ func (eng *engine) stop(s Engine) {
// Notify all event-loops to exit.
eng.eventLoops.iterate(func(i int, el *eventloop) bool {
- err := el.poller.Trigger(queue.HighPriority, func(_ interface{}) error { return errors.ErrEngineShutdown }, nil)
+ err := el.poller.Trigger(queue.HighPriority, func(_ interface{}) error { return errorx.ErrEngineShutdown }, nil)
if err != nil {
eng.opts.Logger.Errorf("failed to enqueue shutdown signal of high-priority for event-loop(%d): %v", i, err)
}
return true
})
if eng.ingress != nil {
- err := eng.ingress.poller.Trigger(queue.HighPriority, func(_ interface{}) error { return errors.ErrEngineShutdown }, nil)
+ err := eng.ingress.poller.Trigger(queue.HighPriority, func(_ interface{}) error { return errorx.ErrEngineShutdown }, nil)
if err != nil {
eng.opts.Logger.Errorf("failed to enqueue shutdown signal of high-priority for main event-loop: %v", err)
}
diff --git a/internal/netpoll/poller_epoll_default.go b/internal/netpoll/poller_epoll_default.go
index d5e4d4e48..ec47d0b46 100644
--- a/internal/netpoll/poller_epoll_default.go
+++ b/internal/netpoll/poller_epoll_default.go
@@ -19,6 +19,7 @@
package netpoll
import (
+ "errors"
"os"
"runtime"
"sync/atomic"
@@ -27,7 +28,7 @@ import (
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/internal/queue"
- "github.com/panjf2000/gnet/v2/pkg/errors"
+ errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
)
@@ -133,12 +134,9 @@ func (p *Poller) Polling(callback PollEventHandler) error {
if fd := int(ev.Fd); fd == p.efd { // poller is awakened to run tasks in queues.
doChores = true
} else {
- switch err = callback(fd, ev.Events, 0); err {
- case nil:
- case errors.ErrAcceptSocket, errors.ErrEngineShutdown:
+ err = callback(fd, ev.Events, 0)
+ if errors.Is(err, errorx.ErrAcceptSocket) || errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in event-loop: %v", err)
}
}
}
@@ -147,12 +145,9 @@ func (p *Poller) Polling(callback PollEventHandler) error {
doChores = false
task := p.urgentAsyncTaskQueue.Dequeue()
for ; task != nil; task = p.urgentAsyncTaskQueue.Dequeue() {
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
@@ -160,12 +155,9 @@ func (p *Poller) Polling(callback PollEventHandler) error {
if task = p.asyncTaskQueue.Dequeue(); task == nil {
break
}
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
diff --git a/internal/netpoll/poller_epoll_ultimate.go b/internal/netpoll/poller_epoll_ultimate.go
index fec70097e..730e0485b 100644
--- a/internal/netpoll/poller_epoll_ultimate.go
+++ b/internal/netpoll/poller_epoll_ultimate.go
@@ -18,6 +18,7 @@
package netpoll
import (
+ "errors"
"os"
"runtime"
"sync/atomic"
@@ -26,7 +27,7 @@ import (
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/internal/queue"
- "github.com/panjf2000/gnet/v2/pkg/errors"
+ errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
)
@@ -135,12 +136,9 @@ func (p *Poller) Polling() error {
if pollAttachment.FD == p.epa.FD { // poller is awakened to run tasks in queues.
doChores = true
} else {
- switch err = pollAttachment.Callback(pollAttachment.FD, ev.events, 0); err {
- case nil:
- case errors.ErrAcceptSocket, errors.ErrEngineShutdown:
+ err = pollAttachment.Callback(pollAttachment.FD, ev.events, 0)
+ if errors.Is(err, errorx.ErrAcceptSocket) || errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in event-loop: %v", err)
}
}
}
@@ -149,12 +147,9 @@ func (p *Poller) Polling() error {
doChores = false
task := p.urgentAsyncTaskQueue.Dequeue()
for ; task != nil; task = p.urgentAsyncTaskQueue.Dequeue() {
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
@@ -162,12 +157,9 @@ func (p *Poller) Polling() error {
if task = p.asyncTaskQueue.Dequeue(); task == nil {
break
}
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
diff --git a/internal/netpoll/poller_kqueue_default.go b/internal/netpoll/poller_kqueue_default.go
index c44be9027..4fbcd8e60 100644
--- a/internal/netpoll/poller_kqueue_default.go
+++ b/internal/netpoll/poller_kqueue_default.go
@@ -19,6 +19,7 @@
package netpoll
import (
+ "errors"
"os"
"runtime"
"sync/atomic"
@@ -26,7 +27,7 @@ import (
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/internal/queue"
- "github.com/panjf2000/gnet/v2/pkg/errors"
+ errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
)
@@ -118,12 +119,9 @@ func (p *Poller) Polling(callback PollEventHandler) error {
doChores = true
p.drainWakeupEvent()
} else {
- switch err = callback(fd, ev.Filter, ev.Flags); err {
- case nil:
- case errors.ErrAcceptSocket, errors.ErrEngineShutdown:
+ err = callback(fd, ev.Filter, ev.Flags)
+ if errors.Is(err, errorx.ErrAcceptSocket) || errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in event-loop: %v", err)
}
}
}
@@ -132,12 +130,9 @@ func (p *Poller) Polling(callback PollEventHandler) error {
doChores = false
task := p.urgentAsyncTaskQueue.Dequeue()
for ; task != nil; task = p.urgentAsyncTaskQueue.Dequeue() {
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
@@ -145,12 +140,9 @@ func (p *Poller) Polling(callback PollEventHandler) error {
if task = p.asyncTaskQueue.Dequeue(); task == nil {
break
}
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
diff --git a/internal/netpoll/poller_kqueue_ultimate.go b/internal/netpoll/poller_kqueue_ultimate.go
index fc50c6521..e12caf0c1 100644
--- a/internal/netpoll/poller_kqueue_ultimate.go
+++ b/internal/netpoll/poller_kqueue_ultimate.go
@@ -19,6 +19,7 @@
package netpoll
import (
+ "errors"
"os"
"runtime"
"sync/atomic"
@@ -27,7 +28,7 @@ import (
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/internal/queue"
- "github.com/panjf2000/gnet/v2/pkg/errors"
+ errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
)
@@ -120,12 +121,9 @@ func (p *Poller) Polling() error {
p.drainWakeupEvent()
} else {
pollAttachment := restorePollAttachment(unsafe.Pointer(&ev.Udata))
- switch err = pollAttachment.Callback(int(ev.Ident), ev.Filter, ev.Flags); err {
- case nil:
- case errors.ErrAcceptSocket, errors.ErrEngineShutdown:
+ err = pollAttachment.Callback(int(ev.Ident), ev.Filter, ev.Flags)
+ if errors.Is(err, errorx.ErrAcceptSocket) || errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in event-loop: %v", err)
}
}
}
@@ -134,12 +132,9 @@ func (p *Poller) Polling() error {
doChores = false
task := p.urgentAsyncTaskQueue.Dequeue()
for ; task != nil; task = p.urgentAsyncTaskQueue.Dequeue() {
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
@@ -147,12 +142,9 @@ func (p *Poller) Polling() error {
if task = p.asyncTaskQueue.Dequeue(); task == nil {
break
}
- switch err = task.Run(task.Arg); err {
- case nil:
- case errors.ErrEngineShutdown:
+ err = task.Run(task.Arg)
+ if errors.Is(err, errorx.ErrEngineShutdown) {
return err
- default:
- logging.Warnf("error occurs in user-defined function, %v", err)
}
queue.PutTask(task)
}
From 2e6430c4ece7b146a6542fac703cf1acea2c02e8 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Thu, 26 Sep 2024 18:50:23 +0800
Subject: [PATCH 26/29] actions: add actions/stale
---
.github/workflows/gh-translator.yml | 2 +-
.github/workflows/stale-bot.yml | 46 +++++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)
create mode 100644 .github/workflows/stale-bot.yml
diff --git a/.github/workflows/gh-translator.yml b/.github/workflows/gh-translator.yml
index c3a0f6d92..60bfe048a 100644
--- a/.github/workflows/gh-translator.yml
+++ b/.github/workflows/gh-translator.yml
@@ -20,4 +20,4 @@ jobs:
with:
BOT_GITHUB_TOKEN: ${{ secrets.GH_TRANSLATOR_TOKEN }}
IS_MODIFY_TITLE: true
- CUSTOM_BOT_NOTE: 🤖 Non-English text detected, translating...
+ CUSTOM_BOT_NOTE: 🤖 Non-English text detected, translating ...
diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml
new file mode 100644
index 000000000..d9222d5d7
--- /dev/null
+++ b/.github/workflows/stale-bot.yml
@@ -0,0 +1,46 @@
+name: Monitor inactive issues and PRs
+on:
+ schedule:
+ - cron: '0 0 * * *'
+ workflow_dispatch:
+
+jobs:
+ stale-issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@v9
+ with:
+ days-before-issue-stale: 30
+ days-before-issue-close: 7
+ stale-issue-label: 'stale'
+ stale-issue-message: |
+ This issue is marked as stale because it has been open for 30 days with no activity.
+
+ You should take one of the following actions:
+ - Manually close this issue if it is no longer relevant
+ - Comment if you have more information to share
+
+ This issue will be automatically closed in 7 days if no further activity occurs.
+ close-issue-message: |
+ This issue was closed because it has been inactive for 7 days since being marked as stale.
+
+ If you believe this is a false alarm, please leave a comment for it or open a new issue, you can also reopen this issue directly if you have permission.
+ days-before-pr-stale: 21
+ days-before-pr-close: 7
+ stale-pr-label: 'stale'
+ stale-pr-message: |
+ This PR is marked as stale because it has been open for 21 days with no activity.
+
+ You should take one of the following actions:
+ - Manually close this PR if it is no longer relevant
+ - Push new commits or comment if you have more information to share
+
+ This PR will be automatically closed in 7 days if no further activity occurs.
+ close-pr-message: |
+ This PR was closed because it has been inactive for 7 days since being marked as stale.
+
+ If you believe this is a false alarm, feel free to reopen this PR or create a new one.
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
From d745cd79dce16da93b21a583ad326e801b7be855 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Thu, 26 Sep 2024 19:02:45 +0800
Subject: [PATCH 27/29] actions: bump up the operations-per-run for
actions/stale
---
.github/workflows/stale-bot.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml
index d9222d5d7..8a67f37de 100644
--- a/.github/workflows/stale-bot.yml
+++ b/.github/workflows/stale-bot.yml
@@ -13,6 +13,7 @@ jobs:
steps:
- uses: actions/stale@v9
with:
+ operations-per-run: 100
days-before-issue-stale: 30
days-before-issue-close: 7
stale-issue-label: 'stale'
From cccef2c355050d8dc07f318c2326f360d86e31a4 Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Thu, 26 Sep 2024 19:51:52 +0800
Subject: [PATCH 28/29] actions: add "actions: write" for actions/stale
temporarily
Fix the cache issue.
See actions/stale#1133 for details.
---
.github/workflows/stale-bot.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml
index 8a67f37de..762a5b316 100644
--- a/.github/workflows/stale-bot.yml
+++ b/.github/workflows/stale-bot.yml
@@ -8,6 +8,7 @@ jobs:
stale-issues:
runs-on: ubuntu-latest
permissions:
+ actions: write
issues: write
pull-requests: write
steps:
From ab70394ced980e640a7ed9b8a9e8be79d5ed591e Mon Sep 17 00:00:00 2001
From: Andy Pan
Date: Wed, 2 Oct 2024 18:51:25 +0800
Subject: [PATCH 29/29] chore: fix lint warnings of missing case in the
`switch` statement
---
eventloop_unix.go | 2 +-
eventloop_windows.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/eventloop_unix.go b/eventloop_unix.go
index 97475302c..0897485d6 100644
--- a/eventloop_unix.go
+++ b/eventloop_unix.go
@@ -293,7 +293,7 @@ func (el *eventloop) ticker(ctx context.Context) {
for {
delay, action = el.eventHandler.OnTick()
switch action {
- case None:
+ case None, Close:
case Shutdown:
// It seems reasonable to mark this as low-priority, waiting for some tasks like asynchronous writes
// to finish up before shutting down the service.
diff --git a/eventloop_windows.go b/eventloop_windows.go
index 565f3ef30..906d8924f 100644
--- a/eventloop_windows.go
+++ b/eventloop_windows.go
@@ -155,7 +155,7 @@ func (el *eventloop) ticker(ctx context.Context) {
for {
delay, action = el.eventHandler.OnTick()
switch action {
- case None:
+ case None, Close:
case Shutdown:
if !shutdown {
shutdown = true