// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Threading; using System.Threading.Tasks; using UltimateXR.Core.Threading.TaskControllers; using UnityEngine; namespace UltimateXR.Extensions.System.Threading { /// /// extensions. /// public static class ActionExt { #region Public Methods /// /// Executes repeatedly this , in the main thread, at until cancellation /// is requested with . /// /// to loop at Hz /// Loop frequency in Hz /// Cancellation token /// /// 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; } } /// /// Executes repeatedly this , in a separated thread, at Hz until /// cancellation is requested using . /// /// to loop at Hz /// Loop frequency in Hz /// Cancellation token 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); } } /// /// Creates a which wraps a cancellable loop executing this in /// the main thread. /// /// to loop at Hz /// Loop frequency in Hz /// /// Delay in milliseconds before loop executes its first iteration. /// /// /// Equal or greater than zero: tells to automatically start looping /// milliseconds after creation. /// /// /// Negative (default) needs to be called on returned /// to start looping. /// /// /// /// /// A to handle (, /// ) the loop execution. /// /// /// /// public static UxrLoopController ToLoop(this Action self, float rate = 10f, int autoStartDelay = -1) { return new UxrLoopController(ct => Loop(self, rate, ct), autoStartDelay); } /// /// Creates a which wraps a cancellable loop executing this in a /// separate thread. /// /// to loop, in a separate thread, at Hz /// Loop frequency in Hz /// /// Delay in milliseconds before loop executes its first iteration. /// /// /// Equal or greater than zero: tells to automatically start looping /// milliseconds after creation. /// /// /// Negative (default) needs to be called on returned /// to start looping. /// /// /// /// /// A to handle (, /// ) the loop execution. /// /// /// public static UxrLoopController ToThreadedLoop(this Action self, float rate = 10f, int autoStartDelay = -1) { return new UxrLoopController(ct => LoopThreaded(self, rate, ct), autoStartDelay); } #endregion } }