Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Add CreateFromPinnedArray to System.Memory ref and add tests#28992

Merged
ahsonkhan merged 6 commits into
dotnet:masterfrom
ahsonkhan:MoveCreateMemory
Apr 11, 2018
Merged

Add CreateFromPinnedArray to System.Memory ref and add tests#28992
ahsonkhan merged 6 commits into
dotnet:masterfrom
ahsonkhan:MoveCreateMemory

Conversation

@ahsonkhan
Copy link
Copy Markdown

@ahsonkhan ahsonkhan commented Apr 10, 2018

Part of https://github.com/dotnet/corefx/issues/28954

Depends on: dotnet/coreclr#17500

TODO: Update newly added tests to use MemoryMarshal.CreateFromPinnedArray

cc @KrzysztofCwalina, @stephentoub, @GrabYourPitchforks, @davidfowl, @pakrym

GC.Collect(2);

Assert.Equal((int)pinnedPtr, (int)Unsafe.AsPointer(ref MemoryMarshal.GetReference(pinnedMemory.Span)));
Assert.NotEqual((int)ptr, (int)Unsafe.AsPointer(ref MemoryMarshal.GetReference(memory.Span)));
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test appropriate (i.e. it will always pass due to the GC.Collect)? cc @jkotas

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int[] pinnedArray = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
GCHandle handle = GCHandle.Alloc(pinnedArray, GCHandleType.Pinned);

var memory = new Memory<int>(unpinnedArray, 0, 2);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you call it unpinnedMemory?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer relevant.

{
int[] unpinnedArray = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
int[] pinnedArray = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
GCHandle handle = GCHandle.Alloc(pinnedArray, GCHandleType.Pinned);
Copy link
Copy Markdown
Member

@KrzysztofCwalina KrzysztofCwalina Apr 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you call it pinnedHandle?

void* memoryHandlePinnedPtr = pinnedMemory.Pin().Pointer;

GC.Collect();
GC.Collect(2);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is absolutely guaranteeing that the GC will move the array, and so the test might fail from time to time.

Copy link
Copy Markdown
Author

@ahsonkhan ahsonkhan Apr 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I thought. In which case, I will remove this Assert (but I will keep the "pinned" components).

Assert.NotEqual((int)ptr, (int)Unsafe.AsPointer(ref MemoryMarshal.GetReference(memory.Span)));

Assert.Equal((int)memoryHandlePinnedPtr, (int)handle.AddrOfPinnedObject().ToPointer());
Assert.NotEqual((int)memoryHandlePtr, (int)memory.Pin().Pointer);
Copy link
Copy Markdown
Member

@KrzysztofCwalina KrzysztofCwalina Apr 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to unpin the arrays (both of them) after the test?

Copy link
Copy Markdown
Author

@ahsonkhan ahsonkhan Apr 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to explicitly call Free on the GCHandle? It won't automatically get disposed after the method returns?

It looks like we do:

This GCHandle must be released with Free when it is no longer needed.

Assert.NotEqual((int)memoryHandlePtr, (int)memory.Pin().Pointer);
}
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have a test that verifies that when memory is created from a prepinned array, it does not cause additional GCHandles to be created during Memory.Pin calls. I am not sure how to write such test, but maybe somebody on the runtime team would know. @jkotas?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g. you would need to start the test in separate process, enable tracing for it, and verify via trace that there was no GC handle allocated. I do not think it is worth the effort with what we have available today. Writing similar tests may become more viable once we have the ability for processes to self-trace.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing similar tests may become more viable once we have the ability for processes to self-trace.

FWIW, we do something similar in a variety of corefx test suites for EventSource-based tracing, e.g.
https://github.com/dotnet/corefx/blob/master/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs#L31
but assuming GC handle-related tracing doesn't come from an EventSource, as Jan suggests that's going to be a lot more involved.

