using System.Collections;
using UnityEngine;
using System.Threading;
namespace UnityAsync
{
[AddComponentMenu("")] // don't show in menu
public partial class AsyncManager : MonoBehaviour
{
///
/// The frame count in the currently active update loop.
///
public static int CurrentFrameCount { get; private set; }
///
/// The time in the currently active update loop.
///
public static float CurrentTime { get; private set; }
///
/// The unscaled time in the currently active update loop.
///
public static float CurrentUnscaledTime { get; private set; }
///
/// Unity's .
///
public static SynchronizationContext UnitySyncContext { get; private set; }
///
/// Background (thread pool) .
///
public static SynchronizationContext BackgroundSyncContext { get; private set; }
///
/// Returns true if called from Unity's .
///
public static bool InUnityContext => Thread.CurrentThread.ManagedThreadId == unityThreadId;
public static AsyncManager Instance { get; private set; }
static int unityThreadId, updateCount, lateCount, fixedCount;
static ContinuationProcessorGroup updates, lateUpdates, fixedUpdates;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Initialize()
{
unityThreadId = Thread.CurrentThread.ManagedThreadId;
UnitySyncContext = SynchronizationContext.Current;
BackgroundSyncContext = new SynchronizationContext(); // TODO: confirm this produces desired behaviour
updates = new ContinuationProcessorGroup();
lateUpdates = new ContinuationProcessorGroup();
fixedUpdates = new ContinuationProcessorGroup();
Instance = new GameObject("Async Manager").AddComponent();
DontDestroyOnLoad(Instance);
}
///
/// Initializes the manager explicitly. Typically used when running Unity Editor tests (NUnit unit tests).
///
public static void InitializeForEditorTests()
{
Initialize();
// Do not run tasks in background when testing:
BackgroundSyncContext = UnitySyncContext;
}
///
/// Queues a continuation.
/// Intended for internal use only - you shouldn't need to invoke this.
///
public static void AddContinuation(in T cont) where T : IAwaitInstructionAwaiter
{
switch(cont.Scheduler)
{
case FrameScheduler.Update:
updates.Add(cont);
break;
case FrameScheduler.LateUpdate:
lateUpdates.Add(cont);
break;
case FrameScheduler.FixedUpdate:
fixedUpdates.Add(cont);
break;
}
}
///
/// Start a coroutine from any context without requiring a MonoBehaviour.
///
public new static Coroutine StartCoroutine(IEnumerator coroutine) => ((MonoBehaviour)Instance).StartCoroutine(coroutine);
void Update()
{
CurrentFrameCount = ++updateCount;
if(CurrentFrameCount <= 1)
return;
CurrentTime = Time.time;
CurrentUnscaledTime = Time.unscaledTime;
updates.Process();
}
void LateUpdate()
{
CurrentFrameCount = ++lateCount;
if(CurrentFrameCount <= 1)
return;
lateUpdates.Process();
}
void FixedUpdate()
{
CurrentFrameCount = ++fixedCount;
if(CurrentFrameCount <= 1)
return;
fixedUpdates.Process();
}
}
}