IL 方法将引用 T 值固定为 void*(从<T>并行代码处理 Span)



在实际问题之前,一个小免责声明:

这是一个与此相关的/后续问题,但由于我在这里谈论一个更普遍的问题(如何固定ref T变量以便能够对其执行指针操作),并且我提出了一个可能的(可能是错误的)解决方案,我打开了一个单独的问题。

所以,问题是:给定一个ref T变量(假设它是数组的第一项),我如何固定它,以便 GC 在使用不安全指针处理底层数组时不会引起问题?

我不确定这在 C# 中是否可行(但我希望我错了),但是查看仅修复ref int变量的方法的 IL 代码,我试图提出一个适用于泛型类型的变体。这是我的想法:

假设我在"MyTestClass"类中有以下delegate

public unsafe delegate void UnsafeAction(void* p);

然后,在伊利诺伊州:

.method public hidebysig static void
Foo<valuetype .ctor (class [netstandard]System.ValueType) T>(
!!0/*T*/& r, 
class MyTestClass/UnsafeAction action
) cil managed
{
.maxstack 2
.locals init (
[0] void* p,
[1] !!0/*T*/& pinned V_1
)
// Load the ref T argument into the pinned variable (as if fixed were used)
IL_0000: nop          
IL_0001: ldarg.0      // r
IL_0002: stloc.1      // V_1
// Cast the T* pointer to void*
IL_0003: ldloc.1      // V_1
IL_0004: conv.u       
IL_0005: stloc.0      // p
// Invoke the action passing the p pointer
IL_0006: ldarg.1      // action
IL_0007: ldloc.0      // p
IL_0008: callvirt     instance void MyTestClass/UnsafeAction::Invoke(void*)
IL_000d: nop          
// Set the pinned variable V_1 to NULL
IL_000e: ldc.i4.0     
IL_000f: conv.u       
IL_0010: stloc.1      // V_1
IL_0011: ret
}

这个想法是能够像这样使用这种方法:

public static void Test<T>(this Span<T> span, Func<T> provider)
{
void Func(void* p)
{
// Do stuff with p, possibly in parallel
// eg. Unsafe.Write(p, provider());
}
Foo(ref span.DangerousGetPinnableReference(), Func);
}

这样的事情会起作用吗?如果是这样,将这种用 IL 编写的方法包含在现有的 .NET 标准 2.0 项目中的最佳方法是什么?

谢谢!

奖励问题:我已经看到 CoreFX 存储库对具有 IL 类的项目使用".ilproj"文件,但 VS 实际上不支持它们。这是某个扩展,还是他们使用自己的自定义脚本来支持该项目格式?我知道有一个ILProj扩展可用,但它既不是官方的,也不是与VS2017兼容的。

编辑:我可能刚刚对如何解决这个问题有一个想法,考虑一下:

public static void Foo<T>(ref T value)
{
fixed (void* p = &Unsafe.As<T, byte>(ref value))
{
// Shouldn't the ref T be correctly fixed here, since
// the "dummy" byte ref had the same address?
}
}

是的,将引用存储在固定的本地就足够了。在方法执行期间,引用的内存不会被移动,因此指针将保持有效。

您还可以从方法中删除nops(分析在 Debug 下编译的代码的结果),之后将变量设置为零也是不必要的,因为该方法在它之后退出。

至于如何使用C#项目分发CIL代码,我会使用DynamicMethod,但我不知道在.NET Standard中是否可用。如果没有,将 IL 编译为.dll.netmodule并引用它或自动将其链接到主项目并不是一个大问题。

最新更新