// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Threading; using System.Diagnostics; using System.Runtime.InteropServices; namespace System.Buffers { /// /// Block tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The /// individual blocks are then treated as independent array segments. /// internal sealed class DiagnosticPoolBlock : MemoryManager { /// /// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool. /// private readonly DiagnosticMemoryPool _pool; private readonly IMemoryOwner _memoryOwner; private MemoryHandle? _memoryHandle; private Memory _memory; private readonly object _syncObj = new object(); private bool _isDisposed; private int _pinCount; /// /// This object cannot be instantiated outside of the static Create method /// internal DiagnosticPoolBlock(DiagnosticMemoryPool pool, IMemoryOwner memoryOwner) { _pool = pool; _memoryOwner = memoryOwner; _memory = memoryOwner.Memory; } public override Memory Memory { get { try { lock (_syncObj) { if (_isDisposed) { MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); } if (_pool.IsDisposed) { MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); } return CreateMemory(_memory.Length); } } catch (Exception exception) { _pool.ReportException(exception); throw; } } } protected override void Dispose(bool disposing) { try { lock (_syncObj) { if (Volatile.Read(ref _pinCount) > 0) { MemoryPoolThrowHelper.ThrowInvalidOperationException_ReturningPinnedBlock(this); } if (_isDisposed) { MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockDoubleDispose(this); } _memoryOwner.Dispose(); _pool.Return(this); _isDisposed = true; } } catch (Exception exception) { _pool.ReportException(exception); throw; } } public override Span GetSpan() { try { lock (_syncObj) { if (_isDisposed) { MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); } if (_pool.IsDisposed) { MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); } return _memory.Span; } } catch (Exception exception) { _pool.ReportException(exception); throw; } } public override MemoryHandle Pin(int byteOffset = 0) { try { lock (_syncObj) { if (_isDisposed) { MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); } if (_pool.IsDisposed) { MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); } if (byteOffset < 0 || byteOffset > _memory.Length) { MemoryPoolThrowHelper.ThrowArgumentOutOfRangeException(_memory.Length, byteOffset); } _pinCount++; _memoryHandle = _memoryHandle ?? _memory.Pin(); unsafe { return new MemoryHandle(((IntPtr)_memoryHandle.Value.Pointer + byteOffset).ToPointer(), default, this); } } } catch (Exception exception) { _pool.ReportException(exception); throw; } } protected override bool TryGetArray(out ArraySegment segment) { try { lock (_syncObj) { if (_isDisposed) { MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); } if (_pool.IsDisposed) { MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); } return MemoryMarshal.TryGetArray(_memory, out segment); } } catch (Exception exception) { _pool.ReportException(exception); throw; } } public override void Unpin() { try { lock (_syncObj) { if (_pinCount == 0) { MemoryPoolThrowHelper.ThrowInvalidOperationException_PinCountZero(this); } _pinCount--; if (_pinCount == 0) { Debug.Assert(_memoryHandle.HasValue); _memoryHandle.Value.Dispose(); _memoryHandle = null; } } } catch (Exception exception) { _pool.ReportException(exception); throw; } } public StackTrace Leaser { get; set; } public void Track() { Leaser = new StackTrace(false); } } }