Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Commit

Permalink
Mirror changes from dotnet/corefx (dotnet/coreclr#15909)
Browse files Browse the repository at this point in the history
* Consolidate System.Memory code to shared folder (dotnet/corefx#26393)

* Consolidate System.Memory code to shared folder

This change is removing the duplicate codes from System.Memory and keep only one copy under the shared folder to be easier to edit such code in one place and get reflected on the other repos.

* Address the review feedback

* Addressing more feedback

* More cleanup

* remove empty line and added a comment

Signed-off-by: dotnet-bot-corefx-mirror <dotnet-bot@microsoft.com>

* Add missing throw helper methods used in the code we got from corefx

* Update the exception helper

* fix the break
Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
  • Loading branch information
dotnet-bot authored and jkotas committed Jan 19, 2018
1 parent eb2f375 commit 263a055
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 32 deletions.
11 changes: 11 additions & 0 deletions src/System.Private.CoreLib/shared/System/Buffers/IRetainable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,20 @@

namespace System.Buffers
{
/// <summary>
/// Provides a mechanism for manual lifetime management.
/// </summary>
public interface IRetainable
{
/// <summary>
/// Call this method to indicate that the IRetainable object is in use.
/// Do not dispose until Release is called.
/// </summary>
void Retain();
/// <summary>
/// Call this method to indicate that the IRetainable object is no longer in use.
/// The object can now be disposed.
/// </summary>
bool Release();
}
}
56 changes: 40 additions & 16 deletions src/System.Private.CoreLib/shared/System/Buffers/MemoryHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,46 @@

namespace System.Buffers
{
/// <summary>
/// A handle for the memory.
/// </summary>
public unsafe struct MemoryHandle : IDisposable
{
private IRetainable _owner;
private IRetainable _retainable;
private void* _pointer;
private GCHandle _handle;

/// <summary>
/// Creates a new memory handle for the memory.
/// </summary>
/// <param name="retainable">reference to manually managed object</param>
/// <param name="pointer">pointer to memory, or null if a pointer was not provided when the handle was created</param>
/// <param name="handle">handle used to pin array buffers</param>
[CLSCompliant(false)]
public MemoryHandle(IRetainable owner, void* pointer = null, GCHandle handle = default(GCHandle))
public MemoryHandle(IRetainable retainable, void* pointer = null, GCHandle handle = default(GCHandle))
{
_owner = owner;
_retainable = retainable;
_pointer = pointer;
_handle = handle;
}

/// <summary>
/// Returns the pointer to memory, or null if a pointer was not provided when the handle was created.
/// </summary>
[CLSCompliant(false)]
public void* Pointer => _pointer;

/// <summary>
/// Returns false if the pointer to memory is null.
/// </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)
Expand All @@ -33,26 +59,24 @@ internal void AddOffset(int offset)
}
}

[CLSCompliant(false)]
public void* Pointer => _pointer;

public bool HasPointer => _pointer != null;

public void Dispose()
{
if (_handle.IsAllocated)
/// <summary>
/// Frees the pinned handle and releases IRetainable.
/// </summary>
public void Dispose()
{
if (_handle.IsAllocated)
{
_handle.Free();
}

if (_owner != null)
if (_retainable != null)
{
_owner.Release();
_owner = null;
_retainable.Release();
_retainable = null;
}

_pointer = null;
_pointer = null;
}

}
}
52 changes: 47 additions & 5 deletions src/System.Private.CoreLib/shared/System/Buffers/OwnedMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,88 @@

namespace System.Buffers
{
/// <summary>
/// Owner of Memory<typeparamref name="T"/> that provides appropriate lifetime management mechanisms for it.
/// </summary>
public abstract class OwnedMemory<T> : IDisposable, IRetainable
{
/// <summary>
/// The number of items in the Memory<typeparamref name="T"/>.
/// </summary>
public abstract int Length { get; }

/// <summary>
/// Returns a span wrapping the underlying memory.
/// </summary>
public abstract Span<T> Span { get; }

/// <summary>
/// Returns a Memory<typeparamref name="T"/> if the underlying memory has not been freed.
/// </summary>
/// <exception cref="System.ObjectDisposedException">
/// Thrown when the underlying memory has already been disposed.
/// </exception>
public Memory<T> Memory
{
get
get
{
if (IsDisposed)
if (IsDisposed)
{
ThrowHelper.ThrowObjectDisposedException(nameof(OwnedMemory<T>), ExceptionResource.Memory_ThrowIfDisposed);
ThrowHelper.ThrowObjectDisposedException_MemoryDisposed();
}
return new Memory<T>(owner: this, 0, Length);
}
}

/// <summary>
/// Returns a handle for the array that has been pinned and hence its address can be taken
/// </summary>
public abstract MemoryHandle Pin();

/// <summary>
/// Returns an array segment.
/// </summary>
protected internal abstract bool TryGetArray(out ArraySegment<T> arraySegment);

/// <summary>
/// Implements IDisposable.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// Throw when there are still retained references to the memory
/// </exception>
public void Dispose()
{
if (IsRetained)
if (IsRetained)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Memory_OutstandingReferences);
ThrowHelper.ThrowInvalidOperationException_OutstandingReferences();
}
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Clean up of any leftover managed and unmanaged resources.
/// </summary>
protected abstract void Dispose(bool disposing);

/// <summary>
/// Return true if someone is holding a reference to the memory.
/// </summary>
protected abstract bool IsRetained { get; }

/// <summary>
/// Return true if the underlying memory has been freed.
/// </summary>
public abstract bool IsDisposed { get; }

/// <summary>
/// Implements IRetainable. Prevent accidental disposal of the memory.
/// </summary>
public abstract void Retain();

/// <summary>
/// Implements IRetainable. The memory can now be diposed.
/// </summary>
public abstract bool Release();

}
Expand Down
43 changes: 34 additions & 9 deletions src/System.Private.CoreLib/shared/System/Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@
using System.Runtime.InteropServices;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
#if !FEATURE_PORTABLE_SPAN
using Internal.Runtime.CompilerServices;
#endif // FEATURE_PORTABLE_SPAN

namespace System
{
/// <summary>
/// Memory represents a contiguous region of arbitrary memory similar to <see cref="Span{T}"/>.
/// Unlike <see cref="Span{T}"/>, it is not a byref-like type.
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
public readonly struct Memory<T>
Expand Down Expand Up @@ -77,16 +83,12 @@ public Memory(T[] array, int start, int length)
_index = start;
_length = length;
}

// Constructor for internal use only.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Memory(OwnedMemory<T> owner, int index, int length)
{
if (owner == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory);
if (index < 0 || length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException();

// No validation performed; caller must provide any necessary validation.
_object = owner;
_index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask
_length = length;
Expand All @@ -105,7 +107,7 @@ private Memory(object obj, int index, int length)
/// Defines an implicit conversion of an array to a <see cref="Memory{T}"/>
/// </summary>
public static implicit operator Memory<T>(T[] array) => new Memory<T>(array);

/// <summary>
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Memory{T}"/>
/// </summary>
Expand Down Expand Up @@ -168,7 +170,7 @@ public Memory<T> Slice(int start, int length)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}

return new Memory<T>(_object, _index + start, length);
}

Expand All @@ -191,7 +193,11 @@ public Span<T> Span
// and then cast to a Memory<T>. Such a cast can only be done with unsafe or marshaling code,
// in which case that's the dangerous operation performed by the dev, and we're just following
// suit here to make it work as best as possible.
#if FEATURE_PORTABLE_SPAN
return new Span<T>(Unsafe.As<Pinnable<T>>(s), MemoryExtensions.StringAdjustment, s.Length).Slice(_index, _length);
#else
return new Span<T>(ref Unsafe.As<char, T>(ref s.GetRawStringData()), s.Length).Slice(_index, _length);
#endif // FEATURE_PORTABLE_SPAN
}
else if (_object != null)
{
Expand Down Expand Up @@ -227,6 +233,10 @@ public Span<T> Span
/// <param name="destination">The span to copy items into.</param>
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>
/// </summary>
public unsafe MemoryHandle Retain(bool pin = false)
{
MemoryHandle memoryHandle = default;
Expand All @@ -245,13 +255,21 @@ public unsafe MemoryHandle Retain(bool pin = false)
// 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
memoryHandle = 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
memoryHandle = new MemoryHandle(null, pointer, handle);
}
}
Expand All @@ -267,7 +285,7 @@ public unsafe MemoryHandle Retain(bool pin = false)
}

/// <summary>
/// Get an array segment from the underlying memory.
/// Get an array segment from the underlying memory.
/// If unable to get the array segment, return false with a default array segment.
/// </summary>
public bool TryGetArray(out ArraySegment<T> arraySegment)
Expand Down Expand Up @@ -297,6 +315,10 @@ public bool TryGetArray(out ArraySegment<T> arraySegment)
/// </summary>
public T[] ToArray() => Span.ToArray();

/// <summary>
/// Determines whether the specified object is equal to the current object.
/// Returns true if the object is Memory or ReadOnlyMemory and if both objects point to the same array and have the same length.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj)
{
Expand Down Expand Up @@ -326,6 +348,9 @@ public bool Equals(Memory<T> other)
_length == other._length;
}

/// <summary>
/// Serves as the default hash function.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode()
{
Expand Down
4 changes: 4 additions & 0 deletions src/System.Private.CoreLib/shared/System/MemoryDebugView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ public T[] Items
return (T[])(object)text.Substring(start, length).ToCharArray();
}

#if FEATURE_PORTABLE_SPAN
return SpanHelpers.PerTypeValues<T>.EmptyArray;
#else
return Array.Empty<T>();
#endif // FEATURE_PORTABLE_SPAN
}
}
}
Expand Down
Loading

0 comments on commit 263a055

Please sign in to comment.