Move DepthFirstScheduler to Assets/*

This commit is contained in:
yutopp
2018-12-28 22:28:51 +09:00
parent 75b4d54839
commit 0f2565ba09
35 changed files with 921 additions and 921 deletions

View File

@@ -1,24 +1,24 @@
using NUnit.Framework;
using System.Linq;
namespace DepthFirstScheduler
{
public class DepthFirstScheduler
{
[Test]
public void ScheduleTreeTest()
{
var s = Schedulable.Create();
var tasks = s.GetRoot().Traverse().ToArray();
Assert.AreEqual(2, tasks.Length);
var task_int = s.AddTask(Scheduler.CurrentThread, () => 0);
task_int = task_int.ContinueWith(Scheduler.CurrentThread, _ => 1);
var status = s.Execute();
Assert.AreEqual(ExecutionStatus.Done, status);
}
}
}
using NUnit.Framework;
using System.Linq;
namespace DepthFirstScheduler
{
public class DepthFirstScheduler
{
[Test]
public void ScheduleTreeTest()
{
var s = Schedulable.Create();
var tasks = s.GetRoot().Traverse().ToArray();
Assert.AreEqual(2, tasks.Length);
var task_int = s.AddTask(Scheduler.CurrentThread, () => 0);
task_int = task_int.ContinueWith(Scheduler.CurrentThread, _ => 1);
var status = s.Execute();
Assert.AreEqual(ExecutionStatus.Done, status);
}
}
}

View File

@@ -1,192 +1,192 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace DepthFirstScheduler
{
public enum ExecutionStatus
{
Unknown,
Done,
Continue, // coroutine or schedulable
Error,
}
public interface IFunctor<T>
{
T GetResult();
Exception GetError();
ExecutionStatus Execute();
}
#region Functor
public class Functor<T> : IFunctor<T>
{
T m_result;
public T GetResult()
{
return m_result;
}
Exception m_error;
public Exception GetError()
{
return m_error;
}
Action m_pred;
public Functor(Func<T> func)
{
m_pred = () => m_result = func();
}
public ExecutionStatus Execute()
{
try
{
m_pred();
return ExecutionStatus.Done;
}
catch (Exception ex)
{
m_error = ex;
return ExecutionStatus.Error;
}
}
}
public static class Functor
{
/// <summary>
/// 引数の型を隠蔽した実行器を生成する
/// </summary>
/// <typeparam name="S">引数の型</typeparam>
/// <typeparam name="T">結果の型</typeparam>
/// <param name="arg"></param>
/// <param name="pred"></param>
/// <returns></returns>
public static Functor<T> Create<S, T>(Func<S> arg, Func<S, T> pred)
{
return new Functor<T>(() => pred(arg()));
}
}
#endregion
#region CoroutineFunctor
public class CoroutineFunctor<T> : IFunctor<T>
{
T m_result;
public T GetResult()
{
return m_result;
}
Exception m_error;
public Exception GetError()
{
return m_error;
}
Func<T> m_arg;
Func<T, IEnumerator> m_starter;
Stack<IEnumerator> m_it;
public CoroutineFunctor(Func<T> arg, Func<T, IEnumerator> starter)
{
m_arg = arg;
m_starter = starter;
}
public ExecutionStatus Execute()
{
if (m_it == null)
{
m_result = m_arg();
m_it = new Stack<IEnumerator>();
m_it.Push(m_starter(m_result));
}
try
{
if (m_it.Count!=0)
{
if (m_it.Peek().MoveNext())
{
var nested = m_it.Peek().Current as IEnumerator;
if (nested!=null)
{
m_it.Push(nested);
}
}
else
{
m_it.Pop();
}
return ExecutionStatus.Continue;
}
else
{
return ExecutionStatus.Done;
}
}
catch(Exception ex)
{
m_error = ex;
return ExecutionStatus.Error;
}
}
}
public static class CoroutineFunctor
{
public static CoroutineFunctor<T> Create<T>(Func<T> arg, Func<T, IEnumerator> starter)
{
return new CoroutineFunctor<T>(arg, starter);
}
}
#endregion
/*
public class SchedulableFunctor<T> : IFunctor<T>
{
Schedulable<T> m_schedulable;
Func<Schedulable<T>> m_starter;
TaskChain m_chain;
public SchedulableFunctor(Func<Schedulable<T>> starter)
{
m_starter = starter;
}
public ExecutionStatus Execute()
{
if (m_chain == null)
{
m_schedulable = m_starter();
m_chain = TaskChain.Schedule(m_schedulable, ex => m_error = ex);
}
return m_chain.Next();
}
Exception m_error;
public Exception GetError()
{
return m_error;
}
public T GetResult()
{
return m_schedulable.Func.GetResult();
}
}
public static class SchedulableFunctor
{
public static SchedulableFunctor<T> Create<T>(Func<Schedulable<T>> starter)
{
return new SchedulableFunctor<T>(starter);
}
}
*/
}
using System;
using System.Collections;
using System.Collections.Generic;
namespace DepthFirstScheduler
{
public enum ExecutionStatus
{
Unknown,
Done,
Continue, // coroutine or schedulable
Error,
}
public interface IFunctor<T>
{
T GetResult();
Exception GetError();
ExecutionStatus Execute();
}
#region Functor
public class Functor<T> : IFunctor<T>
{
T m_result;
public T GetResult()
{
return m_result;
}
Exception m_error;
public Exception GetError()
{
return m_error;
}
Action m_pred;
public Functor(Func<T> func)
{
m_pred = () => m_result = func();
}
public ExecutionStatus Execute()
{
try
{
m_pred();
return ExecutionStatus.Done;
}
catch (Exception ex)
{
m_error = ex;
return ExecutionStatus.Error;
}
}
}
public static class Functor
{
/// <summary>
/// 引数の型を隠蔽した実行器を生成する
/// </summary>
/// <typeparam name="S">引数の型</typeparam>
/// <typeparam name="T">結果の型</typeparam>
/// <param name="arg"></param>
/// <param name="pred"></param>
/// <returns></returns>
public static Functor<T> Create<S, T>(Func<S> arg, Func<S, T> pred)
{
return new Functor<T>(() => pred(arg()));
}
}
#endregion
#region CoroutineFunctor
public class CoroutineFunctor<T> : IFunctor<T>
{
T m_result;
public T GetResult()
{
return m_result;
}
Exception m_error;
public Exception GetError()
{
return m_error;
}
Func<T> m_arg;
Func<T, IEnumerator> m_starter;
Stack<IEnumerator> m_it;
public CoroutineFunctor(Func<T> arg, Func<T, IEnumerator> starter)
{
m_arg = arg;
m_starter = starter;
}
public ExecutionStatus Execute()
{
if (m_it == null)
{
m_result = m_arg();
m_it = new Stack<IEnumerator>();
m_it.Push(m_starter(m_result));
}
try
{
if (m_it.Count!=0)
{
if (m_it.Peek().MoveNext())
{
var nested = m_it.Peek().Current as IEnumerator;
if (nested!=null)
{
m_it.Push(nested);
}
}
else
{
m_it.Pop();
}
return ExecutionStatus.Continue;
}
else
{
return ExecutionStatus.Done;
}
}
catch(Exception ex)
{
m_error = ex;
return ExecutionStatus.Error;
}
}
}
public static class CoroutineFunctor
{
public static CoroutineFunctor<T> Create<T>(Func<T> arg, Func<T, IEnumerator> starter)
{
return new CoroutineFunctor<T>(arg, starter);
}
}
#endregion
/*
public class SchedulableFunctor<T> : IFunctor<T>
{
Schedulable<T> m_schedulable;
Func<Schedulable<T>> m_starter;
TaskChain m_chain;
public SchedulableFunctor(Func<Schedulable<T>> starter)
{
m_starter = starter;
}
public ExecutionStatus Execute()
{
if (m_chain == null)
{
m_schedulable = m_starter();
m_chain = TaskChain.Schedule(m_schedulable, ex => m_error = ex);
}
return m_chain.Next();
}
Exception m_error;
public Exception GetError()
{
return m_error;
}
public T GetResult()
{
return m_schedulable.Func.GetResult();
}
}
public static class SchedulableFunctor
{
public static SchedulableFunctor<T> Create<T>(Func<Schedulable<T>> starter)
{
return new SchedulableFunctor<T>(starter);
}
}
*/
}

