Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V9.0.0/task based sceanario relocation #95

Merged
merged 12 commits into from
Oct 15, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
♻️ organic split that is true to vision
gimlichael committed Oct 14, 2024
commit f1799f82fa9f690db0066f79ad5680bb39da0975
68 changes: 68 additions & 0 deletions src/Cuemon.Core/Threading/TaskActionFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Cuemon.Threading
{
/// <summary>
/// Provides an easy way of invoking an <see cref="Action" /> delegate regardless of the amount of parameters provided.
/// </summary>
/// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update Summary to Reflect Delegate Type

The XML documentation mentions invoking an <see cref="Action" /> delegate, but the class actually works with a Func<TTuple, CancellationToken, Task>. To avoid confusion, consider updating the summary to accurately reflect the delegate type being invoked.

Apply this diff to correct the documentation:

-/// Provides an easy way of invoking an <see cref="Action" /> delegate regardless of the amount of parameters provided.
+/// Provides an easy way of invoking a delegate that returns a <see cref="Task" />, accommodating any number of parameters.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Provides an easy way of invoking an <see cref="Action" /> delegate regardless of the amount of parameters provided.
/// </summary>
/// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>
/// Provides an easy way of invoking a delegate that returns a <see cref="Task" />, accommodating any number of parameters.
/// </summary>
/// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>

public sealed class TaskActionFactory<TTuple> : TemplateFactory<TTuple> where TTuple : Template
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskActionFactory{TTuple}"/> class.
/// </summary>
/// <param name="method">The <see cref="Task"/> based function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
public TaskActionFactory(Func<TTuple, CancellationToken, Task> method, TTuple tuple) : this(method, tuple, method)
{
}

Check warning on line 20 in src/Cuemon.Core/Threading/TaskActionFactory.cs

Codecov / codecov/patch

src/Cuemon.Core/Threading/TaskActionFactory.cs#L18-L20

Added lines #L18 - L20 were not covered by tests

/// <summary>
/// Initializes a new instance of the <see cref="TaskActionFactory{TTuple}"/> class.
/// </summary>
/// <param name="method">The <see cref="Task"/> based function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
/// <param name="originalDelegate">The original delegate wrapped by <paramref name="method"/>.</param>
public TaskActionFactory(Func<TTuple, CancellationToken, Task> method, TTuple tuple, Delegate originalDelegate) : base(tuple, originalDelegate != null)
{
Method = method;
DelegateInfo = Decorator.RawEnclose(method).ResolveDelegateInfo(originalDelegate);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Declare the 'DelegateInfo' Property

You assign a value to DelegateInfo, but it isn't declared within this class. If DelegateInfo is intended to be a property of this class, it should be declared explicitly. If it's inherited, ensure that it's accessible and correctly utilized.

If needed, declare the property as follows (replace DelegateInfoType with the appropriate type):

+public DelegateInfoType DelegateInfo { get; private set; }

Committable suggestion was skipped due to low confidence.

}

/// <summary>
/// Gets the delegate to invoke.
/// </summary>
/// <value>The delegate to invoke.</value>
private Func<TTuple, CancellationToken, Task> Method { get; }

/// <summary>
/// Executes the delegate associated with this instance.
/// </summary>
/// <param name="ct">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">
/// No delegate was specified on the factory.
/// </exception>
/// <exception cref="OperationCanceledException">
/// The <paramref name="ct"/> was canceled.
/// </exception>
public Task ExecuteMethodAsync(CancellationToken ct)
{
ThrowIfNoValidDelegate(Condition.IsNull(Method));
ct.ThrowIfCancellationRequested();
return Method.Invoke(GenericArguments, ct);
}

/// <summary>
/// Creates a shallow copy of the current <see cref="TaskActionFactory{TTuple}"/> object.
/// </summary>
/// <returns>A new <see cref="TaskActionFactory{TTuple}"/> that is a copy of this instance.</returns>
/// <remarks>When thread safety is required this is the method to invoke.</remarks>
public override TemplateFactory<TTuple> Clone()
{
return new TaskActionFactory<TTuple>(Method, GenericArguments.Clone() as TTuple);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure Safe Casting in 'Clone' Method

In the Clone method, you perform a cast after cloning:

return new TaskActionFactory<TTuple>(Method, GenericArguments.Clone() as TTuple);

Using as TTuple can result in null if the cast fails, potentially causing a NullReferenceException. It's safer to use a direct cast if you're confident about the type or add null checking.

Consider the following adjustment:

-return new TaskActionFactory<TTuple>(Method, GenericArguments.Clone() as TTuple);
+return new TaskActionFactory<TTuple>(Method, (TTuple)GenericArguments.Clone());

Or, if there's a possibility of Clone() returning null:

var clonedArguments = GenericArguments.Clone() as TTuple;
if (clonedArguments == null)
{
    throw new InvalidOperationException("Cloning of GenericArguments failed.");
}
return new TaskActionFactory<TTuple>(Method, clonedArguments);

}

Check warning on line 66 in src/Cuemon.Core/Threading/TaskActionFactory.cs

Codecov / codecov/patch

src/Cuemon.Core/Threading/TaskActionFactory.cs#L64-L66

Added lines #L64 - L66 were not covered by tests
}
}
69 changes: 69 additions & 0 deletions src/Cuemon.Core/Threading/TaskFuncFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Cuemon.Threading
{
/// <summary>
/// Provides an easy way of invoking an <see cref="Func{TResult}" /> function delegate regardless of the amount of parameters provided.
/// </summary>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update XML documentation to reflect the correct delegate type

The summary currently mentions Func<TResult>, but the delegate used is of type Func<TTuple, CancellationToken, Task<TResult>>. Updating the summary enhances clarity and accuracy.

Apply this diff to correct the documentation:

-/// Provides an easy way of invoking an <see cref="Func{TResult}" /> function delegate regardless of the amount of parameters provided.
+/// Provides an easy way of invoking a <see cref="Func{TTuple, CancellationToken, Task{TResult}}"/> function delegate regardless of the amount of parameters provided.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Provides an easy way of invoking an <see cref="Func{TResult}" /> function delegate regardless of the amount of parameters provided.
/// </summary>
/// <summary>
/// Provides an easy way of invoking a <see cref="Func{TTuple, CancellationToken, Task{TResult}}"/> function delegate regardless of the amount of parameters provided.
/// </summary>

/// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>
/// <typeparam name="TResult">The type of the return value of the function delegate <see cref="Method"/>.</typeparam>
public sealed class TaskFuncFactory<TTuple, TResult> : TemplateFactory<TTuple> where TTuple : Template
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskFuncFactory{TTuple,TResult}"/> class.
/// </summary>
/// <param name="method">The function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
public TaskFuncFactory(Func<TTuple, CancellationToken, Task<TResult>> method, TTuple tuple) : this(method, tuple, method)
{
}

Check warning on line 21 in src/Cuemon.Core/Threading/TaskFuncFactory.cs

Codecov / codecov/patch

src/Cuemon.Core/Threading/TaskFuncFactory.cs#L19-L21

Added lines #L19 - L21 were not covered by tests

/// <summary>
/// Initializes a new instance of the <see cref="TaskFuncFactory{TTuple,TResult}"/> class.
/// </summary>
/// <param name="method">The function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
/// <param name="originalDelegate">The original delegate wrapped by <paramref name="method"/>.</param>
public TaskFuncFactory(Func<TTuple, CancellationToken, Task<TResult>> method, TTuple tuple, Delegate originalDelegate) : base(tuple, originalDelegate != null)
{
Method = method;
DelegateInfo = Decorator.RawEnclose(method).ResolveDelegateInfo(originalDelegate);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null checks for constructor parameters

To prevent potential ArgumentNullException, consider adding null checks for method, tuple, and originalDelegate.

Apply this diff to add null checks:

 public TaskFuncFactory(Func<TTuple, CancellationToken, Task<TResult>> method, TTuple tuple, Delegate originalDelegate) : base(tuple, originalDelegate != null)
 {
+    if (method == null) throw new ArgumentNullException(nameof(method));
+    if (tuple == null) throw new ArgumentNullException(nameof(tuple));
     Method = method;
     DelegateInfo = Decorator.RawEnclose(method).ResolveDelegateInfo(originalDelegate);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public TaskFuncFactory(Func<TTuple, CancellationToken, Task<TResult>> method, TTuple tuple, Delegate originalDelegate) : base(tuple, originalDelegate != null)
{
Method = method;
DelegateInfo = Decorator.RawEnclose(method).ResolveDelegateInfo(originalDelegate);
}
public TaskFuncFactory(Func<TTuple, CancellationToken, Task<TResult>> method, TTuple tuple, Delegate originalDelegate) : base(tuple, originalDelegate != null)
{
if (method == null) throw new ArgumentNullException(nameof(method));
if (tuple == null) throw new ArgumentNullException(nameof(tuple));
Method = method;
DelegateInfo = Decorator.RawEnclose(method).ResolveDelegateInfo(originalDelegate);
}


/// <summary>
/// Gets the function delegate to invoke.
/// </summary>
/// <value>The function delegate to invoke.</value>
private Func<TTuple, CancellationToken, Task<TResult>> Method { get; set; }

/// <summary>
/// Executes the function delegate associated with this instance.
/// </summary>
/// <param name="ct">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the return value of the function delegate associated with this instance.</returns>
/// <exception cref="InvalidOperationException">
/// No delegate was specified on the factory.
/// </exception>
/// <exception cref="OperationCanceledException">
/// The <paramref name="ct"/> was canceled.
/// </exception>
public Task<TResult> ExecuteMethodAsync(CancellationToken ct)
{
ThrowIfNoValidDelegate(Condition.IsNull(Method));
ct.ThrowIfCancellationRequested();
return Method.Invoke(GenericArguments, ct);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle exceptions from the invoked method

While invoking Method.Invoke, exceptions thrown by the delegate might propagate up the call stack. Consider wrapping the invocation in a try-catch block to handle potential exceptions and maintain robustness.

Apply this diff to handle exceptions:

 public Task<TResult> ExecuteMethodAsync(CancellationToken ct)
 {
     ThrowIfNoValidDelegate(Condition.IsNull(Method));
     ct.ThrowIfCancellationRequested();
-    return Method.Invoke(GenericArguments, ct);
+    try
+    {
+        return Method.Invoke(GenericArguments, ct);
+    }
+    catch (Exception ex)
+    {
+        // Handle or log the exception as appropriate
+        throw new OperationCanceledException("An error occurred during method execution.", ex, ct);
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ThrowIfNoValidDelegate(Condition.IsNull(Method));
ct.ThrowIfCancellationRequested();
return Method.Invoke(GenericArguments, ct);
}
public Task<TResult> ExecuteMethodAsync(CancellationToken ct)
{
ThrowIfNoValidDelegate(Condition.IsNull(Method));
ct.ThrowIfCancellationRequested();
try
{
return Method.Invoke(GenericArguments, ct);
}
catch (Exception ex)
{
// Handle or log the exception as appropriate
throw new OperationCanceledException("An error occurred during method execution.", ex, ct);
}
}


/// <summary>
/// Creates a shallow copy of the current <see cref="TaskFuncFactory{TTuple,TResult}"/> object.
/// </summary>
/// <returns>A new <see cref="TaskFuncFactory{TTuple,TResult}"/> that is a copy of this instance.</returns>
/// <remarks>When thread safety is required this is the method to invoke.</remarks>
public override TemplateFactory<TTuple> Clone()
{
return new TaskFuncFactory<TTuple, TResult>(Method, GenericArguments.Clone() as TTuple);
}

Check warning on line 67 in src/Cuemon.Core/Threading/TaskFuncFactory.cs

Codecov / codecov/patch

src/Cuemon.Core/Threading/TaskFuncFactory.cs#L65-L67

Added lines #L65 - L67 were not covered by tests
}
}
64 changes: 1 addition & 63 deletions src/Cuemon.Threading/TaskActionFactory.cs
Original file line number Diff line number Diff line change
@@ -409,66 +409,4 @@ public static TaskActionFactory<Template<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
return new TaskActionFactory<Template<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>>((tuple, token) => method(tuple.Arg1, tuple.Arg2, tuple.Arg3, tuple.Arg4, tuple.Arg5, tuple.Arg6, tuple.Arg7, tuple.Arg8, tuple.Arg9, tuple.Arg10, tuple.Arg11, tuple.Arg12, tuple.Arg13, tuple.Arg14, tuple.Arg15, token), Template.CreateFifteen(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15), method);
}
}

/// <summary>
/// Provides an easy way of invoking an <see cref="Action" /> delegate regardless of the amount of parameters provided.
/// </summary>
/// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>
public sealed class TaskActionFactory<TTuple> : TemplateFactory<TTuple> where TTuple : Template
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskActionFactory"/> class.
/// </summary>
/// <param name="method">The <see cref="Task"/> based function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
public TaskActionFactory(Func<TTuple, CancellationToken, Task> method, TTuple tuple) : this(method, tuple, method)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="TaskActionFactory{TTuple}"/> class.
/// </summary>
/// <param name="method">The <see cref="Task"/> based function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
/// <param name="originalDelegate">The original delegate wrapped by <paramref name="method"/>.</param>
internal TaskActionFactory(Func<TTuple, CancellationToken, Task> method, TTuple tuple, Delegate originalDelegate) : base(tuple, originalDelegate != null)
{
Method = method;
DelegateInfo = Decorator.RawEnclose(method).ResolveDelegateInfo(originalDelegate);
}

/// <summary>
/// Gets the delegate to invoke.
/// </summary>
/// <value>The delegate to invoke.</value>
private Func<TTuple, CancellationToken, Task> Method { get; }

/// <summary>
/// Executes the delegate associated with this instance.
/// </summary>
/// <param name="ct">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">
/// No delegate was specified on the factory.
/// </exception>
/// <exception cref="OperationCanceledException">
/// The <paramref name="ct"/> was canceled.
/// </exception>
public Task ExecuteMethodAsync(CancellationToken ct)
{
ThrowIfNoValidDelegate(Condition.IsNull(Method));
ct.ThrowIfCancellationRequested();
return Method.Invoke(GenericArguments, ct);
}

/// <summary>
/// Creates a shallow copy of the current <see cref="TaskActionFactory{TTuple}"/> object.
/// </summary>
/// <returns>A new <see cref="TaskActionFactory{TTuple}"/> that is a copy of this instance.</returns>
/// <remarks>When thread safety is required this is the method to invoke.</remarks>
public override TemplateFactory<TTuple> Clone()
{
return new TaskActionFactory<TTuple>(Method, GenericArguments.Clone() as TTuple);
}
}
}
}
67 changes: 2 additions & 65 deletions src/Cuemon.Threading/TaskFuncFactory.cs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ public static class TaskFuncFactory
/// <returns>An instance of <see cref="TaskFuncFactory{TTuple,TResult}"/> object initialized with the specified <paramref name="method"/>.</returns>
public static TaskFuncFactory<Template, TResult> Create<TResult>(Func<CancellationToken, Task<TResult>> method)
{
return new TaskFuncFactory<Template, TResult>((tuple, token) => method(token), Template.CreateZero(), method);
return new TaskFuncFactory<Template, TResult>((_, token) => method(token), Template.CreateZero(), method);
}

