AsyncManager.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. using System.Collections;
  2. using UnityEngine;
  3. using System.Threading;
  4. namespace UnityAsync
  5. {
  6. [AddComponentMenu("")] // don't show in menu
  7. public partial class AsyncManager : MonoBehaviour
  8. {
  9. /// <summary>
  10. /// The frame count in the currently active update loop.
  11. /// </summary>
  12. public static int CurrentFrameCount { get; private set; }
  13. /// <summary>
  14. /// The time in the currently active update loop.
  15. /// </summary>
  16. public static float CurrentTime { get; private set; }
  17. /// <summary>
  18. /// The unscaled time in the currently active update loop.
  19. /// </summary>
  20. public static float CurrentUnscaledTime { get; private set; }
  21. /// <summary>
  22. /// Unity's <see cref="System.Threading.SynchronizationContext"/>.
  23. /// </summary>
  24. public static SynchronizationContext UnitySyncContext { get; private set; }
  25. /// <summary>
  26. /// Background (thread pool) <see cref="System.Threading.SynchronizationContext"/>.
  27. /// </summary>
  28. public static SynchronizationContext BackgroundSyncContext { get; private set; }
  29. /// <summary>
  30. /// Returns true if called from Unity's <see cref="System.Threading.SynchronizationContext"/>.
  31. /// </summary>
  32. public static bool InUnityContext => Thread.CurrentThread.ManagedThreadId == unityThreadId;
  33. public static AsyncManager Instance { get; private set; }
  34. static int unityThreadId, updateCount, lateCount, fixedCount;
  35. static ContinuationProcessorGroup updates, lateUpdates, fixedUpdates;
  36. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  37. static void Initialize()
  38. {
  39. unityThreadId = Thread.CurrentThread.ManagedThreadId;
  40. UnitySyncContext = SynchronizationContext.Current;
  41. BackgroundSyncContext = new SynchronizationContext(); // TODO: confirm this produces desired behaviour
  42. updates = new ContinuationProcessorGroup();
  43. lateUpdates = new ContinuationProcessorGroup();
  44. fixedUpdates = new ContinuationProcessorGroup();
  45. Instance = new GameObject("Async Manager").AddComponent<AsyncManager>();
  46. DontDestroyOnLoad(Instance);
  47. }
  48. /// <summary>
  49. /// Initializes the manager explicitly. Typically used when running Unity Editor tests (NUnit unit tests).
  50. /// </summary>
  51. public static void InitializeForEditorTests()
  52. {
  53. Initialize();
  54. // Do not run tasks in background when testing:
  55. BackgroundSyncContext = UnitySyncContext;
  56. }
  57. /// <summary>
  58. /// Queues a continuation.
  59. /// Intended for internal use only - you shouldn't need to invoke this.
  60. /// </summary>
  61. public static void AddContinuation<T>(in T cont) where T : IAwaitInstructionAwaiter
  62. {
  63. switch(cont.Scheduler)
  64. {
  65. case FrameScheduler.Update:
  66. updates.Add(cont);
  67. break;
  68. case FrameScheduler.LateUpdate:
  69. lateUpdates.Add(cont);
  70. break;
  71. case FrameScheduler.FixedUpdate:
  72. fixedUpdates.Add(cont);
  73. break;
  74. }
  75. }
  76. /// <summary>
  77. /// Start a coroutine from any context without requiring a MonoBehaviour.
  78. /// </summary>
  79. public new static Coroutine StartCoroutine(IEnumerator coroutine) => ((MonoBehaviour)Instance).StartCoroutine(coroutine);
  80. void Update()
  81. {
  82. CurrentFrameCount = ++updateCount;
  83. if(CurrentFrameCount <= 1)
  84. return;
  85. CurrentTime = Time.time;
  86. CurrentUnscaledTime = Time.unscaledTime;
  87. updates.Process();
  88. }
  89. void LateUpdate()
  90. {
  91. CurrentFrameCount = ++lateCount;
  92. if(CurrentFrameCount <= 1)
  93. return;
  94. lateUpdates.Process();
  95. }
  96. void FixedUpdate()
  97. {
  98. CurrentFrameCount = ++fixedCount;
  99. if(CurrentFrameCount <= 1)
  100. return;
  101. fixedUpdates.Process();
  102. }
  103. }
  104. }