-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Allow reference types for pinned GC.AllocateArray() #89293
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
Changes from 7 commits
d579879
5fc9157
376fb89
808d582
ea6de35
c403266
30fd375
13fe7a1
3aeca6b
2e502dc
d7dfad7
3a1bca6
4b805e5
f3aff36
349970a
bfe62ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -732,9 +732,6 @@ internal static void UnregisterMemoryLoadChangeNotification(Action notification) | |
| /// <typeparam name="T">Specifies the type of the array element.</typeparam> | ||
| /// <param name="length">Specifies the length of the array.</param> | ||
| /// <param name="pinned">Specifies whether the allocated array must be pinned.</param> | ||
| /// <remarks> | ||
| /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references. | ||
| /// </remarks> | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path) | ||
| public static unsafe T[] AllocateUninitializedArray<T>(int length, bool pinned = false) // T[] rather than T?[] to match `new T[length]` behavior | ||
| { | ||
|
|
@@ -757,16 +754,12 @@ public static unsafe T[] AllocateUninitializedArray<T>(int length, bool pinned = | |
|
|
||
| #endif | ||
| } | ||
| else if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) | ||
| { | ||
| ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); | ||
| } | ||
|
|
||
| GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; | ||
|
jkotas marked this conversation as resolved.
|
||
| if (pinned) | ||
| flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; | ||
|
|
||
| return Unsafe.As<T[]>(AllocateNewArray(RuntimeTypeHandle.ToIntPtr(typeof(T[]).TypeHandle), length, flags)); | ||
| return Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags)); | ||
|
jkotas marked this conversation as resolved.
Outdated
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! I had no idea it was an intrinsic, how is that discoverable? I changed it to be identical to the similar usage just below: https://github.com/dotnet/runtime/pull/89293/files/d7dfad7341d828be78b09cbe1e49e696d09fac4c#diff-63ddcf9eb1f63c1ddef526934b79b086b1d30883f3293847bf966d047bc7666dR781 Perhaps the should instead revert this line and change the other to use the intrinsic?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
JIT intrinsics are marked with
Agree.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I'll do the change once I get closure on the other discussion |
||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -775,18 +768,12 @@ public static unsafe T[] AllocateUninitializedArray<T>(int length, bool pinned = | |
| /// <typeparam name="T">Specifies the type of the array element.</typeparam> | ||
| /// <param name="length">Specifies the length of the array.</param> | ||
| /// <param name="pinned">Specifies whether the allocated array must be pinned.</param> | ||
| /// <remarks> | ||
| /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references. | ||
| /// </remarks> | ||
| public static T[] AllocateArray<T>(int length, bool pinned = false) // T[] rather than T?[] to match `new T[length]` behavior | ||
| { | ||
| GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS; | ||
|
|
||
| if (pinned) | ||
| { | ||
| if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) | ||
| ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); | ||
|
|
||
| flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1058,11 +1058,52 @@ private static void AllocateArrayTooLarge() | |
| Assert.Throws<OutOfMemoryException>(() => GC.AllocateUninitializedArray<double>(int.MaxValue, pinned: true)); | ||
| } | ||
|
|
||
| [Fact] | ||
| private static void AllocateArrayRefType() | ||
| struct EmbeddedValueType<T> | ||
|
jkotas marked this conversation as resolved.
|
||
| { | ||
| unsafe fixed byte _[7]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a specific reason for this? It looks like trying to fiddle with offsets and alignment. I tend to avoid these kind of mechanisms in tests without a comment as to the why.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was a simple way of increasing the size of the struct, making it less trivially laid out and requiring padding bits - to add a bit of confidence in we're not just overwriting the first set of bytes in a memory location. We could add a comment or just remove it, it's not required.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a non-generic struct like this would be sufficient for what you are trying to test here
jkotas marked this conversation as resolved.
Outdated
|
||
| public T Value; | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData(false), InlineData(true)] | ||
| private static void AllocateArray_UninitializedOrNot_WithManagedType_DoesNotThrow(bool pinned) | ||
| { | ||
| void TryType<T>() | ||
| { | ||
| GC.AllocateUninitializedArray<T>(100, pinned); | ||
| GC.AllocateArray<T>(100, pinned); | ||
|
|
||
| GC.AllocateArray<EmbeddedValueType<T>>(100, pinned); | ||
| GC.AllocateUninitializedArray<EmbeddedValueType<T>>(100, pinned); | ||
| } | ||
|
|
||
| TryType<string>(); | ||
| TryType<object>(); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData(false), InlineData(true)] | ||
| private unsafe static void AllocateArrayPinned_ManagedValueType_CanRoundtripThroughPointer(bool uninitialized) | ||
| { | ||
| GC.AllocateUninitializedArray<string>(100); | ||
| Assert.Throws<ArgumentException>(() => GC.AllocateUninitializedArray<string>(100, pinned: true)); | ||
| const int k_Length = 100; | ||
|
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
|
||
| var rng = new Random(0xAF); | ||
|
|
||
| var array = uninitialized ? GC.AllocateUninitializedArray<EmbeddedValueType<string>>(k_Length, pinned: true) : GC.AllocateArray<EmbeddedValueType<string>>(k_Length, pinned: true); | ||
| byte* pointer = (byte*)Unsafe.AsPointer(ref array[0]); | ||
| var size = Unsafe.SizeOf<EmbeddedValueType<string>>(); | ||
|
|
||
| for(int i = 0; i < k_Length; ++i) | ||
| { | ||
| var idx = rng.Next(k_Length); | ||
| ref var evt = ref Unsafe.AsRef<EmbeddedValueType<string>>(pointer + size * idx); | ||
|
|
||
| var stringValue = rng.NextSingle().ToString(); | ||
| evt.Value = stringValue; | ||
|
|
||
| Assert.Equal(evt.Value, array[idx].Value); | ||
| } | ||
|
|
||
| GC.KeepAlive(array); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.