Files
dungeons/Assets/UltimateXR/Runtime/Scripts/Extensions/System/Threading/ActionExt.cs
2024-08-06 21:58:35 +02:00

137 lines
6.1 KiB
C#

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ActionExt.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Threading;
using System.Threading.Tasks;
using UltimateXR.Core.Threading.TaskControllers;
using UnityEngine;
namespace UltimateXR.Extensions.System.Threading
{
/// <summary>
/// <see cref="Action" /> extensions.
/// </summary>
public static class ActionExt
{
#region Public Methods
/// <summary>
/// Executes repeatedly this <see cref="Action" />, in the main thread, at <paramref name="rate" /> until cancellation
/// is requested with <paramref name="ct" />.
/// </summary>
/// <param name="self"><see cref="Action" /> to loop at <paramref name="rate" /> Hz</param>
/// <param name="rate">Loop frequency in Hz</param>
/// <param name="ct">Cancellation token</param>
/// <seealso cref="LoopThreaded" />
/// <seealso cref="ToLoop" />
public static async void Loop(this Action self, float rate = 10f, CancellationToken ct = default)
{
if (ct.IsCancellationRequested)
{
return;
}
int deltaTimeMs = Mathf.RoundToInt(1000f / rate);
while (!ct.IsCancellationRequested)
{
// Start delay timer parallel to action execution
Task delayTask = TaskExt.Delay(deltaTimeMs, ct);
self();
await delayTask;
}
}
/// <summary>
/// Executes repeatedly this <see cref="Action" />, in a separated thread, at <paramref name="rate" /> Hz until
/// cancellation is requested using <paramref name="ct" />.
/// </summary>
/// <param name="self"><see cref="Action" /> to loop at <paramref name="rate" /> Hz</param>
/// <param name="rate">Loop frequency in Hz</param>
/// <param name="ct">Cancellation token</param>
public static async void LoopThreaded(this Action self, float rate = 10f, CancellationToken ct = default)
{
if (ct.IsCancellationRequested)
{
return;
}
int deltaTimeMs = Mathf.RoundToInt(1000f / rate);
while (!ct.IsCancellationRequested)
{
// We don't want to abort current thread (Task.Run) with ct
// Instead, we wait for action to end, breaking the loop after that.
Task delayTask = TaskExt.Delay(deltaTimeMs, ct);
Task runTask = Task.Run(self, CancellationToken.None);
await Task.WhenAll(delayTask, runTask);
}
}
/// <summary>
/// Creates a <see cref="UxrLoopController" /> which wraps a cancellable loop executing this <see cref="Action" /> in
/// the main thread.
/// </summary>
/// <param name="self"><see cref="Action" /> to loop at <paramref name="rate" /> Hz</param>
/// <param name="rate">Loop frequency in Hz</param>
/// <param name="autoStartDelay">
/// Delay in milliseconds before loop executes its first iteration.
/// <list type="bullet">
/// <item>
/// Equal or greater than zero: tells <see cref="UxrLoopController" /> to automatically start looping
/// <paramref name="autoStartDelay" /> milliseconds after creation.
/// </item>
/// <item>
/// Negative (default) <see cref="UxrLoopController.Start()" /> needs to be called on returned
/// <see cref="UxrLoopController" /> to start looping.
/// </item>
/// </list>
/// </param>
/// <returns>
/// A <see cref="UxrLoopController" /> to handle (<see cref="UxrLoopController.Start()" />,
/// <see cref="UxrLoopController.Stop" />) the loop execution.
/// </returns>
/// <seealso cref="UxrLoopController" />
/// <seealso cref="Loop" />
/// <seealso cref="ToThreadedLoop" />
public static UxrLoopController ToLoop(this Action self, float rate = 10f, int autoStartDelay = -1)
{
return new UxrLoopController(ct => Loop(self, rate, ct), autoStartDelay);
}
/// <summary>
/// Creates a <see cref="UxrLoopController" /> which wraps a cancellable loop executing this <see cref="Action" /> in a
/// separate thread.
/// </summary>
/// <param name="self"><see cref="Action" /> to loop, in a separate thread, at <paramref name="rate" /> Hz</param>
/// <param name="rate">Loop frequency in Hz</param>
/// <param name="autoStartDelay">
/// Delay in milliseconds before loop executes its first iteration.
/// <list type="bullet">
/// <item>
/// Equal or greater than zero: tells <see cref="UxrLoopController" /> to automatically start looping
/// <paramref name="autoStartDelay" /> milliseconds after creation.
/// </item>
/// <item>
/// Negative (default) <see cref="UxrLoopController.Start()" /> needs to be called on returned
/// <see cref="UxrLoopController" /> to start looping.
/// </item>
/// </list>
/// </param>
/// <returns>
/// A <see cref="UxrLoopController" /> to handle (<see cref="UxrLoopController.Start()" />,
/// <see cref="UxrLoopController.Stop" />) the loop execution.
/// </returns>
/// <seealso cref="UxrLoopController" />
/// <seealso cref="Loop" />
public static UxrLoopController ToThreadedLoop(this Action self, float rate = 10f, int autoStartDelay = -1)
{
return new UxrLoopController(ct => LoopThreaded(self, rate, ct), autoStartDelay);
}
#endregion
}
}