Skip to content

Commit

Permalink
feat (TaskCompletionSourceRCA.cs): introduce this new utility class w…
Browse files Browse the repository at this point in the history
…hich ensures that the TaskCreationOptions.RunContinuationsAsynchronously is turned on for all constructors - also employ it in all operations
  • Loading branch information
ksidirop-laerdal committed Dec 4, 2024
1 parent c94dd20 commit a1dc16c
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -44,8 +44,7 @@ public async Task SingleFileDownloadAsync_ShouldThrowAllDownloadAttemptsFailedEx
));

// Assert
await work.Should()
.ThrowWithinAsync<AllDownloadAttemptsFailedException>(3.Seconds());
await work.Should().ThrowWithinAsync<AllDownloadAttemptsFailedException>(3.Seconds());

mockedNativeFileDownloaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00
Expand Down
92 changes: 92 additions & 0 deletions Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceRCA.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Threading.Tasks;

namespace Laerdal.McuMgr.Common.Helpers
{
/// <summary>Like <see cref="TaskCompletionSource"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
internal sealed class TaskCompletionSourceRCA : TaskCompletionSource
{
/// <summary>Like <see cref="TaskCompletionSource"/> but with the <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA() : base(TaskCreationOptions.RunContinuationsAsynchronously)
{
}

/// <summary>Like <see cref="TaskCompletionSource"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA(object state) : base(state, TaskCreationOptions.RunContinuationsAsynchronously)
{
}

/// <summary>Like <see cref="TaskCompletionSource"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA(object state, TaskCreationOptions creationOptions) : base(state, creationOptions | TaskCreationOptions.RunContinuationsAsynchronously)
{
}

/// <summary>Like <see cref="TaskCompletionSource"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA(TaskCreationOptions creationOptions) : base(creationOptions | TaskCreationOptions.RunContinuationsAsynchronously)
{
}
}

/// <summary>Like <see cref="TaskCompletionSource{T}"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource{T}"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
internal sealed class TaskCompletionSourceRCA<T> : TaskCompletionSource<T>
{
/// <summary>Like <see cref="TaskCompletionSource{T}"/> but with the <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource{T}"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA() : base(TaskCreationOptions.RunContinuationsAsynchronously)
{
}

/// <summary>Like <see cref="TaskCompletionSource{T}"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource{T}"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA(object state) : base(state, TaskCreationOptions.RunContinuationsAsynchronously)
{
}

/// <summary>Like <see cref="TaskCompletionSource{T}"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource{T}"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA(object state, TaskCreationOptions creationOptions) : base(state, creationOptions | TaskCreationOptions.RunContinuationsAsynchronously)
{
}

/// <summary>Like <see cref="TaskCompletionSource{T}"/> but with <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> turned on by default in all constructors.</summary>
/// <remarks>
/// Inspired by <see href="https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/">Microsoft's recommendation on TCS</see> which strongly
/// advises using <see cref="TaskCreationOptions"/>.<see cref="TaskCreationOptions.RunContinuationsAsynchronously"/> with <see cref="TaskCompletionSource{T}"/> to avoid exotic deadlocks in certain corner-cases.
/// </remarks>
public TaskCompletionSourceRCA(TaskCreationOptions creationOptions) : base(creationOptions | TaskCreationOptions.RunContinuationsAsynchronously)
{
}
}
}
2 changes: 1 addition & 1 deletion Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public event EventHandler<StateChangedEventArgs> StateChanged

public async Task ResetAsync(int timeoutInMs = -1)
{
var taskCompletionSource = new TaskCompletionSource<bool>(state: false);
var taskCompletionSource = new TaskCompletionSourceRCA<bool>(state: false);

try
{
Expand Down
4 changes: 3 additions & 1 deletion Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public async Task<byte[]> DownloadAsync(
var didWarnOnceAboutUnstableConnection = false;
for (var triesCount = 1; !isCancellationRequested;)
{
var taskCompletionSource = new TaskCompletionSource<byte[]>(state: null);
var taskCompletionSource = new TaskCompletionSourceRCA<byte[]>(state: null);

try
{
Expand Down Expand Up @@ -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_;
Expand Down
2 changes: 1 addition & 1 deletion Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ public async Task UploadAsync<TData>(
var didWarnOnceAboutUnstableConnection = false;
for (var triesCount = 1; !isCancellationRequested;)
{
var taskCompletionSource = new TaskCompletionSource<bool>(state: false);
var taskCompletionSource = new TaskCompletionSourceRCA<bool>(state: false);
try
{
Cancelled += FileUploader_Cancelled_;
Expand Down
2 changes: 1 addition & 1 deletion Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public event EventHandler<FatalErrorOccurredEventArgs> FatalErrorOccurred

public async Task EraseAsync(int imageIndex = 1, int timeoutInMs = -1)
{
var taskCompletionSource = new TaskCompletionSource<bool>(state: false);
var taskCompletionSource = new TaskCompletionSourceRCA<bool>(state: false);

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public async Task InstallAsync(
var didWarnOnceAboutUnstableConnection = false;
for (var triesCount = 1; !isCancellationRequested;)
{
var taskCompletionSource = new TaskCompletionSource<bool>(state: false);
var taskCompletionSource = new TaskCompletionSourceRCA<bool>(state: false);
try
{
Cancelled += FirmwareInstaller_Cancelled_;
Expand Down

0 comments on commit a1dc16c

Please sign in to comment.