-
Notifications
You must be signed in to change notification settings - Fork 5.2k
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 12 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,13 @@ public static unsafe T[] AllocateUninitializedArray<T>(int length, bool pinned = | |
|
|
||
| #endif | ||
| } | ||
| else if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) | ||
| { | ||
| ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); | ||
| } | ||
|
|
||
| // Runtime overrides GC_ALLOC_ZEROING_OPTIONAL if the type contains references, so we don't need to worry about that. | ||
| GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; | ||
jkotas marked this conversation as resolved.
Show resolved
Hide 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
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -775,18 +769,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.
Show resolved
Hide resolved
|
||
| { | ||
| unsafe fixed byte _[7]; | ||
|
||
| 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 length = 100; | ||
| var rng = new Random(0xAF); | ||
|
|
||
| var array = uninitialized ? GC.AllocateUninitializedArray<EmbeddedValueType<string>>(length, pinned: true) : GC.AllocateArray<EmbeddedValueType<string>>(length, pinned: true); | ||
jkotas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| byte* pointer = (byte*)Unsafe.AsPointer(ref array[0]); | ||
| var size = Unsafe.SizeOf<EmbeddedValueType<string>>(); | ||
|
|
||
| for(int i = 0; i < length; ++i) | ||
| { | ||
| var idx = rng.Next(length); | ||
| ref var evt = ref Unsafe.AsRef<EmbeddedValueType<string>>(pointer + size * idx); | ||
|
|
||
| var stringValue = rng.NextSingle().ToString(); | ||
jkotas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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.