241 lines
6.7 KiB
C#
241 lines
6.7 KiB
C#
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
|
||
|
||
}
|
||
}
|