Newer
Older
TIASShot / TIASshot / SelfTest / SelfTestPump.cs
#if SELFTEST
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;

namespace TIASshot {

    /// <summary>
    /// UI スレッドのメッセージを回しながら待つ処理(SELFTEST 限定).
    /// SelfTestRunner と TIASshot.Tests の共通ハーネス(層②)の両方から使う.
    /// ワーカースレッドからの Control.Invoke(ShowMessage / EnableShots 等)やタイマーの Tick は
    /// UI スレッドがメッセージを処理しないと実行されないため,Join や Sleep だけで待つと相互待機でハングする.
    /// 必ずフォームを生成したスレッド(STA)から呼ぶこと.
    /// </summary>
    internal static class SelfTestPump {

        // 1 回のメッセージ処理ごとの休止(ms)(CPU を占有しないため.応答性を損なわない短さにする)
        private const int SleepMs = 2;

        /// <summary>
        /// 条件が成立するまでメッセージを処理して待つ.
        /// </summary>
        /// <param name="condition">成立を待つ条件(UI スレッドで評価する)</param>
        /// <param name="timeout">待機の上限</param>
        /// <param name="afterEachPump">メッセージ処理ごとに呼ぶ処理(null 可.UI 例外の再送出等に使う)</param>
        /// <returns>上限内に成立したら true,タイムアウトしたら false</returns>
        public static bool PumpUntil(Func<bool> condition, TimeSpan timeout, Action afterEachPump = null) {
            var sw = Stopwatch.StartNew();
            while (!condition()) {
                if (sw.Elapsed > timeout) return false;
                Application.DoEvents();
                afterEachPump?.Invoke();
                Thread.Sleep(SleepMs);
            }
            return true;
        }

        /// <summary>
        /// 指定時間だけメッセージを処理し続ける(タイマー駆動の表示更新等を反映させるため).
        /// </summary>
        /// <param name="duration">処理し続ける時間</param>
        /// <param name="afterEachPump">メッセージ処理ごとに呼ぶ処理(null 可)</param>
        public static void PumpFor(TimeSpan duration, Action afterEachPump = null) {
            var sw = Stopwatch.StartNew();
            while (sw.Elapsed < duration) {
                Application.DoEvents();
                afterEachPump?.Invoke();
                Thread.Sleep(SleepMs);
            }
        }
    }
}
#endif