View File

@@ -1,31 +1,31 @@
using System.Collections;
using System.Collections.Generic;
namespace
DepthFirstScheduler
{
public static class IEnumeratorExtensions
{
public static void CoroutinetoEnd(this IEnumerator coroutine)
{
var stack = new Stack<IEnumerator>();
stack.Push(coroutine);
while (stack.Count > 0)
{
if (stack.Peek().MoveNext())
{
var nested = stack.Peek().Current as IEnumerator;
if (nested != null)
{
stack.Push(nested);
}
}
else
{
stack.Pop();
}
}
}
}
}
using System.Collections;
using System.Collections.Generic;
namespace
DepthFirstScheduler
{
public static class IEnumeratorExtensions
{
public static void CoroutinetoEnd(this IEnumerator coroutine)
{
var stack = new Stack<IEnumerator>();
stack.Push(coroutine);
while (stack.Count > 0)
{
if (stack.Peek().MoveNext())
{
var nested = stack.Peek().Current as IEnumerator;
if (nested != null)
{
stack.Push(nested);
}
}
else
{
stack.Pop();
}
}
}
}
}

View File

@@ -1,95 +1,95 @@
using System.Collections;
using System.Collections.Generic;
namespace DepthFirstScheduler
{
public class LockQueue<T> where T : class
{
List<T> m_queue = new List<T>();
public int Count
{
get
{
lock (((ICollection)m_queue).SyncRoot)
{
return m_queue.Count;
}
}
}
public void Enqueue(T t)
{
lock (((ICollection)m_queue).SyncRoot)
{
m_queue.Add(t);
}
}
public T Dequeue(out int remain)
{
lock (((ICollection)m_queue).SyncRoot)
{
if (m_queue.Count == 0)
{
remain = 0;
return null;
}
var item = m_queue[0];
m_queue.RemoveAt(0);
remain = m_queue.Count;
return item;
}
}
public T Dequeue()
{
lock (((ICollection)m_queue).SyncRoot)
{
if (m_queue.Count == 0) return null;
var item = m_queue[0];
m_queue.RemoveAt(0);
return item;
}
}
}
public class LockQueueForValue<T> where T : struct
{
List<T> m_queue = new List<T>();
public int Count
{
get
{
lock (((ICollection)m_queue).SyncRoot)
{
return m_queue.Count;
}
}
}
public void Enqueue(T t)
{
lock (((ICollection)m_queue).SyncRoot)
{
m_queue.Add(t);
}
}
public bool TryDequeue(out T t)
{
lock (((ICollection)m_queue).SyncRoot)
{
if (m_queue.Count == 0)
{
t = default(T);
return false;
}
t = m_queue[0];
m_queue.RemoveAt(0);
return true;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
namespace DepthFirstScheduler
{
public class LockQueue<T> where T : class
{
List<T> m_queue = new List<T>();
public int Count
{
get
{
lock (((ICollection)m_queue).SyncRoot)
{
return m_queue.Count;
}
}
}
public void Enqueue(T t)
{
lock (((ICollection)m_queue).SyncRoot)
{
m_queue.Add(t);
}
}
public T Dequeue(out int remain)
{
lock (((ICollection)m_queue).SyncRoot)
{
if (m_queue.Count == 0)
{
remain = 0;
return null;
}
var item = m_queue[0];
m_queue.RemoveAt(0);
remain = m_queue.Count;
return item;
}
}
public T Dequeue()
{
lock (((ICollection)m_queue).SyncRoot)
{
if (m_queue.Count == 0) return null;
var item = m_queue[0];
m_queue.RemoveAt(0);
return item;
}
}
}
public class LockQueueForValue<T> where T : struct
{
List<T> m_queue = new List<T>();
public int Count
{
get
{
lock (((ICollection)m_queue).SyncRoot)
{
return m_queue.Count;
}
}
}
public void Enqueue(T t)
{
lock (((ICollection)m_queue).SyncRoot)
{
m_queue.Add(t);
}
}
public bool TryDequeue(out T t)
{
lock (((ICollection)m_queue).SyncRoot)
{
if (m_queue.Count == 0)
{
t = default(T);
return false;
}
t = m_queue[0];
m_queue.RemoveAt(0);
return true;
}
}
}
}

View File

@@ -1,150 +1,150 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace DepthFirstScheduler
{
/// <summary>
/// UniRxのMainThreadDispatcherを参考にした。
/// * https://github.com/neuecc/UniRx/blob/master/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/MainThreadDispatcher.cs
/// </summary>
public class MainThreadDispatcher : MonoBehaviour
{
[Header("Debug")]
public int TaskCount;
IEnumerable<Transform> Ancestors(Transform t)
{
yield return t;
if (t.parent != null)
{
foreach (var x in Ancestors(t.parent))
{
yield return x;
}
}
}
private void Update()
{
TaskCount = Scheduler.MainThread.UpdateAndGetTaskCount();
}
static MainThreadDispatcher instance;
static bool initialized;
static bool isQuitting = false;
public static bool IsInitialized
{
get { return initialized && instance != null; }
}
[ThreadStatic]
static object mainThreadToken;
public static MainThreadDispatcher Instance
{
get
{
Initialize();
return instance;
}
}
public static void Initialize()
{
if (!initialized)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
return;
}
#endif
MainThreadDispatcher dispatcher = null;
try
{
dispatcher = GameObject.FindObjectOfType<MainThreadDispatcher>();
}
catch
{
// Throw exception when calling from a worker thread.
var ex = new Exception(
"DepthFirstScheduler requires a MainThreadDispatcher component created on the main thread."
+ " Make sure it is added to the scene before calling DepthFirstScheduler from a worker thread.");
UnityEngine.Debug.LogException(ex);
throw ex;
}
if (isQuitting)
{
// don't create new instance after quitting
// avoid "Some objects were not cleaned up when closing the scene find target" error.
return;
}
if (dispatcher == null)
{
// awake call immediately from UnityEngine
new GameObject("DepthFirstScheduler").AddComponent<MainThreadDispatcher>();
}
else
{
dispatcher.Awake(); // force awake
}
}
}
public static bool IsInMainThread
{
get
{
return (mainThreadToken != null);
}
}
void Awake()
{
if (instance == null)
{
Debug.Log("Initialize UniTask.MainThredDispatcher");
instance = this;
mainThreadToken = new object();
initialized = true;
DontDestroyOnLoad(gameObject);
}
else
{
if (this != instance)
{
Debug.LogWarning("There is already a MainThreadDispatcher in the scene.");
}
}
}
void OnDestroy()
{
if (instance == this)
{
instance = GameObject.FindObjectOfType<MainThreadDispatcher>();
initialized = instance != null;
}
if (Scheduler.SingleWorkerThread != null)
{
Scheduler.SingleWorkerThread.Dispose();
}
}
void OnApplicationQuit()
{
isQuitting = true;
}
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
namespace DepthFirstScheduler
{
/// <summary>
/// UniRxのMainThreadDispatcherを参考にした。
/// * https://github.com/neuecc/UniRx/blob/master/Assets/Plugins/UniRx/Scripts/UnityEngineBridge/MainThreadDispatcher.cs
/// </summary>
public class MainThreadDispatcher : MonoBehaviour
{
[Header("Debug")]
public int TaskCount;
IEnumerable<Transform> Ancestors(Transform t)
{
yield return t;
if (t.parent != null)
{
foreach (var x in Ancestors(t.parent))
{
yield return x;
}
}
}
private void Update()
{
TaskCount = Scheduler.MainThread.UpdateAndGetTaskCount();
}
static MainThreadDispatcher instance;
static bool initialized;
static bool isQuitting = false;
public static bool IsInitialized
{
get { return initialized && instance != null; }
}
[ThreadStatic]
static object mainThreadToken;
public static MainThreadDispatcher Instance
{
get
{
Initialize();
return instance;
}
}
public static void Initialize()
{
if (!initialized)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
return;
}
#endif
MainThreadDispatcher dispatcher = null;
try
{
dispatcher = GameObject.FindObjectOfType<MainThreadDispatcher>();
}
catch
{
// Throw exception when calling from a worker thread.
var ex = new Exception(
"DepthFirstScheduler requires a MainThreadDispatcher component created on the main thread."
+ " Make sure it is added to the scene before calling DepthFirstScheduler from a worker thread.");
UnityEngine.Debug.LogException(ex);
throw ex;
}
if (isQuitting)
{
// don't create new instance after quitting
// avoid "Some objects were not cleaned up when closing the scene find target" error.
return;
}
if (dispatcher == null)
{
// awake call immediately from UnityEngine
new GameObject("DepthFirstScheduler").AddComponent<MainThreadDispatcher>();
}
else
{
dispatcher.Awake(); // force awake
}
}
}
public static bool IsInMainThread
{
get
{
return (mainThreadToken != null);
}
}
void Awake()
{
if (instance == null)
{
Debug.Log("Initialize UniTask.MainThredDispatcher");
instance = this;
mainThreadToken = new object();
initialized = true;
DontDestroyOnLoad(gameObject);
}
else
{
if (this != instance)
{
Debug.LogWarning("There is already a MainThreadDispatcher in the scene.");
}
}
}
void OnDestroy()
{
if (instance == this)
{
instance = GameObject.FindObjectOfType<MainThreadDispatcher>();
initialized = instance != null;
}
if (Scheduler.SingleWorkerThread != null)
{
Scheduler.SingleWorkerThread.Dispose();
}
}
void OnApplicationQuit()
{
isQuitting = true;
}
}
}

View File

@@ -1,47 +1,47 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace DepthFirstScheduler
{
/// <summary>
/// http://blogs.msdn.com/b/toub/archive/2006/04/12/blocking-queues.aspx
///
/// 終了を通知するにはnullを投入する手が使える
/// </summary>
/// <typeparam name="T"></typeparam>
public class MonitorQueue<T>
{
private Int32 _count = 0;
public Int32 Count
{
get
{
return _count;
}
}
private Queue<T> _queue = new Queue<T>();
public T Dequeue()
{
lock (_queue)
{
while (_count <= 0) Monitor.Wait(_queue);
_count--;
return _queue.Dequeue();
}
}
public void Enqueue(T data)
{
lock (_queue)
{
_queue.Enqueue(data);
_count++;
Monitor.Pulse(_queue);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Threading;
namespace DepthFirstScheduler
{
/// <summary>
/// http://blogs.msdn.com/b/toub/archive/2006/04/12/blocking-queues.aspx
///
/// 終了を通知するにはnullを投入する手が使える
/// </summary>
/// <typeparam name="T"></typeparam>
public class MonitorQueue<T>
{
private Int32 _count = 0;
public Int32 Count
{
get
{
return _count;
}
}
private Queue<T> _queue = new Queue<T>();
public T Dequeue()
{
lock (_queue)
{
while (_count <= 0) Monitor.Wait(_queue);
_count--;
return _queue.Dequeue();
}
}
public void Enqueue(T data)
{
lock (_queue)
{
_queue.Enqueue(data);
_count++;
Monitor.Pulse(_queue);
}
}
}
}

View File

@@ -1,241 +1,241 @@
using System;
using System.Collections;
using System.Collections.Generic;
#if ((NET_4_6 || NET_STANDARD_2_0) && 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 || NET_STANDARD_2_0) && 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
}
}
using System;
using System.Collections;
using System.Collections.Generic;
#if ((NET_4_6 || NET_STANDARD_2_0) && 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 || NET_STANDARD_2_0) && 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
}
}

View File

@@ -1,9 +1,9 @@
using System;
namespace DepthFirstScheduler
{
public interface IScheduler : IDisposable
{
void Enqueue(TaskChain item);
}
}
using System;
namespace DepthFirstScheduler
{
public interface IScheduler : IDisposable
{
void Enqueue(TaskChain item);
}
}

View File

@@ -1,91 +1,91 @@
using System;
using System.Linq;
using System.Collections.Generic;
namespace DepthFirstScheduler
{
public enum ChainStatus
{
Unknown,
Continue,
Done,
Error,
}
public class TaskChain
{
public IEnumerator<ISchedulable> Enumerator;
public Action<Exception> OnError;
public ChainStatus ChainStatus;
public static TaskChain Schedule(ISchedulable schedulable, Action<Exception> onError)
{
var item = new TaskChain
{
Enumerator = schedulable.Traverse().GetEnumerator(),
OnError = onError
};
if (item.Enumerator.MoveNext())
{
if (item.Enumerator.Current.Schedulder == null)
{
// default
Scheduler.MainThread.Enqueue(item);
}
else
{
item.Enumerator.Current.Schedulder.Enqueue(item);
}
}
return item;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public ExecutionStatus Next()
{
if (this.ChainStatus == ChainStatus.Done
|| this.ChainStatus== ChainStatus.Error)
{
return ExecutionStatus.Done;
}
{
var status = Enumerator.Current.Execute();
if (status == ExecutionStatus.Error)
{
ChainStatus = ChainStatus.Error;
OnError(Enumerator.Current.GetError());
}
if (status == ExecutionStatus.Continue)
{
// 中断(coroutine)
ChainStatus = ChainStatus.Continue;
return ExecutionStatus.Continue;
}
}
if (!Enumerator.MoveNext())
{
// 終了
ChainStatus = ChainStatus.Done;
return ExecutionStatus.Done;
}
if (Enumerator.Current.Schedulder != null)
{
// Scheduleして中断
ChainStatus = ChainStatus.Continue;
Enumerator.Current.Schedulder.Enqueue(this);
return ExecutionStatus.Done;
}
return Next();
}
}
}
using System;
using System.Linq;
using System.Collections.Generic;
namespace DepthFirstScheduler
{
public enum ChainStatus
{
Unknown,
Continue,
Done,
Error,
}
public class TaskChain
{
public IEnumerator<ISchedulable> Enumerator;
public Action<Exception> OnError;
public ChainStatus ChainStatus;
public static TaskChain Schedule(ISchedulable schedulable, Action<Exception> onError)
{
var item = new TaskChain
{
Enumerator = schedulable.Traverse().GetEnumerator(),
OnError = onError
};
if (item.Enumerator.MoveNext())
{
if (item.Enumerator.Current.Schedulder == null)
{
// default
Scheduler.MainThread.Enqueue(item);
}
else
{
item.Enumerator.Current.Schedulder.Enqueue(item);
}
}
return item;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public ExecutionStatus Next()
{
if (this.ChainStatus == ChainStatus.Done
|| this.ChainStatus== ChainStatus.Error)
{
return ExecutionStatus.Done;
}
{
var status = Enumerator.Current.Execute();
if (status == ExecutionStatus.Error)
{
ChainStatus = ChainStatus.Error;
OnError(Enumerator.Current.GetError());
}
if (status == ExecutionStatus.Continue)
{
// 中断(coroutine)
ChainStatus = ChainStatus.Continue;
return ExecutionStatus.Continue;
}
}
if (!Enumerator.MoveNext())
{
// 終了
ChainStatus = ChainStatus.Done;
return ExecutionStatus.Done;
}
if (Enumerator.Current.Schedulder != null)
{
// Scheduleして中断
ChainStatus = ChainStatus.Continue;
Enumerator.Current.Schedulder.Enqueue(this);
return ExecutionStatus.Done;
}
return Next();
}
}
}

View File

@@ -1,41 +1,41 @@
using System;
namespace DepthFirstScheduler
{
[Serializable]
public struct Unit : IEquatable<Unit>
{
static readonly Unit @default = new Unit();
public static Unit Default { get { return @default; } }
public static bool operator ==(Unit first, Unit second)
{
return true;
}
public static bool operator !=(Unit first, Unit second)
{
return false;
}
public bool Equals(Unit other)
{
return true;
}
public override bool Equals(object obj)
{
return obj is Unit;
}
public override int GetHashCode()
{
return 0;
}
public override string ToString()
{
return "()";
}
}
}
using System;
namespace DepthFirstScheduler
{
[Serializable]
public struct Unit : IEquatable<Unit>
{
static readonly Unit @default = new Unit();
public static Unit Default { get { return @default; } }
public static bool operator ==(Unit first, Unit second)
{
return true;
}
public static bool operator !=(Unit first, Unit second)
{
return false;
}
public bool Equals(Unit other)
{
return true;
}
public override bool Equals(object obj)
{
return obj is Unit;
}
public override int GetHashCode()
{
return 0;
}
public override string ToString()
{
return "()";
}
}
}