mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Dispose 7Zip reader decoders without closing borrowed volumes
This commit is contained in:
@@ -207,9 +207,39 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_currentFolderStream?.Dispose();
|
||||
var stream = _currentFolderStream;
|
||||
_currentFolderStream = null;
|
||||
base.Dispose();
|
||||
_currentFolder = null;
|
||||
try
|
||||
{
|
||||
stream?.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
var stream = _currentFolderStream;
|
||||
_currentFolderStream = null;
|
||||
_currentFolder = null;
|
||||
try
|
||||
{
|
||||
if (stream is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream?.Dispose();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,11 @@ public abstract partial class AbstractReader<TEntry, TVolume>
|
||||
await _entriesForCurrentReadStreamAsync.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// If Volume implements IAsyncDisposable, use async disposal
|
||||
// Readers created by an archive borrow its volume, just as on the sync path.
|
||||
if (!_disposeVolume)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Volume is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
|
||||
|
||||
113
tests/SharpCompress.Test/Streams/SevenZipLifetimeTests.cs
Normal file
113
tests/SharpCompress.Test/Streams/SevenZipLifetimeTests.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Filters;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Readers;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Streams;
|
||||
|
||||
public class SevenZipLifetimeTests : TestBase
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("7Zip.LZMA.7z", false, false)]
|
||||
[InlineData("7Zip.LZMA.7z", false, true)]
|
||||
[InlineData("7Zip.LZMA.7z", true, false)]
|
||||
[InlineData("7Zip.LZMA.7z", true, true)]
|
||||
[InlineData("7Zip.LZMA2.7z", false, false)]
|
||||
[InlineData("7Zip.LZMA2.7z", false, true)]
|
||||
[InlineData("7Zip.LZMA2.7z", true, false)]
|
||||
[InlineData("7Zip.LZMA2.7z", true, true)]
|
||||
[InlineData("7Zip.solid.7z", false, false)]
|
||||
[InlineData("7Zip.solid.7z", false, true)]
|
||||
[InlineData("7Zip.solid.7z", true, false)]
|
||||
[InlineData("7Zip.solid.7z", true, true)]
|
||||
public async Task Reader_Dispose_Releases_Decoder_And_Preserves_Archive(
|
||||
string filename,
|
||||
bool asyncDispose,
|
||||
bool readAll
|
||||
)
|
||||
{
|
||||
using var input = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename));
|
||||
await using var archive = await SevenZipArchive.OpenAsyncArchive(
|
||||
input,
|
||||
new ReaderOptions { LeaveStreamOpen = false },
|
||||
CancellationToken.None
|
||||
);
|
||||
await using var reader = await archive.ExtractAllEntriesAsync();
|
||||
var sevenZipReader = Assert.IsType<SevenZipArchive.SevenZipReader>(reader);
|
||||
sevenZipReader.DiagnosticsEnabled = true;
|
||||
while (await reader.MoveToNextEntryAsync(CancellationToken.None))
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
#if LEGACY_DOTNET
|
||||
using var stream = await reader.OpenEntryStreamAsync(CancellationToken.None);
|
||||
#else
|
||||
await using var stream = await reader.OpenEntryStreamAsync(CancellationToken.None);
|
||||
#endif
|
||||
await stream.CopyToAsync(Stream.Null, Constants.BufferSize, CancellationToken.None);
|
||||
if (!readAll)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var folderStream = sevenZipReader.DiagnosticsCurrentFolderStream;
|
||||
Assert.NotNull(folderStream);
|
||||
while (folderStream is Filter)
|
||||
{
|
||||
folderStream = (Stream)GetField(folderStream, "_baseStream")!;
|
||||
}
|
||||
var decoder = Assert.IsType<LzmaStream>(folderStream);
|
||||
var window = GetField(decoder, "_outWindow")!;
|
||||
Assert.NotNull(GetField(window, "_buffer"));
|
||||
try
|
||||
{
|
||||
if (asyncDispose)
|
||||
{
|
||||
await reader.DisposeAsync();
|
||||
await reader.DisposeAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
sevenZipReader.Dispose();
|
||||
sevenZipReader.Dispose();
|
||||
}
|
||||
|
||||
Assert.Null(sevenZipReader.DiagnosticsCurrentFolderStream);
|
||||
Assert.Null(GetField(window, "_buffer"));
|
||||
Assert.True(input.CanRead);
|
||||
|
||||
// Disposing a borrowed reader must not prevent another extraction.
|
||||
var entry = await archive.EntriesAsync.Where(entry => !entry.IsDirectory).FirstAsync();
|
||||
#if LEGACY_DOTNET
|
||||
using var entryStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
||||
#else
|
||||
await using var entryStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
||||
#endif
|
||||
await entryStream.CopyToAsync(
|
||||
Stream.Null,
|
||||
Constants.BufferSize,
|
||||
CancellationToken.None
|
||||
);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await archive.DisposeAsync();
|
||||
}
|
||||
Assert.False(input.CanRead);
|
||||
}
|
||||
|
||||
private static object? GetField(object instance, string name) =>
|
||||
instance
|
||||
.GetType()
|
||||
.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.GetValue(instance);
|
||||
}
|
||||
Reference in New Issue
Block a user