Timer.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using Best.HTTP.Shared;
  5. using Best.HTTP.Shared.PlatformSupport.Threading;
  6. namespace Best.HTTP.Shared.Extensions
  7. {
  8. public readonly struct TimerData
  9. {
  10. public readonly DateTime Created;
  11. public readonly TimeSpan Interval;
  12. public readonly object Context;
  13. public readonly Func<DateTime, object, bool> OnTimer;
  14. public bool IsOnTime(DateTime now)
  15. {
  16. return now >= this.Created + this.Interval;
  17. }
  18. public TimerData(TimeSpan interval, object context, Func<DateTime, object, bool> onTimer)
  19. {
  20. this.Created = DateTime.Now;
  21. this.Interval = interval;
  22. this.Context = context;
  23. this.OnTimer = onTimer;
  24. }
  25. /// <summary>
  26. /// Create a new TimerData but the Created field will be set to the current time.
  27. /// </summary>
  28. public TimerData CreateNew()
  29. {
  30. return new TimerData(this.Interval, this.Context, this.OnTimer);
  31. }
  32. public override string ToString()
  33. {
  34. return string.Format("[TimerData Created: {0}, Interval: {1}, IsOnTime: {2}]", this.Created.ToString(System.Globalization.CultureInfo.InvariantCulture), this.Interval, this.IsOnTime(DateTime.Now));
  35. }
  36. }
  37. public static class Timer
  38. {
  39. private static List<TimerData> _timers = new List<TimerData>(1);
  40. private static ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
  41. private static int _isSubscribed;
  42. #if UNITY_EDITOR
  43. [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
  44. public static void ResetSetup()
  45. {
  46. HTTPManager.Logger.Information(nameof(Timer), "Reset called!");
  47. Interlocked.Exchange(ref _isSubscribed, 0);
  48. }
  49. #endif
  50. public static void Add(TimerData timer)
  51. {
  52. using (var _ = new WriteLock(_lock))
  53. _timers.Add(timer);
  54. if (Interlocked.CompareExchange(ref _isSubscribed, 1, 0) == 0)
  55. {
  56. HTTPManager.Logger.Information(nameof(Timer), "Subscribing timer to heartbeats!");
  57. HTTPManager.Heartbeats.Subscribe(new TimerImplementation());
  58. }
  59. }
  60. private sealed class TimerImplementation : IHeartbeat
  61. {
  62. public void OnHeartbeatUpdate(DateTime now, TimeSpan dif)
  63. {
  64. using var __ = new Unity.Profiling.ProfilerMarker(nameof(Timer)).Auto();
  65. using (var _ = new WriteLock(_lock))
  66. {
  67. if (_timers.Count == 0)
  68. {
  69. HTTPManager.Heartbeats.Unsubscribe(this);
  70. Interlocked.Exchange(ref _isSubscribed, 0);
  71. HTTPManager.Logger.Information(nameof(Timer), "Unsubscribing timer from heartbeats!");
  72. return;
  73. }
  74. for (int i = 0; i < _timers.Count; ++i)
  75. {
  76. TimerData timer = _timers[i];
  77. if (timer.IsOnTime(now))
  78. {
  79. try
  80. {
  81. bool repeat = timer.OnTimer(now, timer.Context);
  82. if (repeat)
  83. _timers[i] = timer.CreateNew();
  84. else
  85. _timers.RemoveAt(i--);
  86. }
  87. catch (Exception ex)
  88. {
  89. HTTPManager.Logger.Exception(nameof(Timer), "OnTimer", ex);
  90. }
  91. }
  92. }
  93. }
  94. }
  95. }
  96. }
  97. }