/// <summary>
@@ -425,67 +425,4 @@ public static TaskFuncFactory<Template<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
return new TaskFuncFactory<Template<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>, TResult>((tuple, token) => method(tuple.Arg1, tuple.Arg2, tuple.Arg3, tuple.Arg4, tuple.Arg5, tuple.Arg6, tuple.Arg7, tuple.Arg8, tuple.Arg9, tuple.Arg10, tuple.Arg11, tuple.Arg12, tuple.Arg13, tuple.Arg14, tuple.Arg15, token), Template.CreateFifteen(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15), method);
}
}

/// <summary>
/// Provides an easy way of invoking an <see cref="Func{TResult}" /> function delegate regardless of the amount of parameters provided.
/// </summary>
/// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>
/// <typeparam name="TResult">The type of the return value of the function delegate <see cref="Method"/>.</typeparam>
public sealed class TaskFuncFactory<TTuple, TResult> : TemplateFactory<TTuple> where TTuple : Template
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskFuncFactory{TTuple,TResult}"/> class.
/// </summary>
/// <param name="method">The function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
public TaskFuncFactory(Func<TTuple, CancellationToken, Task<TResult>> method, TTuple tuple) : this(method, tuple, method)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="TaskFuncFactory{TTuple,TResult}"/> class.
/// </summary>
/// <param name="method">The function delegate to invoke.</param>
/// <param name="tuple">The n-tuple argument of <paramref name="method"/>.</param>
/// <param name="originalDelegate">The original delegate wrapped by <paramref name="method"/>.</param>
internal TaskFuncFactory(Func<TTuple, CancellationToken, Task<TResult>> method, TTuple tuple, Delegate originalDelegate) : base(tuple, originalDelegate != null)
{
Method = method;
DelegateInfo = Decorator.RawEnclose(method).ResolveDelegateInfo(originalDelegate);
}

