Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
212 views
in Technique[技术] by (71.8m points)

c# - IL method to pin a ref T value as void* (to work on Span<T> from parallel code)

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?
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 nops from your method (the result of analyzing code compiled under Debug), and setting the variable to zero afterwards is also unnecessary, because the method exits 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...