|
4 | 4 |
|
5 | 5 | using System.Buffers;
|
6 | 6 | using System.Diagnostics;
|
7 |
| -using System.Runtime.CompilerServices; |
8 | 7 | using System.Runtime.InteropServices;
|
9 | 8 | using System.Threading;
|
10 | 9 | using System.Threading.Tasks;
|
@@ -628,6 +627,53 @@ private void PurgeBuffers(bool disposing)
|
628 | 627 | }
|
629 | 628 | }
|
630 | 629 |
|
| 630 | + private async Task PurgeBuffersAsync() |
| 631 | + { |
| 632 | + // Same logic as PurgeBuffers, except with async counterparts. |
| 633 | + |
| 634 | + if (_stream == null) |
| 635 | + return; |
| 636 | + |
| 637 | + if (_mode != CompressionMode.Compress) |
| 638 | + return; |
| 639 | + |
| 640 | + // Some deflaters (e.g. ZLib) write more than zero bytes for zero byte inputs. |
| 641 | + // This round-trips and we should be ok with this, but our legacy managed deflater |
| 642 | + // always wrote zero output for zero input and upstack code (e.g. ZipArchiveEntry) |
| 643 | + // took dependencies on it. Thus, make sure to only "flush" when we actually had |
| 644 | + // some input: |
| 645 | + if (_wroteBytes) |
| 646 | + { |
| 647 | + // Compress any bytes left |
| 648 | + await WriteDeflaterOutputAsync(default).ConfigureAwait(false); |
| 649 | + |
| 650 | + // Pull out any bytes left inside deflater: |
| 651 | + bool finished; |
| 652 | + do |
| 653 | + { |
| 654 | + int compressedBytes; |
| 655 | + finished = _deflater.Finish(_buffer, out compressedBytes); |
| 656 | + |
| 657 | + if (compressedBytes > 0) |
| 658 | + await _stream.WriteAsync(new ReadOnlyMemory<byte>(_buffer, 0, compressedBytes)).ConfigureAwait(false); |
| 659 | + } while (!finished); |
| 660 | + } |
| 661 | + else |
| 662 | + { |
| 663 | + // In case of zero length buffer, we still need to clean up the native created stream before |
| 664 | + // the object get disposed because eventually ZLibNative.ReleaseHandle will get called during |
| 665 | + // the dispose operation and although it frees the stream but it return error code because the |
| 666 | + // stream state was still marked as in use. The symptoms of this problem will not be seen except |
| 667 | + // if running any diagnostic tools which check for disposing safe handle objects |
| 668 | + bool finished; |
| 669 | + do |
| 670 | + { |
| 671 | + int compressedBytes; |
| 672 | + finished = _deflater.Finish(_buffer, out compressedBytes); |
| 673 | + } while (!finished); |
| 674 | + } |
| 675 | + } |
| 676 | + |
631 | 677 | protected override void Dispose(bool disposing)
|
632 | 678 | {
|
633 | 679 | try
|
@@ -674,6 +720,58 @@ protected override void Dispose(bool disposing)
|
674 | 720 | }
|
675 | 721 | }
|
676 | 722 |
|
| 723 | + public override ValueTask DisposeAsync() |
| 724 | + { |
| 725 | + return GetType() == typeof(DeflateStream) ? |
| 726 | + DisposeAsyncCore() : |
| 727 | + base.DisposeAsync(); |
| 728 | + } |
| 729 | + |
| 730 | + private async ValueTask DisposeAsyncCore() |
| 731 | + { |
| 732 | + // Same logic as Dispose(true), except with async counterparts. |
| 733 | + try |
| 734 | + { |
| 735 | + await PurgeBuffersAsync().ConfigureAwait(false); |
| 736 | + } |
| 737 | + finally |
| 738 | + { |
| 739 | + // Close the underlying stream even if PurgeBuffers threw. |
| 740 | + // Stream.Close() may throw here (may or may not be due to the same error). |
| 741 | + // In this case, we still need to clean up internal resources, hence the inner finally blocks. |
| 742 | + Stream stream = _stream; |
| 743 | + _stream = null; |
| 744 | + try |
| 745 | + { |
| 746 | + if (!_leaveOpen && stream != null) |
| 747 | + await stream.DisposeAsync().ConfigureAwait(false); |
| 748 | + } |
| 749 | + finally |
| 750 | + { |
| 751 | + try |
| 752 | + { |
| 753 | + _deflater?.Dispose(); |
| 754 | + _inflater?.Dispose(); |
| 755 | + } |
| 756 | + finally |
| 757 | + { |
| 758 | + _deflater = null; |
| 759 | + _inflater = null; |
| 760 | + |
| 761 | + byte[] buffer = _buffer; |
| 762 | + if (buffer != null) |
| 763 | + { |
| 764 | + _buffer = null; |
| 765 | + if (!AsyncOperationIsActive) |
| 766 | + { |
| 767 | + ArrayPool<byte>.Shared.Return(buffer); |
| 768 | + } |
| 769 | + } |
| 770 | + } |
| 771 | + } |
| 772 | + } |
| 773 | + } |
| 774 | + |
677 | 775 | public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) =>
|
678 | 776 | TaskToApm.Begin(WriteAsync(array, offset, count, CancellationToken.None), asyncCallback, asyncState);
|
679 | 777 |
|
|
0 commit comments