1
0

Kleinere Änderungen, Dokumentation und Git-Dateien

This commit is contained in:
2021-10-24 16:51:17 +02:00
parent 9f9ee9469e
commit b74eb5b958
12 changed files with 187 additions and 170 deletions

View File

@@ -5,16 +5,20 @@ using System.Threading.Tasks;
namespace AMWD.Common.Utilities
{
// originally inspired by a code from Yves Goergen (unclassified.software).
/// <summary>
/// Implements an awaitable task that runs after a specified delay. The delay can be reset
/// before and after the task has run. By resetting the delay, the task can be executed multiple
/// times. The scheduled or executing or last executed task can be awaited, until the delay is
/// reset. After that, the next execution can be awaited.
/// Implements an awaitable task that runs after a specified delay. The delay can be reset.
/// By resetting the delay, the task can be executed multiple times.
/// </summary>
public class DelayedTask
{
#region Data
private Timer timer;
private bool nextRunPending;
/// <summary>
/// The synchronisation object.
/// </summary>
@@ -25,7 +29,15 @@ namespace AMWD.Common.Utilities
/// </summary>
protected Action<Exception> exceptionHandler;
private Timer timer;
/// <summary>
/// Provides the <see cref="Task"/> for the <see cref="GetAwaiter"/> method.
/// </summary>
protected TaskCompletionSourceWrapper tcs;
/// <summary>
/// Gets or sets the action to execute.
/// </summary>
protected Action Action { get; set; }
/// <summary>
/// Gets a value indicating whether the timer is running and an execution is scheduled. This
@@ -39,22 +51,6 @@ namespace AMWD.Common.Utilities
/// </summary>
public bool IsRunning { get; private set; }
/// <summary>
/// Indicates whether the action shall be executed again after the currently ongoing
/// execution has completed.
/// </summary>
private bool nextRunPending;
/// <summary>
/// Provides the <see cref="Task"/> for the <see cref="GetAwaiter"/> method.
/// </summary>
protected TaskCompletionSourceWrapper tcs;
/// <summary>
/// Gets or sets the action to execute.
/// </summary>
protected Action Action { get; set; }
/// <summary>
/// Gets or sets the delay to wait before executing the action.
/// </summary>
@@ -66,53 +62,41 @@ namespace AMWD.Common.Utilities
/// <summary>
/// Creates a new task instance that executes the specified action after the delay, but does
/// not start it yet. Multiple executions are allowed when calling <see cref="Reset"/> after
/// the executed was started.
/// not start it yet.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="delay">The delay.</param>
/// <returns></returns>
public static DelayedTask Create(Action action, TimeSpan delay)
{
return new DelayedTask { Action = action, Delay = delay };
}
=> new() { Action = action, Delay = delay };
/// <summary>
/// Creates a new task instance that executes the specified action after the delay, but does
/// not start it yet. Multiple executions are allowed when calling <see cref="Reset"/> after
/// the executed was started.
/// not start it yet.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="delay">The delay.</param>
/// <returns></returns>
public static DelayedTaskWithResult<TResult> Create<TResult>(Func<TResult> action, TimeSpan delay)
{
return DelayedTaskWithResult<TResult>.Create(action, delay);
}
=> DelayedTaskWithResult<TResult>.Create(action, delay);
/// <summary>
/// Executes the specified action after the delay. Multiple executions are allowed when
/// calling <see cref="Reset"/> after the executed was started.
/// Executes the specified action after the delay.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="delay">The delay.</param>
/// <returns></returns>
public static DelayedTask Run(Action action, TimeSpan delay)
{
return new DelayedTask { Action = action, Delay = delay }.Start();
}
=> new DelayedTask { Action = action, Delay = delay }.Start();
/// <summary>
/// Executes the specified action after the delay. Multiple executions are allowed when
/// calling <see cref="Reset"/> after the executed was started.
/// Executes the specified action after the delay.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="delay">The delay.</param>
/// <returns></returns>
public static DelayedTaskWithResult<TResult> Run<TResult>(Func<TResult> action, TimeSpan delay)
{
return DelayedTaskWithResult<TResult>.Run(action, delay);
}
=> DelayedTaskWithResult<TResult>.Run(action, delay);
#endregion Static methods
@@ -158,7 +142,7 @@ namespace AMWD.Common.Utilities
}
/// <summary>
/// Cancels the delay. Any pending executions are cleared. If the action was pending but not
/// Cancels the delay. Any pending execution is cleared. If the action was pending but not
/// yet executing, this task is cancelled. If the action was not pending or is already
/// executing, this task will be completed successfully after the action has completed.
/// </summary>
@@ -185,13 +169,7 @@ namespace AMWD.Common.Utilities
/// <summary>
/// Starts a pending execution immediately, not waiting for the timer to elapse.
/// </summary>
/// <returns>true, if an execution was started; otherwise, false.</returns>
/// <remarks>
/// A new execution is only started if one is currently waiting to run, or already running.
/// In the former case, the execution is scheduled immediately with the timer; in the latter
/// case, it is scheduled for when the currently running execution has completed. If an
/// execution has been started (the method returned true), it can be awaited normally.
/// </remarks>
/// <returns><c>true</c>, if an execution was started; otherwise, <c>false</c>.</returns>
public bool ExecutePending()
{
lock (syncObj)
@@ -287,9 +265,7 @@ namespace AMWD.Common.Utilities
/// </summary>
/// <returns></returns>
protected virtual TaskCompletionSourceWrapper CreateTcs()
{
return new TaskCompletionSourceWrapper<object>();
}
=> new TaskCompletionSourceWrapper<object>();
/// <summary>
/// Called when the timer has elapsed.
@@ -366,18 +342,19 @@ namespace AMWD.Common.Utilities
// Unblock waiters if not already waiting for the next execution.
// This task can be awaited again after the Reset method has been called.
if (exception != null)
{
localTcs?.TrySetException(exception);
}
else
{
SetLastResult(localTcs);
}
}
/// <summary>
/// Runs the action of the task.
/// </summary>
protected virtual void Run()
{
Action();
}
protected virtual void Run() => Action();
/// <summary>
/// Sets the <see cref="TaskCompletionSourceWrapper"/> result from the last action.
@@ -447,31 +424,13 @@ namespace AMWD.Common.Utilities
/// </summary>
/// <param name="result">The result value to bind to this <see cref="Task{TResult}"/>.</param>
/// <seealso cref="TaskCompletionSource{TResult}.TrySetResult(TResult)"/>
public void TrySetResult(TResult result)
{
tcs.TrySetResult(result);
}
public void TrySetResult(TResult result) => tcs.TrySetResult(result);
/// <summary>
/// Attempts to transition the underlying <see cref="Task{TResult}"/> into the
/// <see cref="TaskStatus.Faulted"/> state and binds it to a specified exception.
/// </summary>
/// <param name="exception">The exception to bind to this <see cref="Task{TResult}"/>.</param>
/// <seealso cref="TaskCompletionSource{TResult}.TrySetException(Exception)"/>
public override void TrySetException(Exception exception)
{
tcs.TrySetException(exception);
}
/// <inheritdoc/>
public override void TrySetException(Exception exception) => tcs.TrySetException(exception);
/// <summary>
/// Attempts to transition the underlying <see cref="Task{TResult}"/> into the
/// <see cref="TaskStatus.Canceled"/> state.
/// </summary>
/// <seealso cref="TaskCompletionSource{TResult}.TrySetCanceled()"/>
public override void TrySetCanceled()
{
tcs.TrySetCanceled();
}
/// <inheritdoc/>
public override void TrySetCanceled() => tcs.TrySetCanceled();
}
#endregion Internal TaskCompletionSourceWrapper classes
@@ -497,36 +456,22 @@ namespace AMWD.Common.Utilities
protected new Func<TResult> Action { get; set; }
internal static DelayedTaskWithResult<TResult> Create(Func<TResult> action, TimeSpan delay)
{
return new DelayedTaskWithResult<TResult> { Action = action, Delay = delay };
}
=> new() { Action = action, Delay = delay };
internal static DelayedTaskWithResult<TResult> Run(Func<TResult> action, TimeSpan delay)
{
return (DelayedTaskWithResult<TResult>)new DelayedTaskWithResult<TResult> { Action = action, Delay = delay }.Start();
}
=> (DelayedTaskWithResult<TResult>)new DelayedTaskWithResult<TResult> { Action = action, Delay = delay }.Start();
/// <summary>
/// Creates a <see cref="DelayedTask.TaskCompletionSourceWrapper"/> instance.
/// </summary>
/// <returns></returns>
/// <inheritdoc/>
protected override TaskCompletionSourceWrapper CreateTcs()
{
return new TaskCompletionSourceWrapper<TResult>();
}
=> new TaskCompletionSourceWrapper<TResult>();
/// <summary>
/// Runs the action of the task.
/// </summary>
/// <inheritdoc/>
protected override void Run()
{
lastResult = Action();
}
/// <summary>
/// Sets the <see cref="DelayedTask.TaskCompletionSourceWrapper"/> result from the last action.
/// </summary>
/// <param name="tcs">The <see cref="DelayedTask.TaskCompletionSourceWrapper"/> to set the result of.</param>
/// <inheritdoc/>
protected override void SetLastResult(TaskCompletionSourceWrapper tcs)
{
var myTcs = (TaskCompletionSourceWrapper<TResult>)tcs;
@@ -570,9 +515,7 @@ namespace AMWD.Common.Utilities
/// <param name="delayedTask">The <see cref="DelayedTaskWithResult{TResult}"/> instance to cast.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the current awaitable operation.</returns>
public static implicit operator Task<TResult>(DelayedTaskWithResult<TResult> delayedTask)
{
return delayedTask.Task;
}
=> delayedTask.Task;
/// <summary>
/// Adds an unhandled exception handler to this <see cref="DelayedTask"/> instance.