// 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 work) : this((Delegate)work, null) { } /// /// 指定された作業を行うための ImtTask のインスタンスを初期化します。 /// /// 作業を行う関数の内容 /// work に渡す状態オブジェクト /// work が null です public ImtTask(Action work, object status) : this((Delegate)work, status) { } /// /// 指定された作業を非同期で行うための ImtTask のインスタンスを初期化します。 /// /// 非同期で作業を行う関数の内容 /// work が null です public ImtTask(Func work) : this((Delegate)work, null) { } /// /// 指定された作業を行うための ImtTask のインスタンスを初期化します。 /// /// 作業を行う関数 /// work に渡す状態オブジェクト /// work が null です internal ImtTask(Delegate work, object status) { // 呼び出すべき作業関数がnullなら if (work == null) { // 何をすればよいのですか throw new ArgumentNullException(nameof(work)); } // 呼び出すべき作業関数と状態を設定 this.work = work; this.status = status; } /// /// タスクの処理を行います /// protected internal override void Start() { // TODO : Unityバージョンが上がったらパターンマッチを検討 // 型によって呼び出しの仕方を変える if (work is Action) { // ただの Action なら、タスクを処理して状態を更新 ((Action)work)(status); } else if (work is Func) { // 非同期操作 Func なら、タスクを拾って状態を更新開始 asyncWorker = ((Func)work)(); } } /// /// 状態の更新処理を行います /// /// 更新を継続する場合は true を、更新を停止する場合は false を返します protected internal override bool Update() { // 非同期操作中タスクがないなら if (asyncWorker == null) { // 直ちに終了する return false; } // タスクの完了状態が未完了の間はUpdateし続ける return !asyncWorker.IsCompleted; } /// /// 待機オブジェクトにシグナルを設定します /// protected override void Stop() { // もし非同期操作タスクとして動作して、かつ例外が発生していたのならのなら if (asyncWorker != null && asyncWorker.Exception != null) { // 非同期操作に例外が発生していたとしてシグナルを設定する SetException(asyncWorker.Exception); SetSignalWithCompleted(); } // シグナルを設定する SetSignalWithCompleted(); } } /// /// 非同期で動作するタスクを提供する、値の返却が可能な待機可能クラスです /// public class ImtTask : ImtTask, IAwaitable { // メンバ変数定義 private TResult result; /// /// 指定された作業を行うための ImtTask のインスタンスを初期化します。 /// /// 作業を行う関数の内容 /// work が null です public ImtTask(Func work) : base(work, null) { } /// /// 指定された作業を行うための ImtTask のインスタンスを初期化します。 /// /// 作業を行う関数の内容 /// work に渡す状態オブジェクト /// work が null です public ImtTask(Func work, object status) : base(work, status) { } /// /// 指定された作業を行うための ImtTask のインスタンスを初期化します。 /// /// 作業を行う関数の内容 /// work が null です public ImtTask(Func> work) : base(work, null) { } /// /// 現在のスケジューラを用いて、タスクを開始します。 /// /// このタスクは起動中です /// 起動を開始した自身を返します public new ImtTask Run() { // 現在設定されているスケジューラを用いてスケジュールする return Run>(ImtAwaitableUpdateBehaviourScheduler.CurrentOrDefault); } /// /// 指定されたスケジューラにて、タスクを開始します。 /// /// この、タスクが実行される環境を提供するスケジューラ /// このタスクは起動中です /// 起動を開始した自身を返します public new ImtTask Run(ImtAwaitableUpdateBehaviourScheduler scheduler) { // 指定されたスケジューラを用いてスケジュールする return Run>(scheduler); } /// /// タスクの処理を行います /// protected internal override void Start() { // TODO : Unityバージョンが上がったらパターンマッチを検討 // 型によって呼び出しの仕方を変える if (work is Func) { // ただの Func なら、タスクを処理して状態を更新 result = ((Func)work)(status); } else if (work is Func>) { // 非同期操作 Func> なら、タスクを拾って状態を更新開始 asyncWorker = ((Func>)work)(); } } /// /// この待機クラスの、待機オブジェクトを取得します /// /// 待機オブジェクトを返します public new ImtAwaiter GetAwaiter() { // 待機オブジェクトを生成して返す return new ImtAwaiter(this); } /// /// タスクの結果を取得します /// /// タスクの結果を返します public TResult GetResult() { // もし非同期操作タスクが存在していたのなら(例外は基本クラスのStopで収集) if (asyncWorker != null) { // 結果は非同期操作タスクから返す return ((Task)asyncWorker).Result; } // 結果を返す return result; } } #endregion #region AwaitableUtility /// /// IAwaitable なオブジェクトを待機する時のヘルパー実装を提供します /// public class ImtAwaitableHelper { /// /// 全ての待機オブジェクトを待機する、待機クラスです。 /// private class AwaitableWhenAll : ImtAwaitableUpdateBehaviour { // メンバ変数定義 private ImtAwaitableHelper helper; /// /// AwaitableWhenAll のインスタンスを初期化します /// /// このインスタンスを保持する ImtAwaitHelper public AwaitableWhenAll(ImtAwaitableHelper helper) { // ヘルパーを覚えて、初期状態は完了状態にしておく this.helper = helper; IsCompleted = true; } /// /// 完了状態をリセットします /// public void Reset() { // 未完了状態にする IsCompleted = false; } /// /// 内部の状態更新をします。 /// protected internal override bool Update() { // まだ未完了 IsCompleted = false; // そもそもリストが空なら if (helper.awaitableList.Count == 0) { // 完了状態にして、待機中のオブジェクトの待機を解除 SetSignalWithCompleted(); return true; } // ヘルパーが持っている待機オブジェクトの数分ループ bool isAllFinish = true; foreach (var awaitable in helper.awaitableList) { // もし未完了状態なら if (!awaitable.IsCompleted) { // 全て完了フラグをへし折ってループから抜ける isAllFinish = false; break; } } // もし全ての待機オブジェクトが完了状態なら if (isAllFinish) { // もうリストを空にする helper.awaitableList.Clear(); // 完了状態にして、待機中のオブジェクトの待機を解除 SetSignalWithCompleted(); return false; } // まだ継続動作する return true; } } /// /// いずれかの待機オブジェクトを待機する、待機クラスです /// private class AwaitableWhenAny : ImtAwaitableUpdateBehaviour { // メンバ変数定義 private ImtAwaitableHelper helper; private IAwaitable firstFinishAwaitable; /// /// AwaitableWhenAny のインスタンスを初期化します /// /// このインスタンスを保持する ImtAwaitHelper public AwaitableWhenAny(ImtAwaitableHelper helper) { // ヘルパーを覚えて、初期状態は完了状態にしておく this.helper = helper; IsCompleted = true; } /// /// 完了状態をリセットします /// public void Reset() { // 未完了状態にする IsCompleted = false; } /// /// 内部の状態更新をします。 /// protected internal override bool Update() { // まだ未完了 IsCompleted = false; // そもそもリストが空なら if (helper.awaitableList.Count == 0) { // 完了状態にして、待機中のオブジェクトの待機を解除 SetSignalWithCompleted(); return false; } // ヘルパーが持っている待機オブジェクトの数分ループ firstFinishAwaitable = null; foreach (var awaitable in helper.awaitableList) { // もし完了状態なら if (awaitable.IsCompleted) { // 最初に完了した待機オブジェクトとして覚えて抜ける firstFinishAwaitable = awaitable; break; } } // もし完了オブジェクトが見つからなかったら if (firstFinishAwaitable == null) { // 継続動作する return true; } // リストから完了オブジェクトを削除 helper.awaitableList.Remove(firstFinishAwaitable); // 完了タスクを1つ目を見つけたので、シグナルを送る SetSignal(); // もしリストが空になったのなら if (helper.awaitableList.Count == 0) { // 完了状態にして終了 IsCompleted = true; return false; } // 継続して更新する return true; } /// /// タスクの結果を取得します /// /// タスクの結果を返します public override IAwaitable GetResult() { // 最初に完了した待機オブジェクトを返す return firstFinishAwaitable; } } // メンバ変数定義 private AwaitableWhenAll whenAllOperator; private AwaitableWhenAny whenAnyOperator; private List awaitableList; /// /// ImtAwaitHelper のインスタンスを初期化します /// public ImtAwaitableHelper() { // 各種待機オブジェクトの生成をする whenAllOperator = new AwaitableWhenAll(this); whenAnyOperator = new AwaitableWhenAny(this); awaitableList = new List(); } /// /// 待機ヘルパに待機オブジェクトを追加します。 /// 既に追加済みの待機オブジェクトは無視されます。 /// /// 追加する待機オブジェクト public void AddAwaitable(IAwaitable awaitable) { // 既に追加済みなら if (awaitableList.Contains(awaitable)) { // 何もせず終了 return; } // 待機オブジェクトを追加する awaitableList.Add(awaitable); } /// /// 待機ヘルパに待機オブジェクトを追加します。 /// 既に追加済みの待機オブジェクトは無視されます。 /// /// 追加する待機オブジェクトの IList /// awaitables が null です public void AddRange(IList awaitables) { // awaitablesがnullなら if (awaitables == null) { // 何を追加するんですか throw new ArgumentNullException(nameof(awaitables)); } // リスト内をすべて回る foreach (var awaitable in awaitables) { // 追加する AddAwaitable(awaitable); } } /// /// 待機ヘルパに待機オブジェクトを追加します。 /// 既に追加済みの待機オブジェクトは無視されます。 /// /// 追加する待機オブジェクトの IList /// awaitables が null です public void AddRange(IList> awaitables) { // awaitablesがnullなら if (awaitables == null) { // 何を追加するんですか throw new ArgumentNullException(nameof(awaitables)); } // リスト内をすべて回る foreach (var awaitable in awaitables) { // 追加する AddAwaitable(awaitable); } } /// /// 追加された待機オブジェクトの全てが、完了するまで待機する、待機オブジェクトを提供します。 /// /// 追加された待機オブジェクトの全てを待機する、待機オブジェクトを返します public IAwaitable WhenAll() { // 何も追加しない引数で WhenAll を叩く return WhenAll(null); } /// /// 追加された待機オブジェクトと、引数に指定された待機オブジェクト配列の中を更に追加して /// 全てが完了するまで待機する、待機オブジェクトを提供します。 /// /// 追加する待機オブジェクトの配列。追加しない場合は null の指定が可能です /// 追加された待機オブジェクトの全てを待機する、待機オブジェクトを返します public IAwaitable WhenAll(IList awaitables) { // もし配列の指定があるなら if (awaitables != null) { // 追加する AddRange(awaitables); } // WhenAllを実行する return DoWhenAll(); } /// /// 追加された待機オブジェクトと、引数に指定された待機オブジェクト配列の中を更に追加して /// 全てが完了するまで待機する、待機オブジェクトを提供します。 /// /// 待機可能クラスの返却する値の型 /// 追加する待機オブジェクトの配列。追加しない場合は null の指定が可能です /// 追加された待機オブジェクトの全てを待機する、待機オブジェクトを返します public IAwaitable WhenAll(IList> awaitables) { // もし配列の指定があるなら if (awaitables != null) { // 追加する AddRange(awaitables); } // WhenAllを実行する return DoWhenAll(); } /// /// WhenAllを起動します /// /// WhenAllの待機可能クラスのインスタンスを返します private IAwaitable DoWhenAll() { // WhenAll制御が未起動状態なら if (!whenAllOperator.IsRunning) { // 起動する whenAllOperator.Reset(); whenAllOperator.Run(); } // WhenAll待機オブジェクトを返す return whenAllOperator; } /// /// 追加された待機オブジェクトのいずれかを、完了するまで待機する、待機オブジェクトを提供します。 /// /// 追加された待機オブジェクトのいずれかを、完了するまで待機する、待機オブジェクトを返します public IAwaitable WhenAny() { // 何も追加しない引数で WhenAny を叩く return WhenAny(null); } /// /// 追加された待機オブジェクトのいずれかを、完了するまで待機する、待機オブジェクトを提供します。 /// また、引数で指定された待機オブジェクトの配列内も追加します。 /// /// 追加する待機オブジェクトの配列。追加しない場合は null の指定が可能です /// 追加された待機オブジェクトのいずれかを、完了するまで待機する、待機オブジェクトを返します public IAwaitable WhenAny(IList awaitables) { // もし配列の指定があるなら if (awaitables != null) { // 追加する AddRange(awaitables); } // WhenAnyを実行する return DoWhenAny(); } /// /// 追加された待機オブジェクトのいずれかを、完了するまで待機する、待機オブジェクトを提供します。 /// また、引数で指定された待機オブジェクトの配列内も追加します。 /// /// 待機可能クラスの返却する値の型 /// 追加する待機オブジェクトの配列。追加しない場合は null の指定が可能です /// 追加された待機オブジェクトのいずれかを、完了するまで待機する、待機オブジェクトを返します public IAwaitable WhenAny(IList> awaitables) { // もし配列の指定があるなら if (awaitables != null) { // 追加する AddRange(awaitables); } // WhenAnyを実行する return DoWhenAny(); } /// /// WhenAnyを起動します /// /// WhenAnyの待機可能クラスのインスタンスを返します private IAwaitable DoWhenAny() { // WhenAny制御が未起動状態なら if (!whenAnyOperator.IsRunning) { // 起動する whenAnyOperator.Reset(); whenAnyOperator.Run(); } // WheAny待機オブジェクトを返す return whenAnyOperator; } } #endregion #region Extensions /// /// SynchronizationContext クラスの拡張関数実装用クラスです /// public static class SynchronizationContextExtentions { // 読み取り専用クラス変数宣言 private static readonly SendOrPostCallback CachedSendOrPostCallback = new SendOrPostCallback(_ => ((Action)_)()); /// /// 同期コンテキストに高速な Action コールバックをポストします /// /// ポスト先の動機コンテキスト /// ポストするコールバック public static void Post(this SynchronizationContext context, Action action) { // キャッシュ済みコールバックを用いてActionの参照を渡す context.Post(CachedSendOrPostCallback, action); } /// /// 同期コンテキストに高速な Action コールバックを送信します /// /// 送信先の動機コンテキスト /// 送信するコールバック public static void Send(this SynchronizationContext context, Action action) { // キャッシュ済みコールバックを用いてActionの参照を渡す context.Send(CachedSendOrPostCallback, action); } } /// /// AwaitableやAwaiterなどの拡張関数実装用クラスです /// public static class AwaitableAwaiterExtensions { /// /// 複数の IAwaitable を、すべて待機するための待機可能なインスタンスを生成します。 /// /// 待機する複数の IAwaitable /// 生成された、すべての IAwaitable を待機する待機可能なインスタンスを返します public static IAwaitable WhenAll(this IList awaitables) { // WhenAllとして返す return new ImtAwaitableHelper().WhenAll(awaitables); } /// /// 複数の IAwaitable を、すべて待機するための待機可能なインスタンスを生成します。 /// /// 待機可能クラスの返却する値の型 /// 待機する複数の IAwaitable /// 生成された、すべての IAwaitable を待機する待機可能なインスタンスを返します public static IAwaitable WhenAll(this IList> awaitables) { // WhenAllとして返す return new ImtAwaitableHelper().WhenAll(awaitables); } /// /// 複数の IAwaitable の、いずれかを待機するための待機可能なインスタンスを生成します。 /// /// 待機する複数の IAwaitable /// 生成された、いずれかの IAwaitable を待機する待機可能なインスタンスを返します public static IAwaitable WhenAny(this IList awaitables) { // WhenAnyとして返す return new ImtAwaitableHelper().WhenAny(awaitables); } /// /// 複数の IAwaitable の、いずれかを待機するための待機可能なインスタンスを生成します。 /// /// 待機する複数の IAwaitable /// 生成された、いずれかの IAwaitable を待機する待機可能なインスタンスを返します public static IAwaitable WhenAny(this IList> awaitables) { // WhenAnyとして返す return new ImtAwaitableHelper().WhenAny(awaitables); } } #endregion }