Skip to content

Commit

Permalink
feat: Extend TaskHelper with FireAndForget for task and action
Browse files Browse the repository at this point in the history
  • Loading branch information
davidkallesen committed Dec 30, 2023
1 parent 3bbd787 commit 2937036
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 0 deletions.
16 changes: 16 additions & 0 deletions docs/CodeDoc/Atc/Atc.Helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,22 @@ TaskHelper.
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`taskToRun`&nbsp;&nbsp;-&nbsp;&nbsp;The task to run.<br />
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`timeout`&nbsp;&nbsp;-&nbsp;&nbsp;The timeout.<br />
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`cancellationToken`&nbsp;&nbsp;-&nbsp;&nbsp;The cancellation token.<br />
#### FireAndForget
>```csharp
>void FireAndForget(Action action)
>```
><b>Summary:</b> Executes the provided action on a background thread, ignoring its completion status. This method is intended for fire-and-forget scenarios where the action is non-critical and does not need to be awaited or monitored.
>
><b>Parameters:</b><br>
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`action`&nbsp;&nbsp;-&nbsp;&nbsp;The action to execute asynchronously.<br />
#### FireAndForget
>```csharp
>void FireAndForget(Task task)
>```
><b>Summary:</b> Executes the provided action on a background thread, ignoring its completion status. This method is intended for fire-and-forget scenarios where the action is non-critical and does not need to be awaited or monitored.
>
><b>Parameters:</b><br>
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`action`&nbsp;&nbsp;-&nbsp;&nbsp;The action to execute asynchronously.<br />
#### RunSync
>```csharp
>void RunSync(Func<Task> func)
Expand Down
3 changes: 3 additions & 0 deletions docs/CodeDoc/Atc/IndexExtended.md
Original file line number Diff line number Diff line change
Expand Up @@ -4667,6 +4667,8 @@
- [TaskHelper](Atc.Helpers.md#taskhelper)
- Static Methods
- Execute(Func&lt;CancellationToken, Task&lt;TResult&gt;&gt; taskToRun, TimeSpan timeout, CancellationToken cancellationToken = null)
- FireAndForget(Action action)
- FireAndForget(Task task)
- RunSync(Func&lt;Task&gt; func)
- RunSync(Func&lt;Task&lt;TResult&gt;&gt; func)
- WhenAll(IEnumerable&lt;Task&gt; tasks)
Expand Down Expand Up @@ -5150,6 +5152,7 @@
- [SwitchCaseDefaultException](System.md#switchcasedefaultexception)
- [TaskExtensions](System.md#taskextensions)
- Static Methods
- Forget(this Task task)
- StartAndWaitAllThrottled(this IEnumerable&lt;Task&gt; tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = null)
- StartAndWaitAllThrottled(this IEnumerable&lt;Task&gt; tasksToRun, int maxTasksToRunInParallel, int timeoutInMilliseconds, CancellationToken cancellationToken = null)
- [TcpException](System.md#tcpexception)
Expand Down
8 changes: 8 additions & 0 deletions docs/CodeDoc/Atc/System.md
Original file line number Diff line number Diff line change
Expand Up @@ -2287,6 +2287,14 @@ Extensions for the `System.Threading.Tasks.Task` class.
### Static Methods
#### Forget
>```csharp
>void Forget(this Task task)
>```
><b>Summary:</b> Marks the provided task as 'forgotten', meaning its completion is intentionally unobserved. This method is used to explicitly denote that a task's result or exception is to be ignored. It should be used with caution, primarily in fire-and-forget scenarios where task exceptions are handled separately.
>
><b>Parameters:</b><br>
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`task`&nbsp;&nbsp;-&nbsp;&nbsp;The task to be forgotten.<br />
#### StartAndWaitAllThrottled
>```csharp
>void StartAndWaitAllThrottled(this IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = null)
Expand Down
35 changes: 35 additions & 0 deletions src/Atc/Extensions/TaskExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,39 @@ public static void StartAndWaitAllThrottled(this IEnumerable<Task> tasksToRun, i
// before some Tasks have had their "post" action completed, which references the throttler, resulting in an exception due to accessing a disposed object.
Task.WaitAll(postTaskTasks.ToArray(), cancellationToken);
}

/// <summary>
/// Marks the provided task as 'forgotten', meaning its completion is intentionally unobserved.
/// This method is used to explicitly denote that a task's result or exception is to be ignored.
/// It should be used with caution, primarily in fire-and-forget scenarios where task exceptions are handled separately.
/// </summary>
/// <param name="task">The task to be forgotten.</param>
/// <exception cref="ArgumentNullException">Thrown if the task is null.</exception>
public static void Forget(
this Task task)
{
if (task is null)
{
throw new ArgumentNullException(nameof(task));
}

if (!task.IsCompleted || task.IsFaulted)
{
_ = ForgetAwaited(task);
}
}

[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "OK.")]
private static async Task ForgetAwaited(
Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch
{
// Nothing to do here
}
}
}
37 changes: 37 additions & 0 deletions src/Atc/Helpers/TaskHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,41 @@ public static TResult RunSync<TResult>(Func<Task<TResult>> func)
.GetAwaiter()
.GetResult();
}

/// <summary>
/// Executes the provided action on a background thread, ignoring its completion status.
/// This method is intended for fire-and-forget scenarios where the action is non-critical and does not need to be awaited or monitored.
/// </summary>
/// <param name="action">The action to execute asynchronously.</param>
/// <exception cref="ArgumentNullException">Thrown if the action is null.</exception>
[SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
public static void FireAndForget(
Action action)
{
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}

Task.Run(action).Forget();
}

/// <summary>
/// Initiates the execution of a provided task and intentionally ignores its result or completion status.
/// This is a fire-and-forget utility method, used when the outcome of the task is not needed or is handled elsewhere.
/// It's primarily used for tasks where the result is not critical and does not need to be awaited or monitored.
/// </summary>
/// <param name="task">The task to execute and forget.</param>
/// <exception cref="ArgumentNullException">Thrown if the task is null.</exception>
[SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
public static void FireAndForget(
Task task)
{
if (task is null)
{
throw new ArgumentNullException(nameof(task));
}

task.Forget();
}
}
28 changes: 28 additions & 0 deletions test/Atc.Tests/Helpers/TaskHelperTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ReSharper disable RedundantAssignment
// ReSharper disable NotAccessedVariable
namespace Atc.Tests.Helpers;

[SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "OK.")]
Expand Down Expand Up @@ -142,6 +144,32 @@ public void RunSyncAndReturnResult()
Assert.Equal(42, actual);
}

[Fact]
[SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
[SuppressMessage("Major Code Smell", "S1854:Unused assignments should be removed", Justification = "OK.")]
[SuppressMessage("Blocker Code Smell", "S2699:Tests should include assertions", Justification = "OK.")]
public void FireAndForget_Action()
{
// Act
TaskHelper.FireAndForget(() =>
{
var x = 0;
x++;
});
}

[Fact]
[SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
[SuppressMessage("Blocker Code Smell", "S2699:Tests should include assertions", Justification = "OK.")]
public void FireAndForget_Task()
{
// Arrange
var doSomethingTask = DoSomethingAndReturnResultAsync();

// Act
TaskHelper.FireAndForget(doSomethingTask);
}

private static Task DoSomethingAsync()
{
return Task.Delay(100);
Expand Down

0 comments on commit 2937036

Please sign in to comment.