Add async/await support for all long-running DISM operations (fixes #82)#285
Conversation
|
Hi @jeffkl, I’m aware there’s a merge conflict with main. I’m away from my computer for a while — since maintainer edits are enabled, feel free to resolve it directly if you’d like to review sooner. Happy to fix it myself when I’m back if you’d prefer. Thanks for considering this! |
|
I'm going to push a bunch of changes, I want to enforce a coding style but realized I don't have enough in place to do so. Commits incoming... |
46ac78e to
41eafc9
Compare
|
@Rolling2405 there are other operations that don't use progress but can take some time like opening a session, adding/removing drivers, etc. Should we add Async methods for everything? Those one's just wouldn't have progress. I never use the API in a UI so I'm not sure what users would want. |
|
Yes, I think it makes sense to add async versions for those too even without progress. Any operation that can block the UI thread is worth making awaitable, so callers aren’t forced to wrap everything in Task.Run themselves. |
|
@jeffkl what do you think about this? |
|
@Rolling2405 sorry been slammed as of late and I'm out on vacation starting tomorrow. I've noticed that some of the new tests fail sporadically when I run all tests together but not when I run them individually, been trying to debug that. I'm completely fine with making all APIs that take some time but don't have progress be async. |
Addresses the sporadic test failures jeffkl identified while debugging PR jeffkl#285, where tests passed individually but failed when run together. CommitImageAsync tests were passing DISM_DISCARD_IMAGE flag (1) to the native DismCommitImage function, which fails with a DismException on the CI test WIM image. Changed to DISM_COMMIT_IMAGE flag (0), which is a safe no-op when no changes have been made. Fixes the remaining 2 of 114 test failures in the .NET 10 CI step: - CommitImageAsyncWithProgress - CommitImageAsyncHappyPath
|
@jeffkl When you get a chance, can you please take a look and see if things look good for merging? Thanks! |
…braryImport Added net10.0 to TargetFrameworks with AllowUnsafeBlocks for source-generated P/Invoke - Converted all 48 DllImport declarations to LibraryImport with #if NET7_0_OR_GREATER conditional compilation - Added [UnmanagedFunctionPointer] to native callback delegate - Moved ArtifactsPath to Directory.Build.props (NETSDK1199 fix for .NET 10) - All 4 targets build successfully: net40, netstandard2.0, net8.0, net10.0
- SA1116: Collapse multi-line DismProgress constructor to single line - SA1116: Move first param of Task.Factory.StartNew to its own line - SA1117: Split closing params of Task.Factory.StartNew onto separate lines Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolling2405 <89894749+Rolling2405@users.noreply.github.com>
… blocks Reindent lambda body and trailing parameters in all 12 async method files so that the opening brace, body, and remaining arguments are consistently indented at 16 spaces (one level deeper than the method call), satisfying SA1137 (same indentation) and SA1117 (each parameter on its own line). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolling2405 <89894749+Rolling2405@users.noreply.github.com>
the private sync helper methods in 4 files to satisfy SA1202
('public' members should come before 'private' members):
- DismApi.EnableFeature.cs
- DismApi.MountImage.cs
- DismApi.RemovePackage.cs
- DismApi.SetEdition.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Rolling2405 <89894749+Rolling2405@users.noreply.github.com>
…RS0016/RS0017 for net40 - Remove optional parameter defaults from MountImageAsync overloads to fix RS0026 - Add all 17 public async method signatures to PublicAPI.Unshipped.txt Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rolling2405 <89894749+Rolling2405@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Rolling2405/ManagedDism/sessions/d5267b48-44f8-4364-ac85-dab68ef56967 Co-authored-by: Rolling2405 <89894749+Rolling2405@users.noreply.github.com>
|
I'm back from vacation, I'm taking a look. Copilot didn't do a very good job on the implementation but I'm making progress... |
|
@Rolling2405 question for you. Not all DISM functions take progress, so I don't think they can be canceled. For example. DismAddDriver. If we add So should we add async calls to all possible methods after all? Or just to the ones that support progress and therefore can actually be canceled by DISM? |
|
@jeffkl In that case maybe we should just add it to the ones that support progress so the user can actually cancel when they want to |
49e1448 to
2c2c360
Compare
Closes #82
The DISM API is entirely synchronous, blocking the calling thread during long-running native operations. This causes UI freezes in WPF/WinForms/MAUI apps. As noted in #82, wrapping calls in
Task.Runalone does not solve the problem because the native progress callback fires on the calling thread.This PR adds
*Asynccounterparts for all 17 public methods that accept aDismProgressCallback, using theTaskCompletionSourcepattern suggested by @jeffkl in the issue thread.Implementation
TaskCompletionSource<bool>+Task.Factory.StartNewwithTaskCreationOptions.LongRunning— notTask.Run, since native DISM calls are truly long-running blocking operationsIProgress<DismProgress>instead ofDismProgressCallback(standard .NET async pattern)CancellationTokenwired toDismProgress.Cancel = true, which signals the nativeEventWaitHandle.CancellationToken.Noneis passed toTask.Factory.StartNewto ensure the delegate always runs — cancellation is handled inside the delegate to avoid hangingtcs.TaskUsage