Highlights
A stop no longer leaves a NullReferenceException behind.
π Fixes
- A finished download could be reported as "Failed β Object reference not set to an instance of an object." Both dispatch loops start every chunk task eagerly and then await them one at a time, so a
CancelAsync()abandons every chunk the loop had not reached yet. Those chunks are still inside their read loops and still raise progress events, while the download has already run its terminal path β which closes the package storage and setsPackage.Storagetonull. Each progress event writes the auto-resume metadata through that storage, unguarded, so an abandoned chunk threw an NRE; when its cancellation token is no longer the one in force, that NRE becomes the download's error and reaches the consumer throughDownloadFileCompleted. The metadata write now reads the storage once and skips it when the download is over β resume metadata for a finished transfer is worthless anyway. - A
Dispose()racing a completion could swallow the completion event entirely.Clear()nulls the internalTaskCompletionSource, and an app that releases its engine as soon as a download ends can run that concurrently with the completion signal. Reporting that a download is over no longer depends on that field still being there. ActiveChunksno longer throws when the semaphore it counts has already been disposed byClear(); a torn-down download reports no active chunks instead.DownloadPackage.FlushAsync/CloseAsyncreadStorageonce instead of check-then-use, so a flush that races a close cannot dereferencenull.
π§ Under the hood
StopRaisesNoNullReferenceTestcovers the race deterministically (it raises 7 NREs against 5.9.7 and none against this release), and an opt-in soak (RetryAfterStopStressTest, enabled withNRE_STRESS_ITERATIONS) walks stop β dispose β retry across cancel point, chunk count, parallel on/off, server variant and throttled/instant.- Fixed a lost-update race in two integration tests that recorded peak progress with a non-atomic
Math.Max.
Found from a download manager whose retried-after-stop row was shown as failed even though the file was complete on disk (Downloader.Desktop).
Full Changelog: v5.9.7...v5.9.8