[PR #1101] [CLOSED] Fix LoadStreamForReadingAsync and add proper async code paths for RAR/ARJ multi-volume archives #1532

Closed
opened 2026-01-29 22:20:59 +00:00 by claunia · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/adamhathcock/sharpcompress/pull/1101
Author: @Copilot
Created: 1/3/2026
Status: Closed

Base: adam/rar-async-onlyHead: copilot/sub-pr-1096


📝 Commits (3)

  • 980bfc3 Initial plan
  • ae45f0b Fix LoadStreamForReadingAsync to properly initialize async enumerator and support multi-volume RAR archives
  • a35ddab Replace GetAwaiter().GetResult() with proper async code paths for file part enumeration

📊 Changes

6 files changed (+342 additions, -36 deletions)

View changed files

📝 src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs (+76 -24)
📝 src/SharpCompress/Readers/AbstractReader.cs (+55 -6)
📝 src/SharpCompress/Readers/Arj/ArjReader.cs (+5 -0)
📝 src/SharpCompress/Readers/Arj/MultiVolumeArjReader.cs (+96 -0)
📝 src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs (+104 -5)
📝 src/SharpCompress/Readers/Rar/RarReader.cs (+6 -1)

📄 Description

LoadStreamForReadingAsync had an incorrect null check that threw "Entries async enumerator is not initialized" on first call, preventing async RAR operations with AsyncOnlyStream.

Core fix:

  • Removed premature null check - initialize async enumerator before validating, mirroring sync LoadStreamForReading logic
  • Added AsyncEnumeratorWrapper<T> to bridge async enumerator to sync Entry property and NextEntryForCurrentStream() calls

Async enumeration support:

  • Added NextEntryForCurrentStreamAsync() for async-aware entry iteration
  • Updated MoveToNextEntryAsync() to use async enumeration path

Multi-volume archive handling:

  • Overrode NextEntryForCurrentStreamAsync() in MultiVolumeRarReader and MultiVolumeArjReader
  • Ensures AsyncOnlyStream compatibility by keeping all I/O async when file spans multiple volumes

Async file part enumeration (replacing GetAwaiter().GetResult()):

  • Added CreateFilePartAsyncEnumerableForCurrentEntry() method returning IAsyncEnumerable<FilePart>
  • Created MultiVolumeStreamAsyncEnumerable classes for RAR and ARJ multi-volume archives that properly await async operations
  • Added async constructor to MultiVolumeReadOnlyStream that accepts IAsyncEnumerable<RarFilePart>
  • Updated GetEntryStreamAsync() to use async enumerator instead of sync enumerator
  • Modified all Read/ReadAsync methods in MultiVolumeReadOnlyStream to properly await async enumerators
  • Removed all blocking GetAwaiter().GetResult() calls, ensuring true async execution throughout the async code path
// Before: Threw exception on first call
protected virtual async Task<bool> LoadStreamForReadingAsync(...)
{
    if (_entriesForCurrentReadStreamAsync is null)
        throw new InvalidOperationException("Entries async enumerator is not initialized.");
    // ...never reached
}

// After: Initialize then use
protected virtual async Task<bool> LoadStreamForReadingAsync(...)
{
    _entriesForCurrentReadStreamAsync = GetEntriesAsync(stream, cancellationToken)
        .GetAsyncEnumerator(cancellationToken);
    var result = await _entriesForCurrentReadStreamAsync.MoveNextAsync();
    _entriesForCurrentReadStream = new AsyncEnumeratorWrapper<TEntry>(_entriesForCurrentReadStreamAsync);
    return result;
}

The refactoring follows proper async/await patterns with separate code paths:

  • Sync path: IEnumerable<FilePart> → sync IEnumerator → sync MoveNext() → sync LoadStreamForReading()
  • Async path: IAsyncEnumerable<FilePart> → async IAsyncEnumerator → async MoveNextAsync() → async LoadStreamForReadingAsync()

Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/adamhathcock/sharpcompress/pull/1101 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 1/3/2026 **Status:** ❌ Closed **Base:** `adam/rar-async-only` ← **Head:** `copilot/sub-pr-1096` --- ### 📝 Commits (3) - [`980bfc3`](https://github.com/adamhathcock/sharpcompress/commit/980bfc3943a5e4ecab47ef4d437df8ae2bdbc4b9) Initial plan - [`ae45f0b`](https://github.com/adamhathcock/sharpcompress/commit/ae45f0b6504e6136e7fdc625297cffc1027b1208) Fix LoadStreamForReadingAsync to properly initialize async enumerator and support multi-volume RAR archives - [`a35ddab`](https://github.com/adamhathcock/sharpcompress/commit/a35ddab58392ac416b78a0c54a340ec9fbc8b61b) Replace GetAwaiter().GetResult() with proper async code paths for file part enumeration ### 📊 Changes **6 files changed** (+342 additions, -36 deletions) <details> <summary>View changed files</summary> 📝 `src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs` (+76 -24) 📝 `src/SharpCompress/Readers/AbstractReader.cs` (+55 -6) 📝 `src/SharpCompress/Readers/Arj/ArjReader.cs` (+5 -0) 📝 `src/SharpCompress/Readers/Arj/MultiVolumeArjReader.cs` (+96 -0) 📝 `src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs` (+104 -5) 📝 `src/SharpCompress/Readers/Rar/RarReader.cs` (+6 -1) </details> ### 📄 Description `LoadStreamForReadingAsync` had an incorrect null check that threw "Entries async enumerator is not initialized" on first call, preventing async RAR operations with `AsyncOnlyStream`. **Core fix:** - Removed premature null check - initialize async enumerator before validating, mirroring sync `LoadStreamForReading` logic - Added `AsyncEnumeratorWrapper<T>` to bridge async enumerator to sync `Entry` property and `NextEntryForCurrentStream()` calls **Async enumeration support:** - Added `NextEntryForCurrentStreamAsync()` for async-aware entry iteration - Updated `MoveToNextEntryAsync()` to use async enumeration path **Multi-volume archive handling:** - Overrode `NextEntryForCurrentStreamAsync()` in `MultiVolumeRarReader` and `MultiVolumeArjReader` - Ensures `AsyncOnlyStream` compatibility by keeping all I/O async when file spans multiple volumes **Async file part enumeration (replacing `GetAwaiter().GetResult()`):** - Added `CreateFilePartAsyncEnumerableForCurrentEntry()` method returning `IAsyncEnumerable<FilePart>` - Created `MultiVolumeStreamAsyncEnumerable` classes for RAR and ARJ multi-volume archives that properly await async operations - Added async constructor to `MultiVolumeReadOnlyStream` that accepts `IAsyncEnumerable<RarFilePart>` - Updated `GetEntryStreamAsync()` to use async enumerator instead of sync enumerator - Modified all `Read`/`ReadAsync` methods in `MultiVolumeReadOnlyStream` to properly await async enumerators - Removed all blocking `GetAwaiter().GetResult()` calls, ensuring true async execution throughout the async code path ```csharp // Before: Threw exception on first call protected virtual async Task<bool> LoadStreamForReadingAsync(...) { if (_entriesForCurrentReadStreamAsync is null) throw new InvalidOperationException("Entries async enumerator is not initialized."); // ...never reached } // After: Initialize then use protected virtual async Task<bool> LoadStreamForReadingAsync(...) { _entriesForCurrentReadStreamAsync = GetEntriesAsync(stream, cancellationToken) .GetAsyncEnumerator(cancellationToken); var result = await _entriesForCurrentReadStreamAsync.MoveNextAsync(); _entriesForCurrentReadStream = new AsyncEnumeratorWrapper<TEntry>(_entriesForCurrentReadStreamAsync); return result; } ``` The refactoring follows proper async/await patterns with separate code paths: - **Sync path**: `IEnumerable<FilePart>` → sync `IEnumerator` → sync `MoveNext()` → sync `LoadStreamForReading()` - **Async path**: `IAsyncEnumerable<FilePart>` → async `IAsyncEnumerator` → async `MoveNextAsync()` → async `LoadStreamForReadingAsync()` <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/adamhathcock/sharpcompress/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
claunia added the pull-request label 2026-01-29 22:20:59 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/sharpcompress#1532