.net 有没有一种方法可以将MemoryHandle独占地存储在非托管内存中?

ut6juiuv  于 12个月前  发布在  .NET
关注(0)|答案(3)|浏览(129)

我有一个方法,它接受一个Memory<T>对象。我想固定它,并将一个指向它的指针 * 独占地 * 存储在非托管内存中。我知道我可以通过使用memory.Pin()创建一个MemoryHandle,然后抓取memoryHandle.Pointer来获得这个固定的指针。然而,因为我只想将它存储在非托管内存中,所以我需要确保内存句柄指向的托管对象在我完成它之前不会被收集。有没有一种方法可以在不收集底层对象的情况下将MemoryHandle存储在非托管内存中,而不是在以后释放它?
我知道在理论上,这可以通过将GCHandle存储在我的指针旁边而不是在我完成后处理它来完成。问题是我看不到任何从MemoryMemoryHandle对象中检索GCHandle的方法,即使我很确定至少内存句柄内部包含GC句柄。所以,如果有办法从MemoryHandle中得到GCHandle,那将是这个问题的一个很好的解决方案。

67up9zun

67up9zun1#

您可以使用Marshal.StructureToPtrMarshal.PtrToStructure将其封送。

MemoryHandle memoryHandle = data.Pin();
try
{
    Marshal.StructureToPtr(meoryHandle, YourPointerHere, false);
}
catch
{
    memoryHandle.Dispose();  // dispose the handle in case of failure only
    throw;
}

个字符
请注意,如果Memory<T>MemoryManager支持,那么句柄中将使用IPinnable,这意味着它可能无法正确编组。

b1zrtrql

b1zrtrql2#

我只是有一个想法,这可能可以通过将GCHandle存储到MemoryHandle而不是底层对象本身来完成。类似于:

MemoryHandle memoryHandle = data.Pin();

void* pMemory = memoryHandle.Pointer;
GCHandle memoryHandleHandle = GCHandle.Alloc(memoryHandle);

// Store pMemory and memoryHandleHandle in unmanaged memory...
// Then to dispose:

((MemoryHandle) memoryHandleHandle.Target!).Dispose();
memoryHandleHandle.Free();

字符串
我认为这是可行的,也是安全的,但是与仅仅将GCHandle放到底层对象的理论解决方案相比,感觉非常愚蠢,脆弱和丑陋。最大的真实的缺点是,这是以装箱分配(装箱MemoryHandle)为代价的,考虑到这是一个非常热的代码路径,我宁愿避免这一点。

twh00eeo

twh00eeo3#

这是我想出的解决方案。它是一个简单的 Package 器周围的GCHandle。有一个方法,创建句柄从一个MemoryHandle在一个安全的方式,避免对象或GCHandle分配在绝大多数情况下。重要的是,你不要处置MemoryHandle你创建了UnmanagedMemoryHandle

public unsafe readonly struct UnmanagedMemoryHandle : IDisposable {

    // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Buffers/MemoryHandle.cs
    private struct ExposedMemoryHandle {
        public void* Pointer;
        public GCHandle Handle;
        public IPinnable? Pinnable;
    }

    public static UnmanagedMemoryHandle FromMemoryHandle(MemoryHandle memoryHandle) {
        return FromMemoryHandle(ref memoryHandle);
    }

    public static UnmanagedMemoryHandle FromMemoryHandle(ref MemoryHandle memoryHandle) {
        ref ExposedMemoryHandle exposedMemoryHandle = ref Unsafe.As<MemoryHandle, ExposedMemoryHandle>(ref memoryHandle);

        bool hasHandle = exposedMemoryHandle.Handle.IsAllocated;
        bool hasPinnable = exposedMemoryHandle.Pinnable != null;

        GCHandle handle;

        if (hasHandle && !hasPinnable) {
            if (exposedMemoryHandle.Handle.Target is not IPinnable) {
                // If we only have a handle, we just store that handle
                handle = exposedMemoryHandle.Handle;
            } else {
                // Later, we check if the handle is an instance to IPinnable to see if we need to unpin it.
                //  That's a problem in the case the the Handle just happens to be an IPinnable. In this case
                //  we wrap our handle in a type that is not IPinnable
                handle = GCHandle.Alloc(new DoubleMemoryHandle(exposedMemoryHandle.Handle, null));
            }
        } else if (hasPinnable && !hasHandle) {
            // If we only have a pinnable, we store a handle to that pinnable
            handle = GCHandle.Alloc(exposedMemoryHandle.Pinnable);
        } else if (hasHandle && hasPinnable) {
            // If we have both, store a handle to an object containing the handle and pinnable
            handle = GCHandle.Alloc(new DoubleMemoryHandle(exposedMemoryHandle.Handle, exposedMemoryHandle.Pinnable));
        } else {
            // If we have neither, we don't need to store anything
            handle = default;
        }

        return new UnmanagedMemoryHandle(handle);
    }

    private record DoubleMemoryHandle(GCHandle Handle, IPinnable? Pinnable);

    public readonly GCHandle Handle;

    private UnmanagedMemoryHandle(GCHandle handle) {
        Handle = handle;
    }

    public void Dispose() {
        if (!Handle.IsAllocated) return;

        object? target = Handle.Target;

        if (target is IPinnable pinnable) {
            pinnable.Unpin();
        } else if (target is DoubleMemoryHandle doubleMemoryHandle) {
            doubleMemoryHandle.Handle.Free();
            doubleMemoryHandle.Pinnable?.Unpin();
        }

        Handle.Free();
    }
}

字符串

相关问题