using System; using System.Collections; using System.Collections.Generic; #if (NET_4_6 && UNITY_2017_1_OR_NEWER) using System.Threading.Tasks; #endif namespace DepthFirstScheduler { public interface ISchedulable { /// 実行が終了したか?Coroutineの実行が一回で終わらない場合がある ExecutionStatus Execute(); Exception GetError(); IScheduler Schedulder { get; } ISchedulable Parent { get; set; } void AddChild(ISchedulable child); IEnumerable Traverse(); } public static class ISchedulableExtensions { public static ISchedulable GetRoot(this ISchedulable self) { var current = self; while (current.Parent != null) { current = current.Parent; } return current; } } public class NoParentException: Exception { public NoParentException():base("No parent task can't ContinueWith or OnExecute. First AddTask") { } } public class Schedulable : ISchedulable { List m_children = new List(); public void AddChild(ISchedulable child) { child.Parent = this; m_children.Add(child); } public IEnumerable Traverse() { yield return this; foreach (var child in m_children) { foreach (var x in child.Traverse()) { yield return x; } } } public ISchedulable Parent { get; set; } public IScheduler Schedulder { get; private set; } public IFunctor Func { get; private set; } public Exception GetError() { return Func.GetError(); } public Schedulable() { } public Schedulable(IScheduler scheduler, IFunctor func) { Schedulder = scheduler; Func = func; } public ExecutionStatus Execute() { if (Func == null) { return ExecutionStatus.Done; } return Func.Execute(); } /// /// スケジュールされたタスクをすべて即時に実行する /// public void ExecuteAll() { foreach (var x in this.GetRoot().Traverse()) { while (true) { var status = x.Execute(); if (status != ExecutionStatus.Continue) { if (status == ExecutionStatus.Error) { throw x.GetError(); } break; } // Coroutineタスクが継続している } } } public Schedulable AddTask(IScheduler scheduler, Action pred) { return AddTask(scheduler, () => { pred(); return Unit.Default; }); } public Schedulable AddTask(IScheduler scheduler, Func pred) { var schedulable = new Schedulable(scheduler, Functor.Create(() => Unit.Default, _ => pred())); AddChild(schedulable); return schedulable; } public Schedulable AddCoroutine(IScheduler scheduler, Func starter) { var func = CoroutineFunctor.Create(() => default(T), _ => starter()); var schedulable = new Schedulable(scheduler, func); AddChild(schedulable); return schedulable; } public Schedulable ContinueWith(IScheduler scheduler, Action pred) { return ContinueWith(scheduler, t => { pred(t); return Unit.Default; }); } public Schedulable ContinueWith(IScheduler scheduler, Func pred) { if (Parent == null) { throw new NoParentException(); } Func getResult = null; if (Func != null) { getResult = Func.GetResult; } var func = Functor.Create(getResult, pred); var schedulable = new Schedulable(scheduler, func); Parent.AddChild(schedulable); return schedulable; } public Schedulable ContinueWithCoroutine(IScheduler scheduler, Func starter) { if (Parent == null) { throw new NoParentException(); } var func = CoroutineFunctor.Create(() => default(T), _ => starter()); var schedulable = new Schedulable(scheduler, func); Parent.AddChild(schedulable); return schedulable; } public Schedulable OnExecute(IScheduler scheduler, Action> pred) { if (Parent == null) { throw new NoParentException(); } Func getResult = null; if (Func != null) { getResult = Func.GetResult; } var schedulable = new Schedulable(); schedulable.Func = Functor.Create(getResult, _ => { pred(schedulable); return Unit.Default; }); Parent.AddChild(schedulable); return schedulable; } } public static class Schedulable { public static Schedulable Create() { return new Schedulable().AddTask(Scheduler.CurrentThread, () => { }); } } public static class SchedulableExtensions { public static void Subscribe( this Schedulable schedulable, IScheduler scheduler, Action onCompleted, Action onError) { schedulable.ContinueWith(scheduler, onCompleted); TaskChain.Schedule(schedulable.GetRoot(), onError); } #if (NET_4_6 && UNITY_2017_1_OR_NEWER) public static Task ToTask(this Schedulable schedulable) { return ToTask(schedulable, Scheduler.MainThread); } public static Task ToTask(this Schedulable schedulable, IScheduler scheduler) { var tcs = new TaskCompletionSource(); schedulable.Subscribe(scheduler, r => tcs.TrySetResult(r), ex => tcs.TrySetException(ex)); return tcs.Task; } #endif } }