// zlib/libpng License
//
// Copyright (c) 2018 - 2019 Sinoa
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
namespace IceMilkTea.Core
{
#region Awaiter構造体
///
/// 値を返さない、汎用的な待機構造体です。
///
public struct ImtAwaiter : INotifyCompletion
{
// メンバ変数定義
private IAwaitable awaitableContext;
private ExceptionDispatchInfo exceptionInfo;
///
/// IAwaitable.IsCompleted の値を取り出します
///
public bool IsCompleted => awaitableContext.IsCompleted;
///
/// ImtAwaiter のインスタンスを初期化します
///
/// この待機オブジェクトを保持する IAwaitable
public ImtAwaiter(IAwaitable context)
{
// 保持する担当を覚えて、もろもろ初期化
awaitableContext = context;
exceptionInfo = null;
}
///
/// タスクが完了した時のハンドリングを行います。
///
/// タスクを継続動作させるための継続関数
public void OnCompleted(Action continuation)
{
try
{
// 既にタスクが完了しているのなら
if (IsCompleted)
{
// 直ちに継続関数を叩いて終了
continuation();
return;
}
// 継続関数を登録する
awaitableContext.RegisterContinuation(continuation);
}
catch (Exception exception)
{
// 例外をキャプチャして覚えておいて、直ちに継続関数を叩く
exceptionInfo = ExceptionDispatchInfo.Capture(exception);
continuation();
}
}
///
/// タスクの結果を取得しますが、この構造体は常に結果は操作しません。
///
public void GetResult()
{
// もし例外情報を持っていたら
if (exceptionInfo != null)
{
// ここで例外を投げてメソッドビルダーに例外を任せる
exceptionInfo.Throw();
}
// 待機可能クラスが例外情報を持っているなら
var error = awaitableContext.GetError();
if (error != null)
{
// ここで吐き出す
error.Throw();
}
}
}
///
/// 値を返す、汎用的な待機構造体です。
///
/// 待機可能オブジェクトが返す値の型
public struct ImtAwaiter : INotifyCompletion
{
// メンバ変数定義
private IAwaitable awaitableContext;
private ExceptionDispatchInfo exceptionInfo;
///
/// IAwaitable.IsCompleted の値を取り出します
///
public bool IsCompleted => awaitableContext.IsCompleted;
///
/// ImtAwaiter のインスタンスを初期化します
///
/// この待機オブジェクトを保持する IAwaitable
public ImtAwaiter(IAwaitable context)
{
// 保持する担当を覚えて、もろもろ初期化
awaitableContext = context;
exceptionInfo = null;
}
///
/// タスクが完了した時のハンドリングを行います。
///
/// タスクを継続動作させるための継続関数
public void OnCompleted(Action continuation)
{
try
{
// 既にタスクが完了しているのなら
if (IsCompleted)
{
// 直ちに継続関数を叩いて終了
continuation();
return;
}
// 継続関数を登録する
awaitableContext.RegisterContinuation(continuation);
}
catch (Exception exception)
{
// 例外をキャプチャして覚えておいて、直ちに継続関数を叩く
exceptionInfo = ExceptionDispatchInfo.Capture(exception);
continuation();
}
}
///
/// タスクの結果を取得します。
///
/// IAwaitable.GetResult() の結果を返します
public TResult GetResult()
{
// もし例外情報を持っていたら
if (exceptionInfo != null)
{
// ここで例外を投げてメソッドビルダーに例外を任せる
exceptionInfo.Throw();
}
// 待機可能クラスが例外情報を持っているなら
var error = awaitableContext.GetError();
if (error != null)
{
// ここで吐き出す
error.Throw();
return default(TResult);
}
// 待機結果を取得して返す(この関数が例外を出しても、メソッドビルダーに拾い上げられる)
return awaitableContext.GetResult();
}
}
#endregion
#region Awaitableインターフェイス
///
/// 値を返さない、待機可能なオブジェクトが実装する、インターフェイスを定義しています。
///
public interface IAwaitable
{
///
/// タスクが完了している場合は true を、完了していない場合は false を取り出します。
///
bool IsCompleted { get; }
///
/// 待機をするための、汎用待機オブジェクト ImtAwaiter を取得します。
///
/// 汎用待機オブジェクト ImtAwaiter のインスタンスを返します
ImtAwaiter GetAwaiter();
///
/// Awaiter が待機を完了した時に継続動作するための、継続関数を登録します。
///
/// 登録する継続関数
void RegisterContinuation(Action continuation);
///
/// Awaitable が持っているエラー情報となる例外情報を取得します。
///
/// エラーを保持している場合は、その例外情報を返します。持っていない場合は null を返します。
ExceptionDispatchInfo GetError();
}
///
/// 値を返す、待機可能なオブジェクトが実装する、インターフェイスを定義しています。
///
/// 待機可能オブジェクトが返す値の型
public interface IAwaitable : IAwaitable
{
///
/// 待機をするための、値の返すことのできる汎用待機オブジェクト ImtAwaiter を取得します。
///
/// 汎用待機オブジェクト ImtAwaiter のインスタンスを返します
new ImtAwaiter GetAwaiter();
///
/// 待機した結果を取得します。
///
/// 継続動作時に取得される結果を返します
TResult GetResult();
}
#endregion
#region Awaiter継続関数ハンドラクラス
///
/// 比較的スタンダードな Awaiter の継続関数をハンドリングするクラスです。
/// このクラスは、多数の Awaiter の継続関数を登録することが可能で、継続関数を登録とシグナル設定をするだけで動作します。
///
public class AwaiterContinuationHandler
{
///
/// 登録された Awaiter の継続関数と、その登録した時の同期コンテキストを保持する構造体です
///
private struct Handler
{
///
/// 継続関数登録時の同期コンテキスト
///
private SynchronizationContext context;
///
/// 登録された継続関数
///
private Action continuation;
///
/// Handler のインスタンスを初期化します
///
/// 利用する同期コンテキスト
/// 同期コンテキストにPostする継続関数
public Handler(SynchronizationContext context, Action continuation)
{
// 初期化
this.context = context;
this.continuation = continuation;
}
///
/// 同期コンテキストに継続関数をPostします
///
public void DoPost()
{
// 同期コンテキストに継続関数をポストする
context.Post(continuation);
}
}
// メンバ変数定義
private Queue handlerQueue;
///
/// 登録された継続関数の数
///
public int HandlerCount => GetHandlerCount();
///
/// AwaiterContinuationHandler のインスタンスを既定サイズで初期化します。
///
public AwaiterContinuationHandler() : this(capacity: 32)
{
}
///
/// AwaiterContinuationHandler のインスタンスを指定された容量で初期化します。
///
/// 登録する継続関数の初期容量
public AwaiterContinuationHandler(int capacity)
{
// ハンドラキューの生成
handlerQueue = new Queue(capacity);
}
///
/// 登録された継続関数の数を取得します
///
/// 登録された継続関数の数を返します
public int GetHandlerCount()
{
// キューをロック
lock (handlerQueue)
{
// 登録済みハンドラの数を返す
return handlerQueue.Count;
}
}
///
/// Awaiter の継続関数を登録します。
/// 登録した継続関数は SetSignal() または SetOneShotSignal() 関数にて継続を行うことが可能です。
///
/// 登録する継続関数
public void RegisterContinuation(Action continuation)
{
// キューをロック
lock (handlerQueue)
{
// 継続関数をハンドラキューに追加する
handlerQueue.Enqueue(new Handler(AsyncOperationManager.SynchronizationContext, continuation));
}
}
///
/// 登録された継続関数を、登録時の同期コンテキストを通じて呼び出されるようにします。
/// また、一度シグナルした継続処理の参照は消失するため、再度 Awaite するには、改めて継続関数を登録する必要があります。
///
public void SetSignal()
{
// キューをロック
lock (handlerQueue)
{
// キューが空になるまでループ
while (handlerQueue.Count > 0)
{
// キューからハンドラをデキューして継続関数をポストする
handlerQueue.Dequeue().DoPost();
}
}
}
///
/// 登録された複数の継続関数のうち1つだけ継続関数を呼び出します。
/// 複数の待機オブジェクトが存在している場合は、先に待機したオブジェクトから継続関数を呼びます。
///
public void SetOneShotSignal()
{
// キューをロック
lock (handlerQueue)
{
// キューは既に空であれば
if (handlerQueue.Count == 0)
{
// 終了
return;
}
// キューからハンドラをデキューして継続関数をポストする
handlerQueue.Dequeue().DoPost();
}
}
}
#endregion
#region AwaitableBase
///
/// 値の返却をしない、待機可能なクラスを実装するための基本抽象クラスです。
/// 汎用的な通常の、値の返却をしない待機可能クラスを実装をする場合には、このクラスを継承して下さい。
///
public abstract class ImtAwaitable : IAwaitable, IDisposable
{
// メンバ変数定義
private AwaiterContinuationHandler awaiterHandler;
private ExceptionDispatchInfo exceptionInfo;
///
/// 完了済み Awaitable を取得します
///
public static IAwaitable CompletedAwaitable { get; } = new CompletedAwaitableImpl();
///
/// タスクが完了しているかどうか
///
public virtual bool IsCompleted { get; protected set; }
///
/// このオブジェクトが解放済みかどうか
///
public bool Disposed { get; private set; }
///
/// ImtAwaitable のインスタンスを初期化します
///
public ImtAwaitable()
{
// 待機オブジェクトハンドラの生成
awaiterHandler = new AwaiterContinuationHandler();
}
///
/// ImtAwaitable のインスタンスを初期化します
///
/// 待機オブジェクトハンドラの初期容量
public ImtAwaitable(int capacity)
{
// 待機オブジェクトハンドラの生成
awaiterHandler = new AwaiterContinuationHandler(capacity);
}
///
/// ImtAwaitable のファイナライザを実行します
///
~ImtAwaitable()
{
// ファイナライザからのDispose呼び出し
Dispose(false);
}
///
/// リソースの解放を行います。
/// また、待機中のオブジェクトが存在する場合は、シグナルが直ちに設定されます。
///
public void Dispose()
{
// DisposeからのDispose呼び出しをして、ファイナライズキューに入らないようにしてもらう
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// 実際のリソース解放を行います。
///
/// Diisposeからの呼び出しの場合は true が、ファイナライザからの呼び出しの場合は false が設定されます
protected virtual void Dispose(bool disposing)
{
// 既に解放済みなら
if (Disposed)
{
// 何もせず終了
return;
}
// 無条件にIsCompletedをtrueに設定して、すべての待機オブジェクトにシグナルを送る
IsCompleted = true;
awaiterHandler.SetSignal();
// 解放済みマーク
Disposed = true;
}
///
/// この待機可能クラスの、待機オブジェクトを取得します
///
/// 待機オブジェクトを返します
/// この待機クラスは既に破棄されています
public virtual ImtAwaiter GetAwaiter()
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 新しい待機オブジェクトを生成して返す
return new ImtAwaiter(this);
}
///
/// 待機オブジェクトからの継続関数を登録します
///
/// 登録する継続関数
/// この待機クラスは既に破棄されています
public virtual void RegisterContinuation(Action continuation)
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 待機オブジェクトハンドラに継続関数を登録する
awaiterHandler.RegisterContinuation(continuation);
}
///
/// 登録された継続関数にシグナルを設定して、継続関数が呼び出されるようにします。
///
/// この待機クラスは既に破棄されています
protected internal virtual void SetSignal()
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 待機オブジェクトハンドラのシグナルを設定する
awaiterHandler.SetSignal();
}
///
/// 待機状態が完了するとともに、登録された継続関数にシグナルを設定して、継続関数が呼び出されるようにします。
///
/// この待機クラスは既に破棄されています
protected internal virtual void SetSignalWithCompleted()
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 完了状態にして、待機オブジェクトハンドラのシグナルを設定する
IsCompleted = true;
awaiterHandler.SetSignal();
}
///
/// 登録された複数の継続関数のうち1つだけ継続関数を呼び出します。
/// 複数の待機オブジェクトが存在している場合は、先に待機したオブジェクトから継続関数を呼びます。
///
/// この待機クラスは既に破棄されています
protected virtual void SetOneShotSignal()
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 待機オブジェクトハンドラのシグナルを設定する
awaiterHandler.SetOneShotSignal();
}
///
/// 待機状態が完了するとともに、登録された複数の継続関数のうち1つだけ継続関数を呼び出します。
/// 複数の待機オブジェクトが存在している場合は、先に待機したオブジェクトから継続関数を呼びます。
///
/// この待機クラスは既に破棄されています
protected virtual void SetOneShotSignalWithCompleted()
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 完了状態にして、待機オブジェクトハンドラのシグナルを設定する
IsCompleted = true;
awaiterHandler.SetOneShotSignal();
}
///
/// 待機可能クラスが、例外を発生させてしまった場合、その例外を設定します。
/// この関数で設定した例外は、適切なタイミングで報告されます。
/// また、原則としてこの関数を利用した直後は SetSignal() 関数を呼び出して
/// 直ちに継続関数を解放するようにしてください。
///
/// 設定する例外
/// この待機クラスは既に破棄されています
protected internal void SetException(Exception exception)
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 例外をキャプチャして保持する
exceptionInfo = ExceptionDispatchInfo.Capture(exception);
}
///
/// 待機可能クラスが、発生した例外の情報を取得します。
///
/// 発生した例外の情報を返します
/// この待機クラスは既に破棄されています
public ExceptionDispatchInfo GetError()
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 持っている例外情報を返す
return exceptionInfo;
}
///
/// オブジェクトが解放済みの場合は、例外を送出します
///
/// この待機クラスは既に破棄されています
protected void ThrowIfDisposed()
{
// 既に破棄済みなら
if (Disposed)
{
// 例外を吐く
throw new ObjectDisposedException(this.GetType().FullName);
}
}
///
/// 常に完了を返し続ける Awaitable クラスです
///
private sealed class CompletedAwaitableImpl : ImtAwaitable
{
///
/// 常に true を返し続けます
///
public override bool IsCompleted { get => true; protected set => _ = value; }
///
/// このクラスは Dispose() による影響を一切受けないように上書きします
///
/// 何も実行しません
protected override void Dispose(bool disposing)
{
// Disposedにならないように空で上書きする
}
}
}
///
/// 値の返却をする、待機可能なクラスを実装するための基本抽象クラスです。
/// 汎用的な通常の、値の返却をする待機可能クラスを実装をする場合には、このクラスを継承して下さい。
///
public abstract class ImtAwaitable : ImtAwaitable, IAwaitable
{
///
/// ImtAwaitable のインスタンスを初期化します
///
public ImtAwaitable() : base()
{
}
///
/// ImtAwaitable のインスタンスを初期化します
///
/// 待機オブジェクトハンドラの初期容量
public ImtAwaitable(int capacity) : base(capacity)
{
}
///
/// この待機可能クラスの、待機オブジェクトを取得します
///
/// 待機オブジェクトを返します
/// この待機クラスは既に破棄されています
public new ImtAwaiter GetAwaiter()
{
// 解放済み例外関数を叩く
ThrowIfDisposed();
// 新しい待機オブジェクトを生成して返す
return new ImtAwaiter(this);
}
///
/// この待機可能クラスの結果を取得します
///
/// 結果を返します
public abstract TResult GetResult();
///
/// 指定された値を返す完了済み Awaitable を生成します
///
/// 結果として返す値
/// 指定された値を返す完了済み Awaitable のインスタンスを返します
public static IAwaitable FromResult(TResult result)
{
// 値を返す完了済みインスタンスを生成して返す
return new CompletedAwaitableImpl(result);
}
///
/// 常に完了を返し続ける値を持った Awaitable クラスです
///
private sealed class CompletedAwaitableImpl : ImtAwaitable
{
// メンバ変数定義
private TResult result;
///
/// 常に true を返し続けます
///
public override bool IsCompleted { get => true; protected set => _ = value; }
///
/// CompletedAwaitableImpl クラスのインスタンスを初期化します
///
/// 返すべき値
public CompletedAwaitableImpl(TResult result)
{
// 返す値を覚えておく
this.result = result;
}
///
/// この待機可能クラスの結果を取得します
///
/// 結果を返します
public override TResult GetResult()
{
// 覚えた結果を返す
return result;
}
///
/// このクラスは Dispose() による影響を一切受けないように上書きします
///
/// 何も実行しません
protected override void Dispose(bool disposing)
{
// Disposedにならないように空で上書きする
}
}
}
#endregion
#region AwaitableWaitHandle
///
/// シグナル操作をして待機状態をコントロールすることの出来る、待機可能な抽象クラスです。
///
///
/// 単純なシグナル操作による、待機制御を実現する場合には有用です。
///
public abstract class ImtAwaitableWaitHandle : ImtAwaitable
{
///
/// ImtAwaitableWaitHandle のインスタンスを初期化します
///
/// 初期のシグナル状態
public ImtAwaitableWaitHandle(bool initialSignal)
{
// シグナル状態を初期化
IsCompleted = initialSignal;
}
///
/// この待機ハンドルのシグナルを設定して。
/// 待機オブジェクトの待機を解除します。
///
public abstract void Set();
///
/// この待機ハンドルのシグナルを解除して。
/// オブジェクトが待機状態になるようにします。
///
/// 待機ハンドルは解放済みです
public virtual void Reset()
{
// 解放済み例外の処理をしておく
ThrowIfDisposed();
// 非シグナル状態にする
IsCompleted = false;
}
}
///
/// シグナル状態をマニュアルコントロールする待機可能な、待機ハンドラクラスです
///
public class ImtAwaitableManualReset : ImtAwaitableWaitHandle
{
///
/// ImtAwaitableManualReset のインスタンスを初期化します
///
/// 初期のシグナル状態
public ImtAwaitableManualReset(bool initialSignal) : base(initialSignal)
{
}
///
/// 待機ハンドラをシグナル状態にして、待機オブジェクトの待機を解除します。
/// また ResetSignal() を呼び出さない限り、ずっと待機されない状態になります。
/// 再び、待機状態にさせるには ResetSignal() を呼び出して下さい。
///
/// 待機ハンドルは解放済みです
///
public override void Set()
{
// シグナル状態を設定して、継続関数を呼び出す
SetSignalWithCompleted();
}
}
///
/// シグナル状態をマニュアルコントロールする待機可能な、値を返せる待機ハンドラクラスです。
///
public class ImtAwaitableManualReset : ImtAwaitableManualReset, IAwaitable
{
// メンバ変数定義
private TResult result;
///
/// ImtAwaitableManualReset のインスタンスを初期化します
///
/// 初期のシグナル状態
public ImtAwaitableManualReset(bool initialSignal) : base(initialSignal)
{
}
///
/// 待機した結果の値を、事前に設定します。
///
/// 準備する結果
public void PrepareResult(TResult result)
{
// 結果を覚えておく
this.result = result;
}
///
/// 待機結果を設定してから、待機ハンドラをシグナル状態にして、待機オブジェクトの待機を解除します。
/// また ResetSignal() を呼び出さない限り、ずっと待機されない状態になります。
/// 再び、待機状態にさせるには ResetSignal() を呼び出して下さい。
///
/// 待機した結果として設定する値
/// 待機ハンドルは解放済みです
///
public void Set(TResult result)
{
// 結果を設定して基本クラスのSetを呼ぶ
this.result = result;
Set();
}
///
/// このオブジェクトの待機オブジェクトを取得します
///
/// 待機ハンドルは解放済みです
/// 待機オブジェクトを返します
public new ImtAwaiter GetAwaiter()
{
// 解放済み例外の処理をしておく
ThrowIfDisposed();
// 単純なAwaiterを返す
return new ImtAwaiter(this);
}
///
/// タスクの待機結果を取得します
///
/// 待機結果を返します
public TResult GetResult()
{
// 結果をそのまま返す
return result;
}
}
///
/// シグナル状態を自動コントロールする待機可能な、待機ハンドラクラスです
///
public class ImtAwaitableAutoReset : ImtAwaitableWaitHandle
{
///
/// ImtAwaitableAutoReset のインスタンスを初期化します
///
public ImtAwaitableAutoReset() : base(false)
{
}
///
/// 待機ハンドラをシグナル状態にして、最初に待機した1つの待機オブジェクトの待機を解除します。
/// また、待機オブジェクトの待機が解除された直後に、直ちに非シグナル状態になるため
/// すべての待機オブジェクトの待機を解除するためには、再び Set() を呼び出す必要があります。
///
/// 待機ハンドルは解放済みです
public override void Set()
{
// シグナル状態を設定して、継続関数を1つだけ呼び出した後、直ちに非シグナル状態にする
IsCompleted = true;
SetOneShotSignal();
Reset();
}
}
///
/// シグナル状態を自動コントロールする待機可能な、値を返せる待機ハンドラクラスです
///
public class ImtAwaitableAutoReset : ImtAwaitableAutoReset, IAwaitable
{
// メンバ変数定義
private TResult result;
///
/// 待機した結果の値を、事前に設定します。
///
/// 準備する結果
public void PrepareResult(TResult result)
{
// 結果を覚えておく
this.result = result;
}
///
/// 待機結果を設定してから、待機ハンドラをシグナル状態にして、待機オブジェクトの待機を解除します。
/// また Reset() を呼び出さない限り、ずっと待機されない状態になります。
/// 再び、待機状態にさせるには Reset() を呼び出して下さい。
///
/// 待機した結果として設定する値
/// 待機ハンドルは解放済みです
///
public void Set(TResult result)
{
// 結果を設定して基本クラスのSetを呼ぶ
this.result = result;
base.Set();
}
///
/// このオブジェクトの待機オブジェクトを取得します
///
/// 待機ハンドルは解放済みです
/// 待機オブジェクトを返します
public new ImtAwaiter GetAwaiter()
{
// 解放済み例外の処理をしておく
ThrowIfDisposed();
// 単純なAwaiterを返す
return new ImtAwaiter(this);
}
///
/// タスクの待機結果を取得します
///
/// 待機結果を返します
public TResult GetResult()
{
// 結果をそのまま返す
return result;
}
}
#endregion
#region AwaitableFromEvent
///
/// イベント機構、コールバック機構のコードを、待機可能なコードに変換する待機可能なクラスです。
///
/// イベント または コールバック で使用する関数のシグネチャを示す型
/// イベント または コールバック または オブジェクト状態 で得られた結果の型
public class ImtAwaitableFromEvent : ImtAwaitable
{
// メンバ変数定義
private Func isCompleted;
private Action register;
private Action unregister;
private TEventDelegate handler;
private bool completeState;
private bool autoReset;
private TResult result;
///
/// タスクが完了しているかどうか
///
public override bool IsCompleted
{
get { return isCompleted != null ? isCompleted() : completeState; }
protected set { completeState = value; }
}
///
/// ImtAwaitableFromEvent のインスタンスを初期化します。
///
///
/// completed を null に指定子た場合は、待機オブジェクトの完了状態が内部で保持するようになりますが、改めて
/// 待機し直す場合は、状態をリセットする必要がありますので、その場合は ResetCompleteState() 関数を呼び出してください。
/// または autoReset パラメータに true を設定すれば継続処理直後に自動的に解除されます。
///
/// 待機オブジェクトが、タスクの完了を扱うための関数。内部の完了状態を利用する場合は null の指定が可能です
/// 内部の完了状態を利用する場合に、イベント完了後に自動的にリセットするかどうか
/// 待機オブジェクト内部の継続関数を、イベントハンドラから呼び出せるようにするための変換関数
/// 実際のイベントに登録するための関数
/// 実際のイベントから登録を解除するための関数
///
public ImtAwaitableFromEvent(Func completed, bool autoReset, Func, TEventDelegate> convert, Action eventRegister, Action eventUnregister)
{
// ユーザー関数を覚えるのと、イベントハンドラを作る
isCompleted = completed;
register = eventRegister;
unregister = eventUnregister;
handler = convert(OnEventHandle);
// 待機状態の初期化と自動リセットの値を受け取る
IsCompleted = false;
this.autoReset = autoReset;
// イベントハンドラの登録
register(handler);
}
///
/// 内部の完了状態をリセットし、再び待機可能な状態にします。
/// しかし、コンストラクタで completed パラメータに渡している関数が
/// 非シグナル状態を返し続けてしまう場合はリセットが出来ません。
///
public void ResetCompleteState()
{
// もし既に非シグナル状態なら
if (!IsCompleted)
{
// 既にリセット済み状態のため終了
return;
}
// 状態をリセットしてハンドラを登録する
IsCompleted = false;
register(handler);
}
///
/// イベント または コールバック で得られた結果を取得します
///
/// イベント または コールバック で得られた結果を返します
public override TResult GetResult()
{
// 得た結果を返す
return result;
}
///
/// イベント または コールバック のハンドリングを行います。
///
/// イベント または コールバック からの結果
private void OnEventHandle(TResult result)
{
try
{
// イベントハンドラの解除をして結果を保存
unregister(handler);
this.result = result;
// 待機オブジェクトハンドラのシグナルを設定
SetSignalWithCompleted();
// もし自動リセットがONなら
if (autoReset)
{
// リセットする
ResetCompleteState();
}
}
catch (Exception exception)
{
// もしイベントハンドラ解除や、自動リセット時のリセット状態に
// 問題が発生したら無条件にエラー設定をして直ちにシグナルを設定する
SetException(exception);
SetSignalWithCompleted();
return;
}
}
}
#endregion
#region AwaitableUpdateBehaviourScheduler
// TODO : もはやこれは只のオレオレTaskだな?(将来的に程よいTaskを検討)
///
/// ImtAwaitableUpdateBehaviour の実行環境をスケジュールするスケジューラクラスです
///
public abstract class ImtAwaitableUpdateBehaviourScheduler
{
///
/// スレッドプールを使った ImtAwaitableUpdateBehaviour のスケジュールを行うクラスです
///
private class ThreadPoolUpdateBehaviourScheduler : ImtAwaitableUpdateBehaviourScheduler
{
// クラス変数宣言
private static readonly WaitCallback cache = new WaitCallback(behaviour => InternalUpdate((ImtAwaitableUpdateBehaviour)behaviour));
///
/// 指定された ImtAwaitableUpdateBehaviour をスレッドプール上にスケジュールします
///
/// スケジュールする ImtAwaitableUpdateBehaviour
protected internal override void ScheduleBehaviour(ImtAwaitableUpdateBehaviour behaviour)
{
// スレッドプールに内部更新関数をスケジュールして引数に behaviour を渡す
ThreadPool.QueueUserWorkItem(cache, behaviour);
}
///
/// ImtAwaitableUpdateBehaviour を更新するための内部状態更新関数です
///
/// 更新対象の ImtAwaitableUpdateBehaviour
private static void InternalUpdate(ImtAwaitableUpdateBehaviour behaviour)
{
try
{
// 開始処理を呼ぶ
behaviour.Start();
// 強制停止がOFFかつ継続を返却され続ける間ループ
while (!forceShutdown && behaviour.Update())
{
// 休ませる
Thread.Sleep(0);
}
// 停止処理を呼ぶ
behaviour.InternalStop();
}
catch (Exception exception)
{
// エラーが発生したことを設定してシグナルを強制的に設定する
behaviour.SetException(exception);
behaviour.SetSignalWithCompleted();
return;
}
}
}
///
/// 同期コンテキストを使った ImtAwaitableUpdateBehaviour のスケジュールを行うクラスです
///
private class SynchronizationContextUpdateBehaviourScheduler : ImtAwaitableUpdateBehaviourScheduler
{
///
/// 同期コンテキストが処理するべきパラメータを保持するクラスです
///
private class UpdateTargetParameter
{
///
/// 担当している同期コンテキスト
///
public SynchronizationContext context;
///
/// 処理するべき ImtAwaitableUpdateBehaviour
///
public ImtAwaitableUpdateBehaviour behaviour;
}
// クラス変数宣言
private static readonly SendOrPostCallback wakeupCache = new SendOrPostCallback(target => InternalWakeup((UpdateTargetParameter)target));
private static readonly SendOrPostCallback updateCache = new SendOrPostCallback(target => InternalUpdate((UpdateTargetParameter)target));
private SynchronizationContext currentContext;
///
/// SynchronizationContextUpdateBehaviourScheduler のインスタンスを初期化します
///
internal SynchronizationContextUpdateBehaviourScheduler()
{
// 現在の同期コンテキストを拾う
currentContext = AsyncOperationManager.SynchronizationContext;
}
///
/// 指定された ImtAwaitableUpdateBehaviour を同期コンテキスト上にスケジュールします
///
/// スケジュールする ImtAwaitableUpdateBehaviour
protected internal override void ScheduleBehaviour(ImtAwaitableUpdateBehaviour behaviour)
{
// 更新すべきパラメータの初期化をする
var parameter = new UpdateTargetParameter()
{
context = currentContext,
behaviour = behaviour,
};
// コンテキストに起動関数をポストする
currentContext.Post(wakeupCache, parameter);
}
///
/// ImtAwaitableUpdateBehaviour を起動するための内部起動関数です
///
/// 更新対象のパラメータ
private static void InternalWakeup(UpdateTargetParameter targetParameter)
{
// 更新待機オブジェクトとコンテキストの取得
var behaviour = targetParameter.behaviour;
var context = targetParameter.context;
try
{
// 開始処理を呼ぶ
behaviour.Start();
// 強制停止フラグはOFFまたは、継続を返却されたら
if (!forceShutdown && behaviour.Update())
{
// 内部更新関数をポストする
context.Post(updateCache, targetParameter);
return;
}
// 停止処理を呼ぶ
behaviour.InternalStop();
}
catch (Exception exception)
{
// エラーが発生したことを設定してシグナルを強制的に設定する
behaviour.SetException(exception);
behaviour.SetSignalWithCompleted();
return;
}
}
///
/// ImtAwaitableUpdateBehaviour を更新するための内部状態更新関数です
///
/// 更新対象のパラメータ
private static void InternalUpdate(UpdateTargetParameter targetParameter)
{
// 更新待機オブジェクトとコンテキストの取得
var behaviour = targetParameter.behaviour;
var context = targetParameter.context;
try
{
// 強制停止フラグはOFFまたは、継続を返却されたら
if (!forceShutdown && behaviour.Update())
{
// ふたたび更新関数をポストする
context.Post(updateCache, targetParameter);
return;
}
// 停止処理を呼ぶ
behaviour.InternalStop();
}
catch (Exception exception)
{
// エラーが発生したことを設定してシグナルを強制的に設定する
behaviour.SetException(exception);
behaviour.SetSignalWithCompleted();
return;
}
}
}
// クラス変数宣言
private static readonly ImtAwaitableUpdateBehaviourScheduler threadPoolScheduler = new ThreadPoolUpdateBehaviourScheduler();
private static ImtAwaitableUpdateBehaviourScheduler currentScheduler;
private static bool forceShutdown;
///
/// デフォルトスケジューラ
///
public static ImtAwaitableUpdateBehaviourScheduler DefaultScheduler => GetCurrentSynchronizationContextScheduler();
///
/// 現在設定されているスケジューラ または デフォルト。
/// このプロパティは SetScheduler() 関数によって設定された内容を返却しますが null になる場合は、デフォルトスケジューラを取り出します。
///
public static ImtAwaitableUpdateBehaviourScheduler CurrentOrDefault => currentScheduler ?? DefaultScheduler;
///
/// 指定された ImtAwaitableUpdateBehaviour をスケジュールします
///
/// スケジュールする ImtAwaitableUpdateBehaviour
protected internal abstract void ScheduleBehaviour(ImtAwaitableUpdateBehaviour behaviour);
///
/// スレッドプールを使ったスケジューラを取得します
///
/// スレッドプールを使ったスケジューラのインスタンスを返します
public static ImtAwaitableUpdateBehaviourScheduler GetThreadPoolScheduler()
{
// 生成済みのスケジューラを渡す
return threadPoolScheduler;
}
///
/// 現在の同期コンテキストを使ったスケジューラを取得します
///
/// 現在の同期コンテキストを使ったスケジューラのインスタンスを返します
public static ImtAwaitableUpdateBehaviourScheduler GetCurrentSynchronizationContextScheduler()
{
// 同期コンテキストスケジューラを生成して返す
return new SynchronizationContextUpdateBehaviourScheduler();
}
///
/// 現在のスケジューラを、指定されたスケジューラで設定します。
///
/// 設定するスケジューラを渡しますが null が設定された場合は、内部の既定スケジューラが使われるようになります
public static void SetScheduler(ImtAwaitableUpdateBehaviourScheduler scheduler)
{
// 素直に受け取る
currentScheduler = scheduler;
}
///
/// あらゆるスケジューラの動作を停止させます。
/// また、この関数は一時的なもので、実装の変更が入る恐れがあります。
///
public static void ForceShutdown()
{
// 強制停止フラグを立てる
// TODO : 本来なら、スケジューラでループするのではなく、別の場所での停止ハンドリング(CancellationTokenなど)で出来るようにするべき
forceShutdown = true;
}
}
#endregion
#region AwaitableUpdateBehaviour
// TODO : もはやこれは只のオレオレTaskだな?(将来的に程よいTaskを検討)
///
/// 自己更新が可能な、更新待機可能クラスです。
///
public abstract class ImtAwaitableUpdateBehaviour : ImtAwaitable
{
///
/// 更新処理が起動中かどうか。
/// この値は Awaitable の IsCompleted とは関係ありません
///
public bool IsRunning { get; protected set; }
///
/// 現在のスケジューラを用いて、状態更新を開始します。
///
/// この更新待機クラスは起動中です
/// 起動を開始した自身を返します
public ImtAwaitableUpdateBehaviour Run()
{
// 現在設定されているスケジューラを用いてスケジュールする
return Run(ImtAwaitableUpdateBehaviourScheduler.CurrentOrDefault);
}
///
/// 指定されたスケジューラにて、状態更新を開始します。
///
/// この、更新待機クラスが実行される環境を提供するスケジューラ
/// この更新待機クラスは起動中です
/// 起動を開始した自身を返します
public ImtAwaitableUpdateBehaviour Run(ImtAwaitableUpdateBehaviourScheduler scheduler)
{
// 既に起動中なら
if (IsRunning)
{
// もう起動済みです
throw new InvalidOperationException($"この更新待機クラスは起動中です");
}
// スケジューリングしてもらって起動状態にして自身を返す
scheduler.ScheduleBehaviour(this);
IsRunning = true;
return this;
}
///
/// 現在のスケジューラを用いて、状態更新を開始します。
///
/// 実際に起動する更新待機クラスの型
/// この更新待機クラスは起動中です
/// 起動を開始した自身を返します
public T Run() where T : ImtAwaitableUpdateBehaviour
{
// 通常の起動関数を叩く
return (T)Run();
}
///
/// 指定されたスケジューラにて、状態更新を開始します。
///
/// 実際に起動する更新待機クラスの型
/// この、更新待機クラスが実行される環境を提供するスケジューラ
/// この更新待機クラスは起動中です
/// 起動を開始した自身を返します
public T Run(ImtAwaitableUpdateBehaviourScheduler scheduler) where T : ImtAwaitableUpdateBehaviour
{
// 通常の起動関数を叩く
return (T)Run(scheduler);
}
///
/// 更新の開始処理を行います
///
protected internal virtual void Start()
{
}
///
/// 状態の更新処理を行います
///
/// 更新を継続する場合は true を、更新を停止する場合は false を返します
protected internal virtual bool Update()
{
// 既定動作は直ちに終了
return false;
}
///
/// 更新の終了処理を行います
///
protected virtual void Stop()
{
}
///
/// 更新の終了処理を実行します
///
internal void InternalStop()
{
// 停止関数をたたいて停止状態にする
Stop();
IsRunning = false;
}
}
///
/// 自己更新が可能な、値を返す事ができる待機可能クラスです。
///
/// 返す値の型
public abstract class ImtAwaitableUpdateBehaviour : ImtAwaitableUpdateBehaviour, IAwaitable
{
///
/// タスクの結果を取得します
///
/// タスクの結果を返します
public abstract TResult GetResult();
///
/// この待機可能クラスの、待機オブジェクトを取得します
///
/// 待機オブジェクトを返します
public new ImtAwaiter GetAwaiter()
{
// 待機オブジェクトを生成して返す
return new ImtAwaiter(this);
}
}
#endregion
#region Task
// TODO : もはやこれは只のオレオレTaskだな?(将来的に程よいTaskを検討)
///
/// 非同期で動作するタスクを提供する待機可能クラスです
///
public class ImtTask : ImtAwaitableUpdateBehaviour
{
// メンバ変数定義
protected Delegate work;
protected object status;
protected Task asyncWorker;
///
/// 指定された作業を行うための ImtTask のインスタンスを初期化します。
///
/// 作業を行う関数の内容
/// work が null です
public ImtTask(Action