Adding Memory.Pin() to eventually replace Memory.Retain(bool) (#17269)
authorAhson Khan <ahkha@microsoft.com>
Wed, 28 Mar 2018 00:05:33 +0000 (17:05 -0700)
committerGitHub <noreply@github.com>
Wed, 28 Mar 2018 00:05:33 +0000 (17:05 -0700)
* Adding Memory.Pin() to eventually replace Memory.Retain(bool)

* Fix copy/paste error and return default for when object is null.

* Fix XML comments.

src/mscorlib/shared/System/Memory.cs
src/mscorlib/shared/System/ReadOnlyMemory.cs

index 508ebcb3ee3b02c28871d336aea0681e1ae37511..bb2b1557a990f3d7533e68bdb3388951c874fc11 100644 (file)
@@ -270,9 +270,49 @@ namespace System
         public bool TryCopyTo(Memory<T> destination) => Span.TryCopyTo(destination.Span);
 
         /// <summary>
-        /// Returns a handle for the array.
-        /// <param name="pin">If pin is true, the GC will not move the array and hence its address can be taken</param>
+        /// Creates a handle for the memory.
+        /// The GC will not move the array until the returned <see cref="MemoryHandle"/>
+        /// is disposed, enabling taking and using the memory's address.
         /// </summary>
+        public unsafe MemoryHandle Pin()
+        {
+            if (_index < 0)
+            {
+                return ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
+            }
+            else if (typeof(T) == typeof(char) && _object is string s)
+            {
+                // This case can only happen if a ReadOnlyMemory<char> was created around a string
+                // and then that was cast to a Memory<char> using unsafe / marshaling code.  This needs
+                // to work, however, so that code that uses a single Memory<char> field to store either
+                // a readable ReadOnlyMemory<char> or a writable Memory<char> can still be pinned and
+                // used for interop purposes.
+                GCHandle handle = GCHandle.Alloc(s, GCHandleType.Pinned);
+#if FEATURE_PORTABLE_SPAN
+                void* pointer = Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
+                void* pointer = Unsafe.Add<T>(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<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
+                void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
+#endif // FEATURE_PORTABLE_SPAN
+                return new MemoryHandle(null, pointer, handle);
+            }
+            return default;
+        }
+
+        /// <summary>[Obsolete, use Pin()] Creates a handle for the memory.</summary>
+        /// <param name="pin">
+        /// If pin is true, the GC will not move the array until the returned <see cref="MemoryHandle"/>
+        /// is disposed, enabling taking and using the memory's address.
+        /// </param>
         public unsafe MemoryHandle Retain(bool pin = false)
         {
             MemoryHandle memoryHandle = default;
index 5f3f0e19805e5b68c0bad65a78113ac22dd103e0..dca7db3dfd1643e97561317c93a15d0b769fd486 100644 (file)
@@ -226,10 +226,44 @@ namespace System
         /// <param name="destination">The span to copy items into.</param>
         public bool TryCopyTo(Memory<T> destination) => Span.TryCopyTo(destination.Span);
 
-        /// <summary>Creates a handle for the memory.</summary>
+        /// <summary>
+        /// Creates a handle for the memory.
+        /// The GC will not move the array until the returned <see cref="MemoryHandle"/>
+        /// is disposed, enabling taking and using the memory's address.
+        /// </summary>
+        public unsafe MemoryHandle Pin()
+        {
+            if (_index < 0)
+            {
+                return ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
+            }
+            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<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
+                void* pointer = Unsafe.Add<T>(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<T>((void*)handle.AddrOfPinnedObject(), _index);
+#else
+                void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
+#endif // FEATURE_PORTABLE_SPAN
+                return new MemoryHandle(null, pointer, handle);
+            }
+            return default;
+        }
+
+        /// <summary>[Obsolete, use Pin()] Creates a handle for the memory.</summary>
         /// <param name="pin">
         /// If pin is true, the GC will not move the array until the returned <see cref="MemoryHandle"/>
-        /// is disposed, enabling the memory's address can be taken and used.
+        /// is disposed, enabling taking and using the memory's address.
         /// </param>
         public unsafe MemoryHandle Retain(bool pin = false)
         {