Closed yyjdelete closed 3 years ago
稍等两天,我再看,到时再交流
@yyjdelete 晕菜,真的是这样,难道只能这样吗?
private static void XXX(int chunkSize, int usage)
{
int freeThreshold;
if (usage == 100)
{
freeThreshold = 0;
}
else
{
var tmp = chunkSize * (100.0d - usage + 0.99999999d) / 100L;
if (tmp < int.MinValue)
{
freeThreshold = int.MinValue;
}
else if (tmp > int.MaxValue)
{
freeThreshold = int.MaxValue;
}
else
{
freeThreshold = (int)(uint)tmp;
}
}
//int freeThreshold = (usage == 100) ? 0 : (int)(uint)(chunkSize * (100.0d - usage + 0.99999999d) / 100L);
Console.WriteLine(freeThreshold);
}
感觉可能是,不过还好,好像就这一个地方会出这个问题.
呃, 搞错了, double无所谓,float才是由于精度问题必须取等.
另外下面那行的(int)(uint)
的两步转换在先检查了溢出之后不是必须的
private static int CalcWithOverflow(int chunkSize, int usage)
{
int freeThreshold;
if (usage == 100)
{
freeThreshold = 0;
}
else
{
var tmp = chunkSize * (100.0d - usage + 0.99999999d) / 100L;
if (tmp <= int.MinValue)
{
freeThreshold = int.MinValue;
}
else if (tmp >= int.MaxValue)
{
freeThreshold = int.MaxValue;
}
else
{
freeThreshold = (int)tmp;
}
}
return freeThreshold;
}
https://github.com/cuteant/SpanNetty/blob/34a24e358bf03178075cd90c7d7a319660232481/src/DotNetty.Buffers/PoolChunkList.cs#L77-L78
进一步调试发现这里的转换在java和C#版本中无法得到一致的结果,
对于q100和qInit的int.MaxValue和int.MinValue 在Java中分别得到
-2147483648
,2147483647
(maxOrder=8或11) 在C#版本(仅在netcoreapp3.1/net5.0 x64下测试, 不同版本返回值可能存在差异)中分别得到1032931247
,-1028674028
(maxOrder=8),-326484623
,360542371
(maxOrder=11), 如果不先转换为uint则均是-2147483648
,-2147483648
对于PoolArena._qInit, minUsage始终为int.MinValue,_prevList为其自身. 因此在调用qInit.Free时会由于_freeMaxThreshold为负, 条件恒true(而非Java的恒false), 进入一条死循环的逻辑(_prevList.Move(), 但_prevList==this)从而出现堆栈溢出
对于q100, 是否会出现问题未测试发现. 但也可能会造成一些其他逻辑上的问题.
根据 https://github.com/dotnet/runtime/issues/461#issuecomment-560955673 , 如果当从浮点型转换为整型时出现溢出, 转换的结果是未定义的任何值(并且好像已知在x64和arm上不一致, 但找不到原文了) 这里可能需要将double的结果手动做溢出检查并转换为
int.MaxValue
,int.MinValue