问题
Before the actual question, a small disclaimer:
This is a related/follow up question to this one, but since here I'm talking about a more general issue (how to pin a
ref T
variable to be able to perform pointer operations on it), and I'm proposing a possible (probably wrong) solution, I opened a separate question.
So, the question is: given a ref T
variable (assuming it's the first item of an array), how can I pin it so that the GC won't cause problems while working on the underlying array with unsafe pointers?
I'm not sure this is possible in C# (but I hope I'm wrong), but looking at the IL code for a method that just fixes a ref int
variable, I tried to come up with a variant that worked on generic types. Here's my idea:
Assume I have this delegate
in the class "MyTestClass":
public unsafe delegate void UnsafeAction(void* p);
Then, in IL:
.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
}
The idea would be to be able to use this method like this:
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);
}
Would something like this work? If so, what's the best way to include such a method written in IL into an existing .NET Standard 2.0 project?
Thanks!
Bonus question: I've seen the CoreFX repo using ".ilproj" files for projects with IL classes, but they're not actually supported by VS. Is that some extension or do they use their own custom scripts to support that project format? I know there's an ILProj extension available, but it isn't either official nor compatible with VS2017.
Edit: I might have just had an idea on how to solve this issue, consider this:
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?
}
}
回答1:
Yes, storing the reference in a pinned local is sufficient. During the execution of the method, the referenced memory will not be moved and therefore the pointer will remain valid.
You can also remove the nop
s from your method (the result of analyzing code compiled under Debug), and setting the variable to zero afterwards is also unnecessary, because the method exists just after it.
As for how to distribute the CIL code with a C# project, I would use DynamicMethod, but I don't know if that is available in .NET Standard. If not, compiling the IL to a .dll
or .netmodule
and referencing it or automatically linking it to the main project is not such a big issue.
来源:https://stackoverflow.com/questions/48095145/il-method-to-pin-a-ref-t-value-as-void-to-work-on-spant-from-parallel-code