From a1dc16ce1ad8a2f6b82eb4804aa78f7584090767 Mon Sep 17 00:00:00 2001 From: Kyriakos Sidiropoulos Date: Wed, 4 Dec 2024 14:33:46 +0100 Subject: [PATCH] feat (TaskCompletionSourceRCA.cs): introduce this new utility class which ensures that the TaskCreationOptions.RunContinuationsAsynchronously is turned on for all constructors - also employ it in all operations --- ...dException_GivenRogueNativeErrorMessage.cs | 7 +- .../Common/Helpers/TaskCompletionSourceRCA.cs | 92 +++++++++++++++++++ .../Shared/DeviceResetter/DeviceResetter.cs | 2 +- .../Shared/FileDownloader/FileDownloader.cs | 4 +- .../Shared/FileUploader/FileUploader.cs | 2 +- .../Shared/FirmwareEraser/FirmwareEraser.cs | 2 +- .../FirmwareInstaller/FirmwareInstaller.cs | 2 +- 7 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceRCA.cs diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs index a95ca49..2a2a85d 100644 --- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs +++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs @@ -26,8 +26,8 @@ public async Task SingleFileDownloadAsync_ShouldThrowAllDownloadAttemptsFailedEx var mockedNativeFileDownloaderProxy = new MockedErroneousNativeFileDownloaderProxySpy13( mockedFileData: mockedFileData, - downloaderCallbacksProxy: new GenericNativeFileDownloaderCallbacksProxy_(), - rogueNativeErrorMessage: nativeRogueErrorMessage + rogueNativeErrorMessage: nativeRogueErrorMessage, + downloaderCallbacksProxy: new GenericNativeFileDownloaderCallbacksProxy_() ); var fileDownloader = new McuMgr.FileDownloader.FileDownloader(mockedNativeFileDownloaderProxy); @@ -44,8 +44,7 @@ public async Task SingleFileDownloadAsync_ShouldThrowAllDownloadAttemptsFailedEx )); // Assert - await work.Should() - .ThrowWithinAsync(3.Seconds()); + await work.Should().ThrowWithinAsync(3.Seconds()); mockedNativeFileDownloaderProxy.CancelCalled.Should().BeFalse(); mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00 diff --git a/Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceRCA.cs b/Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceRCA.cs new file mode 100644 index 0000000..d0d3ba6 --- /dev/null +++ b/Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceRCA.cs @@ -0,0 +1,92 @@ +using System.Threading.Tasks; + +namespace Laerdal.McuMgr.Common.Helpers +{ + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + internal sealed class TaskCompletionSourceRCA : TaskCompletionSource + { + /// Like but with the . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA() : base(TaskCreationOptions.RunContinuationsAsynchronously) + { + } + + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA(object state) : base(state, TaskCreationOptions.RunContinuationsAsynchronously) + { + } + + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA(object state, TaskCreationOptions creationOptions) : base(state, creationOptions | TaskCreationOptions.RunContinuationsAsynchronously) + { + } + + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA(TaskCreationOptions creationOptions) : base(creationOptions | TaskCreationOptions.RunContinuationsAsynchronously) + { + } + } + + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + internal sealed class TaskCompletionSourceRCA : TaskCompletionSource + { + /// Like but with the . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA() : base(TaskCreationOptions.RunContinuationsAsynchronously) + { + } + + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA(object state) : base(state, TaskCreationOptions.RunContinuationsAsynchronously) + { + } + + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA(object state, TaskCreationOptions creationOptions) : base(state, creationOptions | TaskCreationOptions.RunContinuationsAsynchronously) + { + } + + /// Like but with . turned on by default in all constructors. + /// + /// Inspired by Microsoft's recommendation on TCS which strongly + /// advises using . with to avoid exotic deadlocks in certain corner-cases. + /// + public TaskCompletionSourceRCA(TaskCreationOptions creationOptions) : base(creationOptions | TaskCreationOptions.RunContinuationsAsynchronously) + { + } + } +} diff --git a/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs b/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs index b23a155..97d77a6 100644 --- a/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs +++ b/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs @@ -98,7 +98,7 @@ public event EventHandler StateChanged public async Task ResetAsync(int timeoutInMs = -1) { - var taskCompletionSource = new TaskCompletionSource(state: false); + var taskCompletionSource = new TaskCompletionSourceRCA(state: false); try { diff --git a/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs b/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs index 811cacf..43b8117 100644 --- a/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs +++ b/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs @@ -240,7 +240,7 @@ public async Task DownloadAsync( var didWarnOnceAboutUnstableConnection = false; for (var triesCount = 1; !isCancellationRequested;) { - var taskCompletionSource = new TaskCompletionSource(state: null); + var taskCompletionSource = new TaskCompletionSourceRCA(state: null); try { @@ -344,6 +344,8 @@ ex is not ArgumentException //10 wops probably missing native lib symbols! } finally { + taskCompletionSource.TrySetCanceled(); //it is best to ensure that the task is fossilized in case of rogue exceptions + Cancelled -= FileDownloader_Cancelled_; StateChanged -= FileDownloader_StateChanged_; DownloadCompleted -= FileDownloader_DownloadCompleted_; diff --git a/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs b/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs index 8d7f2d7..2425092 100644 --- a/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs +++ b/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs @@ -299,7 +299,7 @@ public async Task UploadAsync( var didWarnOnceAboutUnstableConnection = false; for (var triesCount = 1; !isCancellationRequested;) { - var taskCompletionSource = new TaskCompletionSource(state: false); + var taskCompletionSource = new TaskCompletionSourceRCA(state: false); try { Cancelled += FileUploader_Cancelled_; diff --git a/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs b/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs index ff14369..4e66ef5 100644 --- a/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs +++ b/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs @@ -114,7 +114,7 @@ public event EventHandler FatalErrorOccurred public async Task EraseAsync(int imageIndex = 1, int timeoutInMs = -1) { - var taskCompletionSource = new TaskCompletionSource(state: false); + var taskCompletionSource = new TaskCompletionSourceRCA(state: false); try { diff --git a/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs b/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs index 336caf3..f76bea1 100644 --- a/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs +++ b/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs @@ -213,7 +213,7 @@ public async Task InstallAsync( var didWarnOnceAboutUnstableConnection = false; for (var triesCount = 1; !isCancellationRequested;) { - var taskCompletionSource = new TaskCompletionSource(state: false); + var taskCompletionSource = new TaskCompletionSourceRCA(state: false); try { Cancelled += FirmwareInstaller_Cancelled_;