Return pooled memory stream blocks when construction fails

This commit is contained in:
Adam Hathcock
2026-10-13 10:21:45 +01:00
parent b8f3a8695b
commit 46437bbbbc
2 changed files with 45 additions and 2 deletions

View File

@@ -54,7 +54,15 @@ public sealed class PooledMemoryStream : MemoryStream
_length = 0;
_capacity = capacity;
EnsureSegmentedAllocated(capacity);
try
{
EnsureSegmentedAllocated(capacity);
}
catch
{
ReturnPooledBuffers();
throw;
}
}
public override bool CanRead => _isOpen;
@@ -565,7 +573,17 @@ public sealed class PooledMemoryStream : MemoryStream
while (_blocks.Count < requiredBlocks)
{
_blocks.Add(_arrayPool.Rent(_blockSize));
var block = _arrayPool.Rent(_blockSize);
try
{
_blocks.Add(block);
}
catch
{
// A failed list expansion must not lose the rental either.
_arrayPool.Return(block);
throw;
}
}
while (_blocks.Count > requiredBlocks)

View File

@@ -8,6 +8,31 @@ namespace SharpCompress.Test.Streams;
public class PooledMemoryStreamTests
{
[Fact]
public void FailedConstructorReturnsPreviouslyRentedBlocks()
{
var pool = new FailingArrayPool();
Assert.Throws<IOException>(() => new PooledMemoryStream(16, 8, pool));
Assert.Equal(1, pool.ReturnCount);
}
private sealed class FailingArrayPool : ArrayPool<byte>
{
private int rentCount;
public int ReturnCount { get; private set; }
public override byte[] Rent(int minimumLength)
{
if (++rentCount == 2)
{
throw new IOException("Injected rental failure");
}
return new byte[minimumLength];
}
public override void Return(byte[] array, bool clearArray = false) => ReturnCount++;
}
[Fact]
public void GrowsUsingFixedSizeBlocks()
{