diff --git a/src/Common/src/CoreLib/System/IO/FileStreamCompletionSource.Win32.cs b/src/Common/src/CoreLib/System/IO/FileStreamCompletionSource.Win32.cs index 82274b131f4b..62ace0918d71 100644 --- a/src/Common/src/CoreLib/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/Common/src/CoreLib/System/IO/FileStreamCompletionSource.Win32.cs @@ -246,7 +246,7 @@ private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSour internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory memory) : base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes { - _handle = memory.Retain(pin: true); + _handle = memory.Pin(); } internal override void ReleaseNativeResource() diff --git a/src/Common/src/CoreLib/System/Memory.cs b/src/Common/src/CoreLib/System/Memory.cs index 508ebcb3ee3b..bb2b1557a990 100644 --- a/src/Common/src/CoreLib/System/Memory.cs +++ b/src/Common/src/CoreLib/System/Memory.cs @@ -270,9 +270,49 @@ public Span Span public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); /// - /// Returns a handle for the array. - /// If pin is true, the GC will not move the array and hence its address can be taken + /// Creates a handle for the memory. + /// The GC will not move the array until the returned + /// is disposed, enabling taking and using the memory's address. /// + public unsafe MemoryHandle Pin() + { + if (_index < 0) + { + return ((OwnedMemory)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf()); + } + else if (typeof(T) == typeof(char) && _object is string s) + { + // This case can only happen if a ReadOnlyMemory was created around a string + // and then that was cast to a Memory using unsafe / marshaling code. This needs + // to work, however, so that code that uses a single Memory field to store either + // a readable ReadOnlyMemory or a writable Memory can still be pinned and + // used for interop purposes. + GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref s.GetRawStringData()), _index); +#endif // FEATURE_PORTABLE_SPAN + return new MemoryHandle(null, pointer, handle); + } + else if (_object is T[] array) + { + var handle = GCHandle.Alloc(array, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); +#endif // FEATURE_PORTABLE_SPAN + return new MemoryHandle(null, pointer, handle); + } + return default; + } + + /// [Obsolete, use Pin()] Creates a handle for the memory. + /// + /// If pin is true, the GC will not move the array until the returned + /// is disposed, enabling taking and using the memory's address. + /// public unsafe MemoryHandle Retain(bool pin = false) { MemoryHandle memoryHandle = default; diff --git a/src/Common/src/CoreLib/System/ReadOnlyMemory.cs b/src/Common/src/CoreLib/System/ReadOnlyMemory.cs index 5f3f0e19805e..dca7db3dfd16 100644 --- a/src/Common/src/CoreLib/System/ReadOnlyMemory.cs +++ b/src/Common/src/CoreLib/System/ReadOnlyMemory.cs @@ -226,10 +226,44 @@ public ReadOnlySpan Span /// The span to copy items into. public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); - /// Creates a handle for the memory. + /// + /// Creates a handle for the memory. + /// The GC will not move the array until the returned + /// is disposed, enabling taking and using the memory's address. + /// + public unsafe MemoryHandle Pin() + { + if (_index < 0) + { + return ((OwnedMemory)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf()); + } + else if (typeof(T) == typeof(char) && _object is string s) + { + GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref s.GetRawStringData()), _index); +#endif // FEATURE_PORTABLE_SPAN + return new MemoryHandle(null, pointer, handle); + } + else if (_object is T[] array) + { + var handle = GCHandle.Alloc(array, GCHandleType.Pinned); +#if FEATURE_PORTABLE_SPAN + void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); +#else + void* pointer = Unsafe.Add(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); +#endif // FEATURE_PORTABLE_SPAN + return new MemoryHandle(null, pointer, handle); + } + return default; + } + + /// [Obsolete, use Pin()] Creates a handle for the memory. /// /// If pin is true, the GC will not move the array until the returned - /// is disposed, enabling the memory's address can be taken and used. + /// is disposed, enabling taking and using the memory's address. /// public unsafe MemoryHandle Retain(bool pin = false) { diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 637aae916dc0..861ee6ca9b35 100644 --- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -232,7 +232,7 @@ internal static int Encrypt(SafeSslHandle context, ReadOnlyMemory input, r int retVal; unsafe { - using (MemoryHandle handle = input.Retain(pin: true)) + using (MemoryHandle handle = input.Pin()) { retVal = Ssl.SslWrite(context, (byte*)handle.Pointer, input.Length); } diff --git a/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs b/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs index fc3b8ad7aa24..69c914be7da3 100644 --- a/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs +++ b/src/System.IO.Compression/src/System/IO/Compression/DeflateZLib/Deflater.cs @@ -100,7 +100,7 @@ internal unsafe void SetInput(ReadOnlyMemory inputBuffer) lock (SyncLock) { - _inputBufferHandle = inputBuffer.Retain(pin: true); + _inputBufferHandle = inputBuffer.Pin(); _zlibStream.NextIn = (IntPtr)_inputBufferHandle.Pointer; _zlibStream.AvailIn = (uint)inputBuffer.Length; diff --git a/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs b/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs index c0d462cd4aef..ab7fd35d1b91 100644 --- a/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs +++ b/src/System.IO.Pipes/src/System/IO/Pipes/PipeCompletionSource.cs @@ -40,7 +40,7 @@ protected PipeCompletionSource(ThreadPoolBoundHandle handle, ReadOnlyMemory { var completionSource = (PipeCompletionSource)ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs index b532f049dfe9..977563723123 100644 --- a/src/System.Memory/ref/System.Memory.cs +++ b/src/System.Memory/ref/System.Memory.cs @@ -107,6 +107,7 @@ public void CopyTo(System.Memory destination) { } public static implicit operator System.Memory (System.ArraySegment segment) { throw null; } public static implicit operator System.ReadOnlyMemory (System.Memory memory) { throw null; } public static implicit operator System.Memory (T[] array) { throw null; } + public System.Buffers.MemoryHandle Pin() { throw null; } public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.Memory Slice(int start) { throw null; } public System.Memory Slice(int start, int length) { throw null; } @@ -131,6 +132,7 @@ public void CopyTo(System.Memory destination) { } public override int GetHashCode() { throw null; } public static implicit operator System.ReadOnlyMemory (System.ArraySegment segment) { throw null; } public static implicit operator System.ReadOnlyMemory (T[] array) { throw null; } + public System.Buffers.MemoryHandle Pin() { throw null; } public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.ReadOnlyMemory Slice(int start) { throw null; } public System.ReadOnlyMemory Slice(int start, int length) { throw null; } diff --git a/src/System.Memory/tests/Memory/Pin.cs b/src/System.Memory/tests/Memory/Pin.cs new file mode 100644 index 000000000000..2d0d8ff5231e --- /dev/null +++ b/src/System.Memory/tests/Memory/Pin.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using Xunit; + +namespace System.MemoryTests +{ + public static partial class MemoryTests + { + [Fact] + public static void MemoryPin() + { + int[] array = { 1, 2, 3, 4, 5 }; + Memory memory = array; + MemoryHandle handle = memory.Pin(); + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i], pointer[i]); + } + } + handle.Dispose(); + } + + [Fact] + public static void MemoryFromEmptyArrayPin() + { + Memory memory = new int[0]; + MemoryHandle handle = memory.Pin(); + Assert.True(handle.HasPointer); + handle.Dispose(); + } + + [Fact] + public static void DefaultMemoryPin() + { + Memory memory = default; + MemoryHandle handle = memory.Pin(); + Assert.False(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer == null); + } + handle.Dispose(); + } + + [Fact] + public static void MemoryPinAndSlice() + { + int[] array = { 1, 2, 3, 4, 5 }; + Memory memory = array; + memory = memory.Slice(1); + MemoryHandle handle = memory.Pin(); + Span span = memory.Span; + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], pointer[i]); + } + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], span[i]); + } + } + handle.Dispose(); + } + + [Fact] + public static void OwnedMemoryPin() + { + int[] array = { 1, 2, 3, 4, 5 }; + OwnedMemory owner = new CustomMemoryForTest(array); + Memory memory = owner.Memory; + MemoryHandle handle = memory.Pin(); + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i], pointer[i]); + } + } + handle.Dispose(); + } + + [Fact] + public static void OwnedMemoryPinAndSlice() + { + int[] array = { 1, 2, 3, 4, 5 }; + OwnedMemory owner = new CustomMemoryForTest(array); + Memory memory = owner.Memory.Slice(1); + MemoryHandle handle = memory.Pin(); + Span span = memory.Span; + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], pointer[i]); + } + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], span[i]); + } + } + handle.Dispose(); + } + } +} diff --git a/src/System.Memory/tests/Memory/Retain.cs b/src/System.Memory/tests/Memory/Retain.cs index 3a33817dff1c..1ef319103c5f 100644 --- a/src/System.Memory/tests/Memory/Retain.cs +++ b/src/System.Memory/tests/Memory/Retain.cs @@ -45,6 +45,30 @@ public static void MemoryRetainWithPinning() handle.Dispose(); } + [Fact] + public static void MemoryFromEmptyArrayRetainWithPinning() + { + Memory memory = new int[0]; + MemoryHandle handle = memory.Retain(pin: true); + Assert.True(handle.HasPointer); + handle.Dispose(); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public static void DefaultMemoryRetain(bool pin) + { + Memory memory = default; + MemoryHandle handle = memory.Retain(pin: pin); + Assert.False(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer == null); + } + handle.Dispose(); + } + [Fact] public static void MemoryRetainWithPinningAndSlice() { @@ -110,15 +134,6 @@ public static void OwnedMemoryRetainWithPinning() handle.Dispose(); } - [Fact] - public static void MemoryFromEmptyArrayRetainWithPinning() - { - Memory memory = new int[0]; - MemoryHandle handle = memory.Retain(pin: true); - Assert.True(handle.HasPointer); - handle.Dispose(); - } - [Fact] public static void OwnedMemoryRetainWithPinningAndSlice() { @@ -146,20 +161,5 @@ public static void OwnedMemoryRetainWithPinningAndSlice() } handle.Dispose(); } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public static void DefaultMemoryRetain(bool pin) - { - Memory memory = default; - MemoryHandle handle = memory.Retain(pin: pin); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } } } diff --git a/src/System.Memory/tests/Memory/Strings.cs b/src/System.Memory/tests/Memory/Strings.cs index 6e4e0bfe0539..b711179cd1ae 100644 --- a/src/System.Memory/tests/Memory/Strings.cs +++ b/src/System.Memory/tests/Memory/Strings.cs @@ -57,18 +57,13 @@ public static void Memory_Slice_MatchesSubstring(string input, int offset, int c } [Fact] - public static unsafe void Memory_Retain_ExpectedPointerValue() + public static unsafe void Memory_Pin_ExpectedPointerValue() { string input = "0123456789"; ReadOnlyMemory readonlyMemory = input.AsMemory(); Memory m = MemoryMarshal.AsMemory(readonlyMemory); - using (MemoryHandle h = m.Retain(pin: false)) - { - Assert.Equal(IntPtr.Zero, (IntPtr)h.Pointer); - } - - using (MemoryHandle h = m.Retain(pin: true)) + using (MemoryHandle h = m.Pin()) { GC.Collect(); fixed (char* ptr = input) diff --git a/src/System.Memory/tests/MemoryMarshal/AsMemory.cs b/src/System.Memory/tests/MemoryMarshal/AsMemory.cs index 43b3ab1c3ae7..40990d08cbe7 100644 --- a/src/System.Memory/tests/MemoryMarshal/AsMemory.cs +++ b/src/System.Memory/tests/MemoryMarshal/AsMemory.cs @@ -39,17 +39,17 @@ public static IEnumerable ReadOnlyMemoryCharInstances() [Theory] [MemberData(nameof(ReadOnlyMemoryInt32Instances))] - public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) => AsMemory_Roundtrips_Core(readOnlyMemory); + public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) => AsMemory_Roundtrips_Core(readOnlyMemory, true); [Theory] [MemberData(nameof(ReadOnlyMemoryObjectInstances))] - public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) => AsMemory_Roundtrips_Core(readOnlyMemory); + public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) => AsMemory_Roundtrips_Core(readOnlyMemory, false); [Theory] [MemberData(nameof(ReadOnlyMemoryCharInstances))] public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) { - AsMemory_Roundtrips_Core(readOnlyMemory); + AsMemory_Roundtrips_Core(readOnlyMemory, true); Memory memory = MemoryMarshal.AsMemory(readOnlyMemory); ReadOnlyMemory readOnlyClone = memory; @@ -66,7 +66,7 @@ public static void AsMemory_Roundtrips(ReadOnlyMemory readOnlyMemory) } } - private static unsafe void AsMemory_Roundtrips_Core(ReadOnlyMemory readOnlyMemory) + private static unsafe void AsMemory_Roundtrips_Core(ReadOnlyMemory readOnlyMemory, bool canBePinned) { Memory memory = MemoryMarshal.AsMemory(readOnlyMemory); ReadOnlyMemory readOnlyClone = memory; @@ -87,13 +87,16 @@ private static unsafe void AsMemory_Roundtrips_Core(ReadOnlyMemory readOnl Assert.Equal(array1.Offset, array2.Offset); Assert.Equal(array1.Count, array2.Count); - // Retain - using (MemoryHandle readOnlyMemoryHandle = readOnlyMemory.Retain()) - using (MemoryHandle readOnlyCloneHandle = readOnlyMemory.Retain()) - using (MemoryHandle memoryHandle = readOnlyMemory.Retain()) + if (canBePinned) { - Assert.Equal((IntPtr)readOnlyMemoryHandle.Pointer, (IntPtr)readOnlyCloneHandle.Pointer); - Assert.Equal((IntPtr)readOnlyMemoryHandle.Pointer, (IntPtr)memoryHandle.Pointer); + // Pin + using (MemoryHandle readOnlyMemoryHandle = readOnlyMemory.Pin()) + using (MemoryHandle readOnlyCloneHandle = readOnlyMemory.Pin()) + using (MemoryHandle memoryHandle = readOnlyMemory.Pin()) + { + Assert.Equal((IntPtr)readOnlyMemoryHandle.Pointer, (IntPtr)readOnlyCloneHandle.Pointer); + Assert.Equal((IntPtr)readOnlyMemoryHandle.Pointer, (IntPtr)memoryHandle.Pointer); + } } } } diff --git a/src/System.Memory/tests/ReadOnlyMemory/Pin.cs b/src/System.Memory/tests/ReadOnlyMemory/Pin.cs new file mode 100644 index 000000000000..803612035cae --- /dev/null +++ b/src/System.Memory/tests/ReadOnlyMemory/Pin.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using Xunit; + +namespace System.MemoryTests +{ + public static partial class ReadOnlyMemoryTests + { + [Fact] + public static void MemoryPin() + { + int[] array = { 1, 2, 3, 4, 5 }; + ReadOnlyMemory memory = array; + MemoryHandle handle = memory.Pin(); + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i], pointer[i]); + } + } + handle.Dispose(); + } + + [Fact] + public static void MemoryFromEmptyArrayPin() + { + ReadOnlyMemory memory = new int[0]; + MemoryHandle handle = memory.Pin(); + Assert.True(handle.HasPointer); + handle.Dispose(); + } + + [Fact] + public static void DefaultMemoryPin() + { + ReadOnlyMemory memory = default; + MemoryHandle handle = memory.Pin(); + Assert.False(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer == null); + } + handle.Dispose(); + } + + [Fact] + public static void MemoryPinAndSlice() + { + int[] array = { 1, 2, 3, 4, 5 }; + ReadOnlyMemory memory = array; + memory = memory.Slice(1); + MemoryHandle handle = memory.Pin(); + ReadOnlySpan span = memory.Span; + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], pointer[i]); + } + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], span[i]); + } + } + handle.Dispose(); + } + + [Fact] + public static void OwnedMemoryPin() + { + int[] array = { 1, 2, 3, 4, 5 }; + OwnedMemory owner = new CustomMemoryForTest(array); + ReadOnlyMemory memory = owner.Memory; + MemoryHandle handle = memory.Pin(); + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i], pointer[i]); + } + } + handle.Dispose(); + } + + [Fact] + public static void OwnedMemoryPinAndSlice() + { + int[] array = { 1, 2, 3, 4, 5 }; + OwnedMemory owner = new CustomMemoryForTest(array); + ReadOnlyMemory memory = owner.Memory.Slice(1); + MemoryHandle handle = memory.Pin(); + ReadOnlySpan span = memory.Span; + Assert.True(handle.HasPointer); + unsafe + { + int* pointer = (int*)handle.Pointer; + + GC.Collect(); + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], pointer[i]); + } + + for (int i = 0; i < memory.Length; i++) + { + Assert.Equal(array[i + 1], span[i]); + } + } + handle.Dispose(); + } + } +} diff --git a/src/System.Memory/tests/ReadOnlyMemory/Retain.cs b/src/System.Memory/tests/ReadOnlyMemory/Retain.cs index 6f31b00cd0ae..d438c78d77e7 100644 --- a/src/System.Memory/tests/ReadOnlyMemory/Retain.cs +++ b/src/System.Memory/tests/ReadOnlyMemory/Retain.cs @@ -54,6 +54,21 @@ public static void MemoryFromEmptyArrayRetainWithPinning() handle.Dispose(); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public static void DefaultMemoryRetain(bool pin) + { + ReadOnlyMemory memory = default; + MemoryHandle handle = memory.Retain(pin: pin); + Assert.False(handle.HasPointer); + unsafe + { + Assert.True(handle.Pointer == null); + } + handle.Dispose(); + } + [Fact] public static void MemoryRetainWithPinningAndSlice() { @@ -146,20 +161,5 @@ public static void OwnedMemoryRetainWithPinningAndSlice() } handle.Dispose(); } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public static void DefaultMemoryRetain(bool pin) - { - ReadOnlyMemory memory = default; - MemoryHandle handle = memory.Retain(pin: pin); - Assert.False(handle.HasPointer); - unsafe - { - Assert.True(handle.Pointer == null); - } - handle.Dispose(); - } } } diff --git a/src/System.Memory/tests/ReadOnlyMemory/Strings.cs b/src/System.Memory/tests/ReadOnlyMemory/Strings.cs index ba7e86b8f23f..6a775ad87ad7 100644 --- a/src/System.Memory/tests/ReadOnlyMemory/Strings.cs +++ b/src/System.Memory/tests/ReadOnlyMemory/Strings.cs @@ -94,17 +94,12 @@ public static void AsReadOnlyMemory_TryGetArray_ReturnsFalse() } [Fact] - public static unsafe void AsReadOnlyMemory_Retain_ExpectedPointerValue() + public static unsafe void AsReadOnlyMemory_Pin_ExpectedPointerValue() { string input = "0123456789"; ReadOnlyMemory m = input.AsMemory(); - using (MemoryHandle h = m.Retain(pin: false)) - { - Assert.Equal(IntPtr.Zero, (IntPtr)h.Pointer); - } - - using (MemoryHandle h = m.Retain(pin: true)) + using (MemoryHandle h = m.Pin()) { GC.Collect(); fixed (char* ptr = input) @@ -137,7 +132,7 @@ public static unsafe void AsReadOnlyMemory_PointerAndLength(string text, int sta Assert.Equal(length, m.Length); - using (MemoryHandle h = m.Retain(pin: true)) + using (MemoryHandle h = m.Pin()) { fixed (char* pText = text) { diff --git a/src/System.Memory/tests/System.Memory.Tests.csproj b/src/System.Memory/tests/System.Memory.Tests.csproj index 47a328f7f8dc..27d689a92a88 100644 --- a/src/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/System.Memory/tests/System.Memory.Tests.csproj @@ -163,6 +163,7 @@ + @@ -193,6 +194,7 @@ + diff --git a/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index abccbc36c73a..02943cc70f0f 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -126,7 +126,7 @@ public static SecurityStatusPal EncryptMessage( unsafe { - MemoryHandle memHandle = input.Retain(pin: true); + MemoryHandle memHandle = input.Pin(); try { PAL_TlsIo status; diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index bd311d28c971..0caf5822029b 100644 --- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -183,7 +183,7 @@ private unsafe SocketError ProcessIOCPResultWithSingleBufferHandle(SocketError s // Return pending and we will continue in the completion port callback. if (_singleBufferHandleState == SingleBufferHandleState.InProcess) { - _singleBufferHandle = _buffer.Retain(pin: true); + _singleBufferHandle = _buffer.Pin(); _singleBufferHandleState = SingleBufferHandleState.Set; } return SocketError.IOPending; @@ -199,7 +199,7 @@ internal unsafe SocketError DoOperationAccept(Socket socket, SafeCloseSocket han NativeOverlapped* overlapped = AllocateNativeOverlapped(); try { - _singleBufferHandle = buffer.Retain(pin: true); + _singleBufferHandle = buffer.Pin(); _singleBufferHandleState = SingleBufferHandleState.Set; bool success = socket.AcceptEx( @@ -234,7 +234,7 @@ internal unsafe SocketError DoOperationConnect(Socket socket, SafeCloseSocket ha try { Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); - _singleBufferHandle = _buffer.Retain(pin: true); + _singleBufferHandle = _buffer.Pin(); _singleBufferHandleState = SingleBufferHandleState.Set; bool success = socket.ConnectEx( @@ -472,7 +472,7 @@ internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeClo } Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); - _singleBufferHandle = _buffer.Retain(pin: true); + _singleBufferHandle = _buffer.Pin(); _singleBufferHandleState = SingleBufferHandleState.Set; _wsaRecvMsgWSABufferArray[0].Pointer = (IntPtr)_singleBufferHandle.Pointer; diff --git a/src/System.Runtime/ref/System.Runtime.cs b/src/System.Runtime/ref/System.Runtime.cs index eb92a61fce91..a5e68227835a 100644 --- a/src/System.Runtime/ref/System.Runtime.cs +++ b/src/System.Runtime/ref/System.Runtime.cs @@ -1700,6 +1700,7 @@ public void CopyTo(System.Memory destination) { } public static implicit operator System.Memory (System.ArraySegment segment) { throw null; } public static implicit operator System.ReadOnlyMemory (System.Memory memory) { throw null; } public static implicit operator System.Memory (T[] array) { throw null; } + public System.Buffers.MemoryHandle Pin() { throw null; } public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.Memory Slice(int start) { throw null; } public System.Memory Slice(int start, int length) { throw null; } @@ -1947,6 +1948,7 @@ public void CopyTo(System.Memory destination) { } public override int GetHashCode() { throw null; } public static implicit operator System.ReadOnlyMemory (System.ArraySegment segment) { throw null; } public static implicit operator System.ReadOnlyMemory (T[] array) { throw null; } + public System.Buffers.MemoryHandle Pin() { throw null; } public System.Buffers.MemoryHandle Retain(bool pin = false) { throw null; } public System.ReadOnlyMemory Slice(int start) { throw null; } public System.ReadOnlyMemory Slice(int start, int length) { throw null; } diff --git a/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt b/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt new file mode 100644 index 000000000000..ba98be8651f3 --- /dev/null +++ b/src/System.Runtime/src/ApiCompatBaseline.uapaot.txt @@ -0,0 +1,4 @@ +Compat issues with assembly System.Runtime: +MembersMustExist : Member 'System.Memory.Pin()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.ReadOnlyMemory.Pin()' does not exist in the implementation but it does exist in the contract. +Total Issues: 2 diff --git a/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt b/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt index c89c017dc8bb..181752efced1 100644 --- a/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt +++ b/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt @@ -9,6 +9,10 @@ TypesMustExist : Type 'System.Security.Cryptography.ECCurve' does not exist in t TypesMustExist : Type 'System.Security.Cryptography.ECParameters' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'System.Security.Cryptography.ECPoint' does not exist in the implementation but it does exist in the contract. +Compat issues with assembly System.Runtime: +MembersMustExist : Member 'System.Memory.Pin()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'System.ReadOnlyMemory.Pin()' does not exist in the implementation but it does exist in the contract. + # Compat issues complaining about class vs delegate and class vs struct are because of a bug in APICompat tool where the implementation is picking # the wrong core assembly. It is picking System.Runtime instead of System.Private.CoreLib, there isn't any straight forward way to fix so baselining. TypeCannotChangeClassification : Type 'System.Action' is a 'class' in the implementation but is a 'delegate' in the contract.