KumoKyaku/kcp

TryRecv() = Object reference not set to an instance of an object

xAL95 opened this issue · 1 comments

xAL95 commented

When sending packets (repeadetly 100+) 3-5 times, then null reference exception occurs.

Important. This ONLY happens when using

new PoolSegManager.Kcp()

and loop without delay/sleep.

With sleep/delay 1 then there is no issue.

Example error code:

var kcp = new PoolSegManager.Kcp(12, this);
kcp.NoDelay(1, 10, 2, 1);
kcp.WndSize(64, 64);
kcp.SetMtu(512);

Task.Run(() =>
            {
                while (true)
                {
                    kcp.Update(DateTime.UtcNow);

                    try
                    {
                        int len;
                        do
                        {
                            var (buffer, avalidSzie) = kcp.TryRecv();
                            len = avalidSzie;
                            if (buffer != null)
                            {
                                buffer.Dispose();
                            }
                        } while (len > 0);
                    }
                    catch(Exception e)
                    {
                        Console.WriteLine(e);
                    }
                }
            });

Untitled

Bug已经复现。
为什么只有PoolSegManager才有Bug?
因为只有PoolSegManager的PoolSegManager.Seg是class,才会出现空引用。

怀疑的两个可能性:
1:TryRecv()的 rcv_queue.Count == 0 判断后,var seq = rcv_queue[0]; 执行前,存在其他线程删除了rcv_queue中的元素。
Q:通过代码分析,删除元素只有 UncheckRecv(Span buffer) 中 rcv_queue.RemoveRange(0, count); 一处。并且只在Recv环节调用,并不存在多线程问题。

2: https://source.dot.net/#System.Private.CoreLib/List.cs,203 rcv_queue.Add(seg);环节,是先增加size,后对下标赋值。
是不是有可能,rcv_queue.Count == 0 判断,var seq = rcv_queue[0]; 正好在List.Add中间执行?
直觉上这种可能性很小,但也不是没有。
一个佐证,但不能确定。在rcv_queue.Count == 0 判断,var seq = rcv_queue[0];两句代码之间插入了日志。Console.WriteLine($"rcv_queue TryRecv正在检测 rcv_queue[0]{rcv_queue[0]} ManagedThreadId{System.Threading.Thread.CurrentThread.ManagedThreadId}");
Bug消失了。

为什么sleep/delay 1 不出现Bug?
测试用例中如果delay,正好可以将Add和Recv函数错开调用。