using System;
using System.Collections;
using System.Collections.Generic;
#if (NET_4_6 && UNITY_2018_1_OR_NEWER)
using System.Threading;
using System.Runtime.CompilerServices;
#endif
namespace UniTask
{
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 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 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)
{
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)
{
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)
{
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 ISchedulable ContinueWithNested(Func> starter, IScheduler scheduler)
{
var func = SchedulableFunctor.Create(() => starter(Func.GetResult()));
return new Schedulable(scheduler, func, this);
}
*/
}
public static class Schedulable
{
public static Schedulable Create()
{
return new Schedulable();
}
}
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_2018_1_OR_NEWER)
public static SchedulableAwaiter GetAwaiter(this Schedulable schedulable)
{
return new SchedulableAwaiter(schedulable, true);
}
#endif
/*
public static ISchedulable Sequencial(IEnumerable> schedulables, Action mergePred)
{
var it = schedulables.GetEnumerator();
ISchedulable last = Schedulable.Start(null, () => Unit.Default);
while (it.MoveNext())
{
var current = it.Current;
var merger = current.ContinueWith(result =>
{
mergePred(result);
return Unit.Default;
});
if (last != null)
{
// 連結
current.EnumParents().Last().Parent = last;
}
last = merger;
}
return last;
}
*/
/*
public static ISchedulable MergeCollection(this ISchedulable schedulable,
Func> extractor,
Func pred,
Action merger,
IScheduler scheduler = null)
{
return schedulable.ContinueWithNested(x =>
{
var schedulables = extractor(x).Select(y => Schedulable.Start(scheduler, () => pred(x, y)));
return Sequencial(schedulables, z => merger(x, z)).ContinueWith(_ => x);
}, scheduler);
}
*/
#if (NET_4_6 && UNITY_2018_1_OR_NEWER)
public class SchedulableAwaiter : INotifyCompletion
{
private readonly Schedulable target = null;
private T result;
private readonly bool continueOnCapturedContext;
public T GetResult()
{
return result;
}
public bool IsCompleted { get; private set; }
public SchedulableAwaiter(Schedulable target)
{
this.target = target;
continueOnCapturedContext = true;
}
public SchedulableAwaiter(Schedulable target, bool continueOnCapturedContext)
{
this.target = target;
this.continueOnCapturedContext = continueOnCapturedContext;
}
public void OnCompleted(Action continuation)
{
var context = SynchronizationContext.Current;
if (continueOnCapturedContext && context != null)
{
target.Subscribe(target.Parent.Schedulder, r =>
{
result = r;
IsCompleted = true;
context.Post(_ => continuation(), null);
}, ex => { throw ex; });
}
else
{
target.Subscribe(target.Parent.Schedulder, r =>
{
result = r;
IsCompleted = true;
continuation();
}, ex => { throw ex; });
}
}
}
#endif
}
}