You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
579 lines
19 KiB
579 lines
19 KiB
11 months ago
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Text;
|
||
|
using System.Threading;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace UniRx
|
||
|
{
|
||
|
#if UniRxLibrary
|
||
|
public static partial class SchedulerUnity
|
||
|
{
|
||
|
#else
|
||
|
public static partial class Scheduler
|
||
|
{
|
||
|
public static void SetDefaultForUnity()
|
||
|
{
|
||
|
Scheduler.DefaultSchedulers.ConstantTimeOperations = Scheduler.Immediate;
|
||
|
Scheduler.DefaultSchedulers.TailRecursion = Scheduler.Immediate;
|
||
|
Scheduler.DefaultSchedulers.Iteration = Scheduler.CurrentThread;
|
||
|
Scheduler.DefaultSchedulers.TimeBasedOperations = MainThread;
|
||
|
Scheduler.DefaultSchedulers.AsyncConversions = Scheduler.ThreadPool;
|
||
|
}
|
||
|
#endif
|
||
|
static IScheduler mainThread;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Unity native MainThread Queue Scheduler. Run on mainthread and delayed on coroutine update loop, elapsed time is calculated based on Time.time.
|
||
|
/// </summary>
|
||
|
public static IScheduler MainThread
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return mainThread ?? (mainThread = new MainThreadScheduler());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static IScheduler mainThreadIgnoreTimeScale;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Another MainThread scheduler, delay elapsed time is calculated based on Time.unscaledDeltaTime.
|
||
|
/// </summary>
|
||
|
public static IScheduler MainThreadIgnoreTimeScale
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return mainThreadIgnoreTimeScale ?? (mainThreadIgnoreTimeScale = new IgnoreTimeScaleMainThreadScheduler());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static IScheduler mainThreadFixedUpdate;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Run on fixed update mainthread, delay elapsed time is calculated based on Time.fixedTime.
|
||
|
/// </summary>
|
||
|
public static IScheduler MainThreadFixedUpdate
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return mainThreadFixedUpdate ?? (mainThreadFixedUpdate = new FixedUpdateMainThreadScheduler());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static IScheduler mainThreadEndOfFrame;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Run on end of frame mainthread, delay elapsed time is calculated based on Time.deltaTime.
|
||
|
/// </summary>
|
||
|
public static IScheduler MainThreadEndOfFrame
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return mainThreadEndOfFrame ?? (mainThreadEndOfFrame = new EndOfFrameMainThreadScheduler());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class MainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
|
||
|
{
|
||
|
readonly Action<object> scheduleAction;
|
||
|
|
||
|
public MainThreadScheduler()
|
||
|
{
|
||
|
MainThreadDispatcher.Initialize();
|
||
|
scheduleAction = new Action<object>(Schedule);
|
||
|
}
|
||
|
|
||
|
// delay action is run in StartCoroutine
|
||
|
// Okay to action run synchronous and guaranteed run on MainThread
|
||
|
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
// zero == every frame
|
||
|
if (dueTime == TimeSpan.Zero)
|
||
|
{
|
||
|
yield return null; // not immediately, run next frame
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
yield return new WaitForSeconds((float)dueTime.TotalSeconds);
|
||
|
}
|
||
|
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
|
||
|
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
// zero == every frame
|
||
|
if (period == TimeSpan.Zero)
|
||
|
{
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null; // not immediately, run next frame
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var seconds = (float)(period.TotalMilliseconds / 1000.0);
|
||
|
var yieldInstruction = new WaitForSeconds(seconds); // cache single instruction object
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
yield return yieldInstruction;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public DateTimeOffset Now
|
||
|
{
|
||
|
get { return Scheduler.Now; }
|
||
|
}
|
||
|
|
||
|
void Schedule(object state)
|
||
|
{
|
||
|
var t = (Tuple<BooleanDisposable, Action>)state;
|
||
|
if (!t.Item1.IsDisposed)
|
||
|
{
|
||
|
t.Item2();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
MainThreadDispatcher.Post(scheduleAction, Tuple.Create(d, action));
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
|
||
|
{
|
||
|
return Schedule(dueTime - Now, action);
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(TimeSpan dueTime, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(dueTime);
|
||
|
|
||
|
MainThreadDispatcher.SendStartCoroutine(DelayAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(period);
|
||
|
|
||
|
MainThreadDispatcher.SendStartCoroutine(PeriodicAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
void ScheduleQueueing<T>(object state)
|
||
|
{
|
||
|
var t = (Tuple<ICancelable, T, Action<T>>)state;
|
||
|
if (!t.Item1.IsDisposed)
|
||
|
{
|
||
|
t.Item3(t.Item2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
|
||
|
{
|
||
|
MainThreadDispatcher.Post(QueuedAction<T>.Instance, Tuple.Create(cancel, state, action));
|
||
|
}
|
||
|
|
||
|
static class QueuedAction<T>
|
||
|
{
|
||
|
public static readonly Action<object> Instance = new Action<object>(Invoke);
|
||
|
|
||
|
public static void Invoke(object state)
|
||
|
{
|
||
|
var t = (Tuple<ICancelable, T, Action<T>>)state;
|
||
|
|
||
|
if (!t.Item1.IsDisposed)
|
||
|
{
|
||
|
t.Item3(t.Item2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class IgnoreTimeScaleMainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
|
||
|
{
|
||
|
readonly Action<object> scheduleAction;
|
||
|
|
||
|
public IgnoreTimeScaleMainThreadScheduler()
|
||
|
{
|
||
|
MainThreadDispatcher.Initialize();
|
||
|
scheduleAction = new Action<object>(Schedule);
|
||
|
}
|
||
|
|
||
|
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
if (dueTime == TimeSpan.Zero)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var elapsed = 0f;
|
||
|
var dt = (float)dueTime.TotalSeconds;
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) break;
|
||
|
|
||
|
elapsed += Time.unscaledDeltaTime;
|
||
|
if (elapsed >= dt)
|
||
|
{
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
// zero == every frame
|
||
|
if (period == TimeSpan.Zero)
|
||
|
{
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null; // not immediately, run next frame
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var elapsed = 0f;
|
||
|
var dt = (float)period.TotalSeconds;
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) break;
|
||
|
|
||
|
elapsed += Time.unscaledDeltaTime;
|
||
|
if (elapsed >= dt)
|
||
|
{
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
elapsed = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public DateTimeOffset Now
|
||
|
{
|
||
|
get { return Scheduler.Now; }
|
||
|
}
|
||
|
|
||
|
void Schedule(object state)
|
||
|
{
|
||
|
var t = (Tuple<BooleanDisposable, Action>)state;
|
||
|
if (!t.Item1.IsDisposed)
|
||
|
{
|
||
|
t.Item2();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
MainThreadDispatcher.Post(scheduleAction, Tuple.Create(d, action));
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
|
||
|
{
|
||
|
return Schedule(dueTime - Now, action);
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(TimeSpan dueTime, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(dueTime);
|
||
|
|
||
|
MainThreadDispatcher.SendStartCoroutine(DelayAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(period);
|
||
|
|
||
|
MainThreadDispatcher.SendStartCoroutine(PeriodicAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
|
||
|
{
|
||
|
MainThreadDispatcher.Post(QueuedAction<T>.Instance, Tuple.Create(cancel, state, action));
|
||
|
}
|
||
|
|
||
|
static class QueuedAction<T>
|
||
|
{
|
||
|
public static readonly Action<object> Instance = new Action<object>(Invoke);
|
||
|
|
||
|
public static void Invoke(object state)
|
||
|
{
|
||
|
var t = (Tuple<ICancelable, T, Action<T>>)state;
|
||
|
|
||
|
if (!t.Item1.IsDisposed)
|
||
|
{
|
||
|
t.Item3(t.Item2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class FixedUpdateMainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
|
||
|
{
|
||
|
public FixedUpdateMainThreadScheduler()
|
||
|
{
|
||
|
MainThreadDispatcher.Initialize();
|
||
|
}
|
||
|
|
||
|
IEnumerator ImmediateAction<T>(T state, Action<T> action, ICancelable cancellation)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action, state);
|
||
|
}
|
||
|
|
||
|
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
if (dueTime == TimeSpan.Zero)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var startTime = Time.fixedTime;
|
||
|
var dt = (float)dueTime.TotalSeconds;
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) break;
|
||
|
|
||
|
var elapsed = Time.fixedTime - startTime;
|
||
|
if (elapsed >= dt)
|
||
|
{
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
// zero == every frame
|
||
|
if (period == TimeSpan.Zero)
|
||
|
{
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var startTime = Time.fixedTime;
|
||
|
var dt = (float)period.TotalSeconds;
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) break;
|
||
|
|
||
|
var ft = Time.fixedTime;
|
||
|
var elapsed = ft - startTime;
|
||
|
if (elapsed >= dt)
|
||
|
{
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
startTime = ft;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public DateTimeOffset Now
|
||
|
{
|
||
|
get { return Scheduler.Now; }
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(Action action)
|
||
|
{
|
||
|
return Schedule(TimeSpan.Zero, action);
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
|
||
|
{
|
||
|
return Schedule(dueTime - Now, action);
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(TimeSpan dueTime, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(dueTime);
|
||
|
|
||
|
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(DelayAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(period);
|
||
|
|
||
|
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(PeriodicAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
|
||
|
{
|
||
|
MainThreadDispatcher.StartFixedUpdateMicroCoroutine(ImmediateAction(state, action, cancel));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class EndOfFrameMainThreadScheduler : IScheduler, ISchedulerPeriodic, ISchedulerQueueing
|
||
|
{
|
||
|
public EndOfFrameMainThreadScheduler()
|
||
|
{
|
||
|
MainThreadDispatcher.Initialize();
|
||
|
}
|
||
|
|
||
|
IEnumerator ImmediateAction<T>(T state, Action<T> action, ICancelable cancellation)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action, state);
|
||
|
}
|
||
|
|
||
|
IEnumerator DelayAction(TimeSpan dueTime, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
if (dueTime == TimeSpan.Zero)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var elapsed = 0f;
|
||
|
var dt = (float)dueTime.TotalSeconds;
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) break;
|
||
|
|
||
|
elapsed += Time.deltaTime;
|
||
|
if (elapsed >= dt)
|
||
|
{
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IEnumerator PeriodicAction(TimeSpan period, Action action, ICancelable cancellation)
|
||
|
{
|
||
|
// zero == every frame
|
||
|
if (period == TimeSpan.Zero)
|
||
|
{
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) yield break;
|
||
|
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var elapsed = 0f;
|
||
|
var dt = (float)period.TotalSeconds;
|
||
|
while (true)
|
||
|
{
|
||
|
yield return null;
|
||
|
if (cancellation.IsDisposed) break;
|
||
|
|
||
|
elapsed += Time.deltaTime;
|
||
|
if (elapsed >= dt)
|
||
|
{
|
||
|
MainThreadDispatcher.UnsafeSend(action);
|
||
|
elapsed = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public DateTimeOffset Now
|
||
|
{
|
||
|
get { return Scheduler.Now; }
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(Action action)
|
||
|
{
|
||
|
return Schedule(TimeSpan.Zero, action);
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(DateTimeOffset dueTime, Action action)
|
||
|
{
|
||
|
return Schedule(dueTime - Now, action);
|
||
|
}
|
||
|
|
||
|
public IDisposable Schedule(TimeSpan dueTime, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(dueTime);
|
||
|
|
||
|
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(DelayAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public IDisposable SchedulePeriodic(TimeSpan period, Action action)
|
||
|
{
|
||
|
var d = new BooleanDisposable();
|
||
|
var time = Scheduler.Normalize(period);
|
||
|
|
||
|
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(PeriodicAction(time, action, d));
|
||
|
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
public void ScheduleQueueing<T>(ICancelable cancel, T state, Action<T> action)
|
||
|
{
|
||
|
MainThreadDispatcher.StartEndOfFrameMicroCoroutine(ImmediateAction(state, action, cancel));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|