Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Interop marshaling for Span<T> #9113

Closed
morganbr opened this issue Oct 12, 2017 · 5 comments
Closed

Proposal: Interop marshaling for Span<T> #9113

morganbr opened this issue Oct 12, 2017 · 5 comments

Comments

@morganbr
Copy link
Contributor

One of the valuable aspects of Span is it keeps code safe and largely pointer-free. However, at the interop boundary, Span isn't supported, which leads to code like:

fixed (byte* bufPtr = &buffer.DangerousGetPinnableReference())
{
    bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length));
}

This both forces the method to become unsafe and is a lot of extra work. We should add marshaling support for Span to allow it to be used end-to-end.

For the most part, existing array marshaling semantics are a good match for Spans. For example:

[DllImport("kernel32.dll")]
static extern uint GetTempPath(uint nBufferLength, Span<char>lpBuffer);

would marshal the Span's underlying pointer (pinning as necessary). Reverse interop is a bit trickier. For example, the native signature of HeapAlloc is:
LPVOID WINAPI HeapAlloc(_In_ HANDLE hHeap, _In_ DWORD dwFlags, _In_ SIZE_T dwBytes);
If we wanted a span wrapping the returned pointer, an ideal P/Invoke would look like:

static extern Span<byte> HeapAlloc(SafeHandle hHeap, uint dwFlags, UIntPtr dwBytes);

but that doesn't tell the Span its length. It may be enough to add MarshalAsAttribute with SizeParamIndex (or SizeConst):

static extern [MarshalAs(SizeParamIndex=3)] Span<byte> HeapAlloc(SafeHandle hHeap, uint dwFlags, UIntPtr dwBytes);

Open questions:

  1. Should the Span's elements be marshaled as we would for an array? For example, a bool is 1 byte in managed code, but defaults to marshaling to 4 bytes. If we apply this with Spans, it requires copying in those cases.
  2. How should InAttribute and OutAttribute apply? Should they match what we do with arrays?
  3. In reverse interop cases, the Span may hold native memory that now needs to be freed. What's a good pattern for freeing the memory and getting rid of the now-wild pointer in the Span?
@morganbr
Copy link
Contributor Author

@ghost
Copy link

ghost commented Oct 12, 2017

  1. My expectation would be that Span on a P/Invoke would act more like refs and pointers than arrays. No copying, data type must be blittable. I already know that the P/Invoke'd target is not going to honor any range checking imposed by the Span's own length so the value of declaring a P/Invoke argument as Span is simply brevity for those cases where the caller already has a Span. The length is just ballast.

  2. Given 1, In and Out are irrelevant.

  3. I don't think it makes much sense to marshal Span's in this direction. Even if you provided some MarshalAs metadata to compute the size from something else, my inclination would still be to marshal as pointer or SafeHandle and wrap a Span around it.

In general, we built a lot of "convenience magic" into the CLR 1.0 P/Invoke mechanism, but generally, I find myself preferring to limit reliance on the more advanced magic and making any "complicated" marshaling steps explicit in my code.

@GF-Huang
Copy link

GF-Huang commented Feb 12, 2021

So, can use this syntax now in .NET 5?

[DllImport("kernel32.dll")]
static extern uint GetTempPath(uint nBufferLength, Span<char> lpBuffer);

@stephentoub
Copy link
Member

can use this syntax now in .NET 5?

No

@AaronRobinsonMSFT
Copy link
Member

There are no plans to update the built-in system to use Span<T>. Future investments in expanding type support will be accomplished via source generation - #43060.

@ghost ghost locked as resolved and limited conversation to collaborators Jul 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants