// 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; using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; namespace Microsoft.AspNetCore.Http.Tests { public class TestMemoryPool : MemoryPool { private MemoryPool _pool = Shared; private bool _disposed; public override IMemoryOwner Rent(int minBufferSize = -1) { CheckDisposed(); return new PooledMemory(_pool.Rent(minBufferSize), this); } protected override void Dispose(bool disposing) { _disposed = true; } public override int MaxBufferSize => 4096; internal void CheckDisposed() { if (_disposed) { throw new ObjectDisposedException(nameof(TestMemoryPool)); } } private class PooledMemory : MemoryManager { private IMemoryOwner _owner; private readonly TestMemoryPool _pool; private int _referenceCount; private bool _returned; private string _leaser; public PooledMemory(IMemoryOwner owner, TestMemoryPool pool) { _owner = owner; _pool = pool; _leaser = Environment.StackTrace; _referenceCount = 1; } ~PooledMemory() { Debug.Assert(_returned, "Block being garbage collected instead of returned to pool" + Environment.NewLine + _leaser); } protected override void Dispose(bool disposing) { _pool.CheckDisposed(); } public override MemoryHandle Pin(int elementIndex = 0) { _pool.CheckDisposed(); Interlocked.Increment(ref _referenceCount); if (!MemoryMarshal.TryGetArray(_owner.Memory, out ArraySegment segment)) { throw new InvalidOperationException(); } unsafe { try { if ((uint)elementIndex > (uint)segment.Count) { throw new ArgumentOutOfRangeException(nameof(elementIndex)); } GCHandle handle = GCHandle.Alloc(segment.Array, GCHandleType.Pinned); return new MemoryHandle(Unsafe.Add(((void*)handle.AddrOfPinnedObject()), elementIndex + segment.Offset), handle, this); } catch { Unpin(); throw; } } } public override void Unpin() { _pool.CheckDisposed(); int newRefCount = Interlocked.Decrement(ref _referenceCount); if (newRefCount < 0) throw new InvalidOperationException(); if (newRefCount == 0) { _returned = true; } } protected override bool TryGetArray(out ArraySegment segment) { _pool.CheckDisposed(); return MemoryMarshal.TryGetArray(_owner.Memory, out segment); } public override Memory Memory { get { _pool.CheckDisposed(); return _owner.Memory; } } public override Span GetSpan() { _pool.CheckDisposed(); return _owner.Memory.Span; } } } }