@ahsonkhan ahsonkhan added the blocked Issue/PR is blocked on something - see comments label Apr 10, 2018
public static System.Memory<T> AsMemory<T>(System.ReadOnlyMemory<T> memory) { throw null; }
public static System.ReadOnlySpan<TTo> Cast<TFrom, TTo>(System.ReadOnlySpan<TFrom> span) where TFrom : struct where TTo : struct { throw null; }
public static System.Span<TTo> Cast<TFrom, TTo>(System.Span<TFrom> span) where TFrom : struct where TTo : struct { throw null; }
public static System.Memory<T> CreateFromPinnedArray<T>(T[] array, int start, int length) { throw null; }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not also removing it from Memory? Are you doing that separately?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of staging the change and removing it after asp.net updates its use. Depending on how quickly they can consume new corefx packages, I might remove it earlier. dotnet/extensions#335

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't block (stage) the change this close to the RC deadline. They can make the update as part of the dotnet-maestro-bot PR. /c @pakrym

* Add CreateFromPinnedArray to MemoryMarshal

* Address PR feedback: Add a warning remark to CreateFromPinnedArray

Signed-off-by: dotnet-bot-corefx-mirror <dotnet-bot@microsoft.com>
@ahsonkhan ahsonkhan removed the blocked Issue/PR is blocked on something - see comments label Apr 11, 2018
@ahsonkhan
Copy link
Copy Markdown
Author

Cherry-picked from mirror PR: #29012

if (_length < 0)
{
#if FEATURE_PORTABLE_SPAN
void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
Copy link
Copy Markdown
Author

@ahsonkhan ahsonkhan Apr 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to highlight a fix for a bug here in Memory.Pin where handle could be null (for portable span, for pre-pinned arrays). Please review.

Tests from this PR caught the bug:
https://mc.dot.net/#/user/ahsonkhan/pr~2Fjenkins~2Fdotnet~2Fcorefx~2Fmaster~2F/test~2Ffunctional~2Fcli~2F/f4b9ba1fd81623c838cc27647936c3569e83aa22/workItem/System.Memory.Tests/analysis/xunit/System.SpanTests.MemoryMarshalTests~2FCreateFromPinnedArrayVerifyPinning
System.SpanTests.MemoryMarshalTests/CreateFromPinnedArrayVerifyPinning

System.InvalidOperationException : Handle is not initialized.
Stack Trace :
   at System.Runtime.InteropServices.GCHandle.AddrOfPinnedObject()
   at System.Memory`1.Pin() in D:\j\workspace\windows-TGrou---2a8f9c29\src\Common\src\CoreLib\System\Memory.cs:line 358
   at System.SpanTests.MemoryMarshalTests.CreateFromPinnedArrayVerifyPinning() in D:\j\workspace\windows-TGrou---2a8f9c29\src\System.Memory\tests\MemoryMarshal\CreateFromPinnedArray.cs:line 140

cc @stephentoub, @KrzysztofCwalina, @jkotas, @GrabYourPitchforks

@joshfree joshfree added this to the 2.1.0 milestone Apr 11, 2018
@ahsonkhan
Copy link
Copy Markdown
Author

@stephentoub stephentoub modified the milestone: 2.1.0 Apr 11, 2018
@ahsonkhan ahsonkhan merged commit b4d701a into dotnet:master Apr 11, 2018
@ahsonkhan ahsonkhan deleted the MoveCreateMemory branch April 11, 2018 20:43
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…corefx#28992)

* Add CreateFromPinnedArray to System.Memory ref and add tests

* Update CreateFromPinnedArray tests

* Removing asserts on unpinned array since there is no guarantee that GC will move it.

* Add CreateFromPinnedArray to MemoryMarshal (dotnet/coreclrdotnet/corefx#17500)

* Add CreateFromPinnedArray to MemoryMarshal

* Address PR feedback: Add a warning remark to CreateFromPinnedArray

Signed-off-by: dotnet-bot-corefx-mirror <dotnet-bot@microsoft.com>

* Fixing bug in Memory.Pin and adding API to uapaot baseline


Commit migrated from dotnet/corefx@b4d701a
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants