/// <summary> /// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of /// the /// block tracking objects, and adds them all to the pool. /// </summary> private MemoryPoolBlock AllocateSlab() { #pragma warning disable CA2000 // Dispose objects before losing scope var slab = MemoryPoolSlab.Create(_slabLength); #pragma warning restore CA2000 // Dispose objects before losing scope _slabs.Push(slab); var basePtr = slab.NativePointer; // Page align the blocks var offset = (int)((((ulong)basePtr + _blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr); var blockCount = (_slabLength - offset) / _blockSize; Interlocked.Add(ref _totalAllocatedBlocks, blockCount); MemoryPoolBlock block = null; for (var i = 0; i < blockCount; i++) { block = new MemoryPoolBlock(this, slab, offset, _blockSize); if (i != blockCount - 1) // last block { Return(block); } offset += _blockSize; } return(block); }
/// <summary> /// Called to return a block to the pool. Once Return has been called the memory no longer belongs to the caller, and /// Very Bad Things will happen if the memory is read of modified subsequently. If a caller fails to call Return and the /// block tracking object is garbage collected, the block tracking object's finalizer will automatically re-create and /// return /// a new tracking object into the pool. This will only happen if there is a bug in the server, however it is necessary to /// avoid /// leaving "dead zones" in the slab due to lost block tracking objects. /// </summary> /// <param name="block">The block to return. It must have been acquired by calling Lease on the same memory pool instance.</param> internal void Return(MemoryPoolBlock block) { if (!_isDisposed) { _blocks.Enqueue(block); } else { GC.SuppressFinalize(block); } }
internal void Return(MemoryPoolBlock block) { #if BLOCK_LEASE_TRACKING Debug.Assert(block.Pool == this, "Returned block was not leased from this pool"); Debug.Assert(block.IsLeased, $"Block being returned to pool twice: {block.Leaser}{Environment.NewLine}"); block.IsLeased = false; #endif if (!_isDisposed) { _blocks.Enqueue(block); } }
/// <summary> /// Called to return a block to the pool. Once Return has been called the memory no longer belongs to the caller, and /// Very Bad Things will happen if the memory is read of modified subsequently. If a caller fails to call Return and the /// block tracking object is garbage collected, the block tracking object's finalizer will automatically re-create and return /// a new tracking object into the pool. This will only happen if there is a bug in the server, however it is necessary to avoid /// leaving "dead zones" in the slab due to lost block tracking objects. /// </summary> /// <param name="block">The block to return. It must have been acquired by calling Lease on the same memory pool instance.</param> public void Return(MemoryPoolBlock block) { #if BLOCK_LEASE_TRACKING Debug.Assert(block.Pool == this, "Returned block was not leased from this pool"); Debug.Assert(block.IsLeased, $"Block being returned to pool twice: {block.Leaser}{Environment.NewLine}"); block.IsLeased = false; #endif if (block.Slab != null && block.Slab.IsActive) { _blocks.Enqueue(block); } else { GC.SuppressFinalize(block); } }
/// <summary> /// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the /// block tracking objects, and adds them all to the pool. /// </summary> private MemoryPoolBlock AllocateSlab() { var slab = MemoryPoolSlab.Create(_slabLength); _slabs.Push(slab); var basePtr = slab.NativePointer; // Page align the blocks var firstOffset = (int)((((ulong)basePtr + (uint)_blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr); // Ensure page aligned Debug.Assert((((ulong)basePtr + (uint)firstOffset) & (uint)(_blockSize - 1)) == 0); var blockAllocationLength = ((_slabLength - firstOffset) & ~(_blockSize - 1)); var offset = firstOffset; for (; offset + _blockSize < blockAllocationLength; offset += _blockSize) { var block = MemoryPoolBlock.Create( offset, _blockSize, this, slab); #if BLOCK_LEASE_TRACKING block.IsLeased = true; #endif Return(block); } Debug.Assert(offset + _blockSize - firstOffset == blockAllocationLength); // return last block rather than adding to pool var newBlock = MemoryPoolBlock.Create( offset, _blockSize, this, slab); return(newBlock); }
/// <summary> /// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the /// block tracking objects, and adds them all to the pool. /// </summary> private MemoryPoolBlock AllocateSlab() { var slab = MemoryPoolSlab.Create(_slabLength); _slabs.Push(slab); _slabAllocationCallback?.Invoke(slab); slab._deallocationCallback = _slabDeallocationCallback; var basePtr = slab.NativePointer; var firstOffset = (int)((_blockStride - 1) - ((ulong)(basePtr + _blockStride - 1) % _blockStride)); var poolAllocationLength = _slabLength - _blockStride; var offset = firstOffset; for (; offset + _blockLength < poolAllocationLength; offset += _blockStride) { var block = MemoryPoolBlock.Create( offset, _blockLength, this, slab); #if BLOCK_LEASE_TRACKING block.IsLeased = true; #endif Return(block); } // return last block rather than adding to pool var newBlock = MemoryPoolBlock.Create( offset, _blockLength, this, slab); return(newBlock); }
/// <summary> /// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the /// block tracking objects, and adds them all to the pool. /// </summary> private MemoryPoolBlock AllocateSlab() { var slab = MemoryPoolSlab.Create(_slabLength); _slabs.Push(slab); // Get the address for alignment IntPtr basePtr = Marshal.UnsafeAddrOfPinnedArrayElement(slab.PinnedArray, 0); // Page align the blocks var offset = (int)((((ulong)basePtr + (uint)_blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr); // Ensure page aligned Debug.Assert(((ulong)basePtr + (uint)offset) % _blockSize == 0); var blockCount = (_slabLength - offset) / _blockSize; Interlocked.Add(ref _totalAllocatedBlocks, blockCount); MemoryPoolBlock block = null; for (int i = 0; i < blockCount; i++) { block = new MemoryPoolBlock(this, slab, offset, _blockSize); if (i != blockCount - 1) // last block { #if BLOCK_LEASE_TRACKING block.IsLeased = true; #endif Return(block); } offset += _blockSize; } return(block); }