/// <summary>
/// Gets the function delegate to invoke.
/// </summary>
/// <value>The function delegate to invoke.</value>
private Func<TTuple, CancellationToken, Task<TResult>> Method { get; set; }

/// <summary>
/// Executes the function delegate associated with this instance.
/// </summary>
/// <param name="ct">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the return value of the function delegate associated with this instance.</returns>
/// <exception cref="InvalidOperationException">
/// No delegate was specified on the factory.
/// </exception>
/// <exception cref="OperationCanceledException">
/// The <paramref name="ct"/> was canceled.
/// </exception>
public Task<TResult> ExecuteMethodAsync(CancellationToken ct)
{
ThrowIfNoValidDelegate(Condition.IsNull(Method));
ct.ThrowIfCancellationRequested();
return Method.Invoke(GenericArguments, ct);
}

/// <summary>
/// Creates a shallow copy of the current <see cref="TaskFuncFactory{TTuple,TResult}"/> object.
/// </summary>
/// <returns>A new <see cref="TaskFuncFactory{TTuple,TResult}"/> that is a copy of this instance.</returns>
/// <remarks>When thread safety is required this is the method to invoke.</remarks>
public override TemplateFactory<TTuple> Clone()
{
return new TaskFuncFactory<TTuple, TResult>(Method, GenericArguments.Clone() as TTuple);
}
}
}
}
Loading