using System; using System.Collections.Generic; using System.Threading; using Best.HTTP.Shared; using Best.HTTP.Shared.PlatformSupport.Threading; namespace Best.HTTP.Shared.Extensions { public readonly struct TimerData { public readonly DateTime Created; public readonly TimeSpan Interval; public readonly object Context; public readonly Func OnTimer; public bool IsOnTime(DateTime now) { return now >= this.Created + this.Interval; } public TimerData(TimeSpan interval, object context, Func onTimer) { this.Created = DateTime.Now; this.Interval = interval; this.Context = context; this.OnTimer = onTimer; } /// /// Create a new TimerData but the Created field will be set to the current time. /// public TimerData CreateNew() { return new TimerData(this.Interval, this.Context, this.OnTimer); } public override string ToString() { return string.Format("[TimerData Created: {0}, Interval: {1}, IsOnTime: {2}]", this.Created.ToString(System.Globalization.CultureInfo.InvariantCulture), this.Interval, this.IsOnTime(DateTime.Now)); } } public static class Timer { private static List _timers = new List(1); private static ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private static int _isSubscribed; #if UNITY_EDITOR [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] public static void ResetSetup() { HTTPManager.Logger.Information(nameof(Timer), "Reset called!"); Interlocked.Exchange(ref _isSubscribed, 0); } #endif public static void Add(TimerData timer) { using (var _ = new WriteLock(_lock)) _timers.Add(timer); if (Interlocked.CompareExchange(ref _isSubscribed, 1, 0) == 0) { HTTPManager.Logger.Information(nameof(Timer), "Subscribing timer to heartbeats!"); HTTPManager.Heartbeats.Subscribe(new TimerImplementation()); } } private sealed class TimerImplementation : IHeartbeat { public void OnHeartbeatUpdate(DateTime now, TimeSpan dif) { using var __ = new Unity.Profiling.ProfilerMarker(nameof(Timer)).Auto(); using (var _ = new WriteLock(_lock)) { if (_timers.Count == 0) { HTTPManager.Heartbeats.Unsubscribe(this); Interlocked.Exchange(ref _isSubscribed, 0); HTTPManager.Logger.Information(nameof(Timer), "Unsubscribing timer from heartbeats!"); return; } for (int i = 0; i < _timers.Count; ++i) { TimerData timer = _timers[i]; if (timer.IsOnTime(now)) { try { bool repeat = timer.OnTimer(now, timer.Context); if (repeat) _timers[i] = timer.CreateNew(); else _timers.RemoveAt(i--); } catch (Exception ex) { HTTPManager.Logger.Exception(nameof(Timer), "OnTimer", ex); } } } } } } } }