-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Bug]: conn.Next not safe #648
Comments
|
|
sync.Pool中被回收的字节数组,可能被其他协程复用 |
The recycled byte array in sync.Pool may be reused by other coroutines |
我重新看了一下,确实有这种可能,不过只有当 |
I re-read it, and it is indeed possible, but it will only happen when |
这个地方应该是把 |
最近刚遇到这个问题,最终定位到了这里,复现概率确实挺低的。 |
I just encountered this problem recently and finally located it here. The probability of recurrence is indeed very low. |
我觉得这样修改应该就能修复这个问题: diff --git a/connection_unix.go b/connection_unix.go
index cfce3a92..82c1d89b 100644
--- a/connection_unix.go
+++ b/connection_unix.go
@@ -326,6 +326,11 @@ 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 inBufferLen == n && len(tail) == 0 {
+ c.loop.cache.Reset()
+ c.loop.cache.Write(head)
+ return c.loop.cache.Bytes(), err
+ }
return head[:n], err
}
c.loop.cache.Reset() |
I think this modification should fix the problem: diff --git a/connection_unix.go b/connection_unix.go
indexcfce3a92..82c1d89b 100644
--- a/connection_unix.go
+++ b/connection_unix.go
@@ -326,6 +326,11 @@ 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 inBufferLen == n && len(tail) == 0 {
+ c.loop.cache.Reset()
+ c.loop.cache.Write(head)
+ return c.loop.cache.Bytes(), err
+ }
return head[:n], err
}
c.loop.cache.Reset() |
更简单的方法: diff --git a/connection_unix.go b/connection_unix.go
index cfce3a92..3ccac4ab 100644
--- a/connection_unix.go
+++ b/connection_unix.go
@@ -325,11 +325,11 @@ 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 {
- return head[:n], err
- }
c.loop.cache.Reset()
c.loop.cache.Write(head)
+ if len(head) >= n {
+ return c.loop.cache.Bytes(), err
+ }
c.loop.cache.Write(tail)
if inBufferLen >= n {
return c.loop.cache.Bytes(), err 不过这种方式就变成了每次都会复制数据,会有一点性能损耗。 |
Easier way: diff --git a/connection_unix.go b/connection_unix.go
indexcfce3a92..3ccac4ab 100644
--- a/connection_unix.go
+++ b/connection_unix.go
@@ -325,11 +325,11 @@ 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 {
- return head[:n], err
- }
c.loop.cache.Reset()
c.loop.cache.Write(head)
+ if len(head) >= n {
+ return c.loop.cache.Bytes(), err
+ }
c.loop.cache.Write(tail)
if inBufferLen >= n {
return c.loop.cache.Bytes(), err However, this method will copy data every time, which will cause a little performance loss. |
我理解这个损耗应该是必须的,并且这里只是复制了一下数据,并没有新申请内存,感觉上还好~ |
I understand that this loss should be necessary, and I just copied the data here and did not apply for new memory. It feels fine~ |
你方便提一个 PR 吗? |
Is it convenient for you to submit a PR? |
方便 |
convenient |
好像不行,要求读 n 个字节,这样把整个head全返回出去了 diff --git a/connection_unix.go b/connection_unix.go
index cfce3a9..1bf11e9 100644
--- a/connection_unix.go
+++ b/connection_unix.go
@@ -325,10 +325,11 @@ func (c *conn) Next(n int) (buf []byte, err error) {
}
head, tail := c.inboundBuffer.Peek(n)
defer c.inboundBuffer.Discard(n) //nolint:errcheck
+ c.loop.cache.Reset()
if len(head) >= n {
- return head[:n], err
+ c.loop.cache.Write(head[:n])
+ return c.loop.cache.Bytes(), err
}
- c.loop.cache.Reset()
c.loop.cache.Write(head)
c.loop.cache.Write(tail)
if inBufferLen >= n { 这样可以吗? |
It doesn't seem to work. It requires reading n bytes, so the entire head is returned. diff --git a/connection_unix.go b/connection_unix.go
indexcfce3a9..1bf11e9 100644
--- a/connection_unix.go
+++ b/connection_unix.go
@@ -325,10 +325,11 @@ func (c *conn) Next(n int) (buf []byte, err error) {
}
head, tail := c.inboundBuffer.Peek(n)
defer c.inboundBuffer.Discard(n) //nolint:errcheck
+ c.loop.cache.Reset()
if len(head) >= n {
- return head[:n], err
+ c.loop.cache.Write(head[:n])
+ return c.loop.cache.Bytes(), err
}
- c.loop.cache.Reset()
c.loop.cache.Write(head)
c.loop.cache.Write(tail)
if inBufferLen >= n { Is this okay? |
确实,我前面忘了考虑这一点,就按你最后修改的那个来吧。 |
Indeed, I forgot to consider this before, so I will just go with the last one you modified. |
这么说起来后面的 |
Speaking of which, it seems that the |
我实际测试了一下,你这个代码还是没问题,刚才理解错了 head, tail := c.inboundBuffer.Peek(n) // Peek 只取了 n 个字节
defer c.inboundBuffer.Discard(n)
c.loop.cache.Reset()
if len(head) >= n {
c.loop.cache.Write(head) // head 的长度实际不会大于 n,所以这里 head 与 head[:n] 等价
return c.loop.cache.Bytes(), err
} 后面的 |
I actually tested it, and your code is still fine. I just misunderstood it. head, tail := c.inboundBuffer.Peek(n) // Peek only takes n bytes
defer c.inboundBuffer.Discard(n)
c.loop.cache.Reset()
if len(head) >= n {
c.loop.cache.Write(head) // The length of head will not actually be greater than n, so here head is equivalent to head[:n]
return c.loop.cache.Bytes(), err
} The following |
好吧,我刚才也被你绕进去了,这样看应该就没问题了。 |
Okay, you got me involved just now, so it should be fine. |
Actions I've taken before I'm here
What happened?
reopened: #647
@panjf2000 看到上一个被关了,不好意思,重提一个
我提前看过了文档,并且也熟读gnet源码,但是依然感觉有问题
文档里的表述是: the []byte buf returned by Next() is not allowed to be passed to a new goroutine
#647 提到的问题里,并没有将Next()返回的字节数组传递给新的goroutine,是Next本身内部实现造成的线程不安全
Next()方法返回的一刹那,就已经线程不安全了
Major version of gnet
v2
Specific version of gnet
v2
Operating system
Linux, macOS
OS version
Linux 3.10.0
Go version
go1.17.12
Relevant log output
Code snippets (optional)
No response
How to Reproduce
多个事件循环并发执行时概率复现
Does this issue reproduce with the latest release?
It can reproduce with the latest release
The text was updated successfully, but these errors were encountered: