Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
18 changes: 0 additions & 18 deletions src/Common/src/CoreLib/System/Buffers/MemoryHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,6 @@ public unsafe struct MemoryHandle : IDisposable
/// </summary>
public bool HasPointer => _pointer != null;

/// <summary>
/// Adds an offset to the pinned pointer.
/// </summary>
/// <exception cref="System.ArgumentNullException">
/// Throw when pinned pointer is null.
/// </exception>
internal void AddOffset(int offset)
{
if (_pointer == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.pointer);
}
else
{
_pointer = (void*)((byte*)_pointer + offset);
}
}

/// <summary>
/// Frees the pinned handle and releases IRetainable.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Common/src/CoreLib/System/Buffers/OwnedMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public Memory<T> Memory
/// <summary>
/// Returns a handle for the array that has been pinned and hence its address can be taken
/// </summary>
public abstract MemoryHandle Pin();
public abstract MemoryHandle Pin(int offset = 0);

/// <summary>
/// Returns an array segment.
Expand Down
3 changes: 1 addition & 2 deletions src/Common/src/CoreLib/System/Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ public unsafe MemoryHandle Retain(bool pin = false)
{
if (_index < 0)
{
memoryHandle = ((OwnedMemory<T>)_object).Pin();
memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
}
else if (typeof(T) == typeof(char) && _object is string s)
{
Expand Down
3 changes: 1 addition & 2 deletions src/Common/src/CoreLib/System/ReadOnlyMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,7 @@ public unsafe MemoryHandle Retain(bool pin = false)
{
if (_index < 0)
{
memoryHandle = ((OwnedMemory<T>)_object).Pin();
memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
memoryHandle = ((OwnedMemory<T>)_object).Pin((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>());
}
else if (typeof(T) == typeof(char) && _object is string s)
{
Expand Down
7 changes: 6 additions & 1 deletion src/Common/tests/System/Buffers/NativeOwnedMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ protected override bool IsRetained

public override unsafe Span<byte> Span => new Span<byte>((void*)_ptr, _length);

public override unsafe MemoryHandle Pin() => new MemoryHandle(this, (void*)_ptr);
public override unsafe MemoryHandle Pin(int offset = 0)
Copy link
Member

Choose a reason for hiding this comment

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

What happens when offset is negative? Is it important to handle that case in more robust way?

Copy link
Member

Choose a reason for hiding this comment

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

Similar case - offset is more than the length of the block?

Copy link
Author

Choose a reason for hiding this comment

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

The NativeOwnedMemory is implementing OwnedMemory for testing purposes only so it isn't crucial for this implementation to validate offset.

It makes sense that for an array backed OwnedMemory, we can't support negative offset, but for NativeOwnedMemory, should a negative offset be allowed?

Copy link
Member

Choose a reason for hiding this comment

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

I do not see the difference between the two cases. Negative offset does not make sense to either, I think.

{
if (offset < 0 || offset > _length) throw new ArgumentOutOfRangeException(nameof(offset));
void* pointer = (void*)((byte*)_ptr + offset);
return new MemoryHandle(this, pointer);
}

public override bool Release()
{
Expand Down
2 changes: 1 addition & 1 deletion src/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ protected OwnedMemory() { }
public abstract System.Span<T> Span { get; }
public void Dispose() { }
protected abstract void Dispose(bool disposing);
public abstract System.Buffers.MemoryHandle Pin();
public abstract System.Buffers.MemoryHandle Pin(int offset=0);
public abstract bool Release();
public abstract void Retain();
protected internal abstract bool TryGetArray(out System.ArraySegment<T> arraySegment);
Expand Down
6 changes: 4 additions & 2 deletions src/System.Memory/tests/Memory/CustomMemoryForTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

Expand Down Expand Up @@ -38,13 +39,14 @@ public override Span<T> Span
}
}

public override MemoryHandle Pin()
public override MemoryHandle Pin(int offset = 0)
{
unsafe
{
Retain();
if (offset < 0 || offset > _array.Length) throw new ArgumentOutOfRangeException(nameof(offset));
var handle = GCHandle.Alloc(_array, GCHandleType.Pinned);
return new MemoryHandle(this, (void*)handle.AddrOfPinnedObject(), handle);
return new MemoryHandle(this, Unsafe.Add<byte>((void*)handle.AddrOfPinnedObject(), offset), handle);
}
}

Expand Down
30 changes: 30 additions & 0 deletions src/System.Memory/tests/Memory/OwnedMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,36 @@ public static void OwnedMemoryDispose()
owner.Dispose();
Assert.True(owner.IsDisposed);
}

[Fact]
public static void OwnedMemoryPinEmptyArray()
{
int[] a = {};
OwnedMemory<int> owner = new CustomMemoryForTest<int>(a);
MemoryHandle handle = owner.Pin();
Assert.True(handle.HasPointer);
}

[Fact]
public static void OwnedMemoryPinArray()
{
int[] array = { 1, 2, 3, 4, 5 };
OwnedMemory<int> owner = new CustomMemoryForTest<int>(array);
MemoryHandle handle = owner.Pin();
Assert.True(handle.HasPointer);
unsafe
{
int* pointer = (int*)handle.Pointer;

GC.Collect();

for (int i = 0; i < owner.Memory.Length; i++)
{
Assert.Equal(array[i], pointer[i]);
}
}
handle.Dispose();
}

[Fact]
public static void MemoryFromOwnedMemoryAfterDispose()
Expand Down
17 changes: 17 additions & 0 deletions src/System.Memory/tests/Memory/Retain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ public static void OwnedMemoryRetainWithPinning()
handle.Dispose();
}

[Fact]
public static void MemoryFromEmptyArrayRetainWithPinning()
{
Memory<int> memory = new int[0];
MemoryHandle handle = memory.Retain(pin: true);
Assert.True(handle.HasPointer);
unsafe
{
int* pointer = (int*)handle.Pointer;

GC.Collect();
Copy link
Member

Choose a reason for hiding this comment

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

This test is useless. GC.Collect will not affect unmanaged pointer stored in a local.

I have merged your changes into the mirror PR already. If you would like to clean this up, do it in a follow up PR.


Assert.True(pointer != null);
}
handle.Dispose();
}

[Fact]
public static void OwnedMemoryRetainWithPinningAndSlice()
{
Expand Down
17 changes: 17 additions & 0 deletions src/System.Memory/tests/ReadOnlyMemory/Retain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ public static void MemoryRetainWithPinning()
handle.Dispose();
}

[Fact]
public static void MemoryFromEmptyArrayRetainWithPinning()
{
ReadOnlyMemory<int> memory = new int[0];
MemoryHandle handle = memory.Retain(pin: true);
Assert.True(handle.HasPointer);
unsafe
{
int* pointer = (int*)handle.Pointer;

GC.Collect();

Assert.True(pointer != null);
}
handle.Dispose();
}

[Fact]
public static void MemoryRetainWithPinningAndSlice()
{
Expand Down
2 changes: 1 addition & 1 deletion src/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3728,7 +3728,7 @@ protected OwnedMemory() { }
public abstract System.Span<T> Span { get; }
public void Dispose() { }
protected abstract void Dispose(bool disposing);
public abstract System.Buffers.MemoryHandle Pin();
public abstract MemoryHandle Pin(int offset=0);
public abstract bool Release();
public abstract void Retain();
protected internal abstract bool TryGetArray(out System.ArraySegment<T> arraySegment);
Expand Down