Merge commit 'f20f27259c28dbff6697958b8727d39d24f74ce1' as 'DepthFirstScheduler'

Co-authored-by: ousttrue <ousttrue@gmail.com>
Co-authored-by: TORISOUP <tori.birdstrike@gmail.com>
Co-authored-by: yutopp <yutopp@gmail.com>
This commit is contained in:
ousttrue 2018-12-28 21:15:54 +09:00
commit d43d135c80
34 changed files with 1504 additions and 0 deletions

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cc0ab91a2011fcf4d925dd94bccdb243
folderAsset: yes
timeCreated: 1540294739
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3003e87b5df13394eadfda925bb816e5
timeCreated: 1540294752
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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);
}
}
*/
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c320f3f577dde634a871dc88266c2a20
timeCreated: 1520084196
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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();
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4719fc9e6319c654b8a4818bef2c3a85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 ousttrue
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b673d33707cbc5446870804437f3cbea
timeCreated: 1535290899
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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;
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f6b1eb436e400704ab6e2ad6eff29f4b
timeCreated: 1519978475
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b030a9507e46dd3488c080be0227b219
timeCreated: 1519977925
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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);
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5e968e04ff53e1c4a9a2869b7cf76055
timeCreated: 1519990411
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,66 @@
# DepthFirstScheduler(深さ優先スケジューラー)
Asynchronous task scheduler for Unity-5.6 or later
これは、Unity5.6でTaskが無いことを補完するためのライブラリです。
木構造にタスクを組み立てて深さ優先で消化します。
* タスクの実行スケジューラー(Unityメインスレッドやスレッドプール)を指定できる
# 使い方
```cs
var schedulable = new Schedulable<Unit>();
schedulable
.AddTask(Scheduler.ThreadPool, () => // 子供のタスクを追加する
{
return glTF_VRM_Material.Parse(ctx.Json);
})
.ContinueWith(Scheduler.MainThread, gltfMaterials => // 兄弟のタスクを追加する
{
ctx.MaterialImporter = new VRMMaterialImporter(ctx, gltfMaterials);
})
.Subscribe(Scheduler.MainThread, onLoaded, onError);
;
```
# Schedulable<T>
T型の結果を返すタスク。
## AddTask(IScheduler scheduler, Func<T> firstTask)
子供のタスクを追加する。
ToDo: 一つ目の子供に引数を渡す手段が無い
## ContinueWith
## ContinueWithCoroutine
## OnExecute
動的にタスクを追加するためのHook。
中で、
```
parent.AddTask
```
することで実行時に子タスクを追加できる。
## Subscribe
タスクの実行を開始する。
実行結果を得る。
# Scheduler
## StepScheduler
Unity
## CurrentThreadScheduler
即時
## ThreadPoolScheduler
スレッド
## ThreadScheduler
スレッド

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1231fa531fdd33f4baccc935c18c0872
timeCreated: 1535290899
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 45e21e7db278f344fbaeffc4c4b82b1e
timeCreated: 1519981307
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5a160dc164df4094dbbc892055ac82ae
timeCreated: 1520084097
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0700fb042f010694782d238049678651
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,102 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace DepthFirstScheduler
{
public static partial class Scheduler
{
private static IScheduler currentThread;
public static IScheduler CurrentThread
{
get { return currentThread ?? (currentThread = new CurrentThreadScheduler()); }
}
public class CurrentThreadScheduler : IScheduler
{
[ThreadStatic]
private static Queue<TaskChain> queue;
private static Queue<TaskChain> GetQueue()
{
return queue;
}
private static void SetQueue(Queue<TaskChain> newQueue)
{
queue = newQueue;
}
public void Enqueue(TaskChain item)
{
var q = GetQueue();
if (q == null)
{
q = new Queue<TaskChain>(5);
q.Enqueue(item);
SetQueue(q);
try
{
Trampoline.Run(q);
}
finally
{
SetQueue(null);
}
}
else
{
q.Enqueue(item);
}
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
var queue = GetQueue();
if (queue != null) queue.Clear();
SetQueue(null);
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
static class Trampoline
{
public static void Run(Queue<TaskChain> queue)
{
while (queue.Count > 0)
{
var chain = queue.Dequeue();
while (true)
{
var status = chain.Next();
if (status != ExecutionStatus.Continue)
{
break;
}
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 21979cc7137a17d4ea8b6202381a02d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
namespace DepthFirstScheduler
{
public static partial class Scheduler
{
private static StepScheduler mainThread;
public static StepScheduler MainThread
{
get
{
if (mainThread != null) return mainThread;
mainThread = new StepScheduler();
MainThreadDispatcher.Initialize();
return mainThread;
}
}
public class StepScheduler : IScheduler
{
LockQueue<TaskChain> m_taskQueue = new LockQueue<TaskChain>();
public void Enqueue(TaskChain item)
{
m_taskQueue.Enqueue(item);
}
TaskChain m_chain;
public int UpdateAndGetTaskCount()
{
if (m_chain != null)
{
var status = m_chain.Next();
if (status == ExecutionStatus.Continue)
{
// m_item継続中
return m_taskQueue.Count;
}
m_chain = null;
}
int count;
m_chain = m_taskQueue.Dequeue(out count);
return count;
}
public void Dispose()
{
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 84161938c020c37419bc79f021f849fb
timeCreated: 1520062584
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
using System;
namespace DepthFirstScheduler
{
public static partial class Scheduler
{
private static IScheduler threadPool;
public static IScheduler ThreadPool
{
get { return threadPool ?? (threadPool = new ThreadPoolScheduler()); }
}
public class ThreadPoolScheduler : IScheduler
{
public void Enqueue(TaskChain item)
{
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
{
if (item == null)
{
return;
}
while (true)
{
var status = item.Next();
if (status != ExecutionStatus.Continue)
{
break;
}
}
});
}
public void Dispose()
{
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d8a8d78a486bbdf4b95ae83d02b480d8
timeCreated: 1524110819
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,101 @@
using System;
using System.Threading;
namespace DepthFirstScheduler
{
public static partial class Scheduler
{
private static IScheduler singleWorkerThread;
public static IScheduler SingleWorkerThread
{
get { return singleWorkerThread ?? (singleWorkerThread = new ThreadScheduler()); }
}
public class ThreadScheduler : IScheduler
{
MonitorQueue<TaskChain> m_queue = new MonitorQueue<TaskChain>();
Thread m_thread;
public ThreadScheduler()
{
// start worker thread
m_thread = new Thread(new ParameterizedThreadStart(Worker));
m_thread.Start(m_queue);
}
static void Worker(Object arg)
{
MonitorQueue<TaskChain> queue = (MonitorQueue<TaskChain>)arg;
while (true)
{
var chain = queue.Dequeue();
if (chain == null)
{
break;
}
while (true)
{
var status = chain.Next();
if (status != ExecutionStatus.Continue)
{
break;
}
}
}
// end
}
public void Enqueue(TaskChain item)
{
m_queue.Enqueue(item);
}
#region IDisposable Support
private bool disposedValue = false; // 重複する呼び出しを検出するには
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: マネージ状態を破棄します (マネージ オブジェクト)。
if (m_thread != null)
{
m_queue.Enqueue(null);
m_thread.Join();
m_thread = null;
}
}
// TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
// TODO: 大きなフィールドを null に設定します。
disposedValue = true;
}
}
// TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
// ~ThreadScheduler() {
// // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
// Dispose(false);
// }
// このコードは、破棄可能なパターンを正しく実装できるように追加されました。
public void Dispose()
{
// このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
Dispose(true);
// TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
// GC.SuppressFinalize(this);
}
#endregion
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8df1de98bdc0b534bbe68e8f2f8a858f
timeCreated: 1520062592
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 38fc7e076ba93a847ad72d3d459d06b6
timeCreated: 1520084019
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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 "()";
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 98bd42f8c3a895d4bac6cbcf523878bb
timeCreated: 1520081817
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: