holopy3/Assets/UniGLTF/DepthFirstScheduler/Schedulable.cs

242 lines
6.7 KiB
C#
Raw Permalink Normal View History

2021-01-28 12:07:52 +00:00
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
{
/// <returns>実行が終了したかCoroutineの実行が一回で終わらない場合がある</returns>
ExecutionStatus Execute();
Exception GetError();
IScheduler Schedulder { get; }
ISchedulable Parent { get; set; }
void AddChild(ISchedulable child);
IEnumerable<ISchedulable> 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<T> : ISchedulable
{
List<ISchedulable> m_children = new List<ISchedulable>();
public void AddChild(ISchedulable child)
{
child.Parent = this;
m_children.Add(child);
}
public IEnumerable<ISchedulable> 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<T> Func
{
get;
private set;
}
public Exception GetError()
{
return Func.GetError();
}
public Schedulable()
{
}
public Schedulable(IScheduler scheduler, IFunctor<T> func)
{
Schedulder = scheduler;
Func = func;
}
public ExecutionStatus Execute()
{
if (Func == null)
{
return ExecutionStatus.Done;
}
return Func.Execute();
}
/// <summary>
/// スケジュールされたタスクをすべて即時に実行する
/// </summary>
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<Unit> AddTask(IScheduler scheduler, Action pred)
{
return AddTask(scheduler, () => { pred(); return Unit.Default; });
}
public Schedulable<U> AddTask<U>(IScheduler scheduler, Func<U> pred)
{
var schedulable = new Schedulable<U>(scheduler, Functor.Create(() => Unit.Default, _ => pred()));
AddChild(schedulable);
return schedulable;
}
public Schedulable<T> AddCoroutine(IScheduler scheduler, Func<IEnumerator> starter)
{
var func = CoroutineFunctor.Create(() => default(T), _ => starter());
var schedulable = new Schedulable<T>(scheduler, func);
AddChild(schedulable);
return schedulable;
}
public Schedulable<Unit> ContinueWith(IScheduler scheduler, Action<T> pred)
{
return ContinueWith(scheduler, t => { pred(t); return Unit.Default; });
}
public Schedulable<U> ContinueWith<U>(IScheduler scheduler, Func<T, U> pred)
{
if (Parent == null)
{
throw new NoParentException();
}
Func<T> getResult = null;
if (Func != null)
{
getResult = Func.GetResult;
}
var func = Functor.Create(getResult, pred);
var schedulable = new Schedulable<U>(scheduler, func);
Parent.AddChild(schedulable);
return schedulable;
}
public Schedulable<T> ContinueWithCoroutine(IScheduler scheduler, Func<IEnumerator> starter)
{
if (Parent == null)
{
throw new NoParentException();
}
var func = CoroutineFunctor.Create(() => default(T), _ => starter());
var schedulable = new Schedulable<T>(scheduler, func);
Parent.AddChild(schedulable);
return schedulable;
}
public Schedulable<Unit> OnExecute(IScheduler scheduler, Action<Schedulable<Unit>> pred)
{
if (Parent == null)
{
throw new NoParentException();
}
Func<T> getResult = null;
if (Func != null)
{
getResult = Func.GetResult;
}
var schedulable = new Schedulable<Unit>();
schedulable.Func = Functor.Create(getResult, _ => { pred(schedulable); return Unit.Default; });
Parent.AddChild(schedulable);
return schedulable;
}
}
public static class Schedulable
{
public static Schedulable<Unit> Create()
{
return new Schedulable<Unit>().AddTask(Scheduler.CurrentThread, () =>
{
});
}
}
public static class SchedulableExtensions
{
public static void Subscribe<T>(
this Schedulable<T> schedulable,
IScheduler scheduler,
Action<T> onCompleted,
Action<Exception> onError)
{
schedulable.ContinueWith(scheduler, onCompleted);
TaskChain.Schedule(schedulable.GetRoot(), onError);
}
#if (NET_4_6 && UNITY_2017_1_OR_NEWER)
public static Task<T> ToTask<T>(this Schedulable<T> schedulable)
{
return ToTask(schedulable, Scheduler.MainThread);
}
public static Task<T> ToTask<T>(this Schedulable<T> schedulable, IScheduler scheduler)
{
var tcs = new TaskCompletionSource<T>();
schedulable.Subscribe(scheduler, r => tcs.TrySetResult(r), ex => tcs.TrySetException(ex));
return tcs.Task;
}
#endif
}
}