--- nono/vm/scheduler.h 2026/04/29 17:04:28 1.1 +++ nono/vm/scheduler.h 2026/04/29 17:06:01 1.1.1.17 @@ -1,123 +1,118 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// スケジューラ // #pragma once -#include +#include "thread.h" +#include "message.h" +#include "spscqueue.h" +#include #include #include -#include -#include "device.h" -#include "event.h" -#include "mpu.h" + +class EventManager; +class Syncer; // スケジューラ -class Scheduler : public Device +class Scheduler : public ThreadDevice { - typedef Device inherited; + using inherited = ThreadDevice; + private: // リクエストフラグ - static const uint REQ_SYNC = 0x0001; // スケジューラを同期モードに - static const uint REQ_FAST = 0x0002; // スケジューラを高速モードに - static const uint REQ_RUN = 0x0004; // MPU が通常状態になった - static const uint REQ_STOP = 0x0008; // MPU が STOP 状態になった - static const uint REQ_MODEMASK = 0x000f; // mode マスク - static const uint REQ_POWER_ON = 0x0010; // 電源オン - static const uint REQ_POWER_OFF = 0x0020; // 電源オフ - static const uint REQ_EXIT = 0x0040; // アプリケーション終了 - - // 動作モード - // 動作モード変数 (mode) は高速モードか通常モードかと、MPU が通常状態が - // STOP 状態かを同時に保持している。高速走行中であっても MPU が STOP 状態 - // なら通常走行するというロジックを比較1回で済ませるため。 - static const uint32 SCHED_STOP = 0x0001; // スケジューラが STOP 状態 - static const uint32 SCHED_SYNC = 0x0002; // 同期(%1)/高速(%0)モード - // 演算を1回で済ませるために、同じ値にすること - static_assert(SCHED_STOP == CPU_REQ_STOP, "must be the same"); + static const uint32 REQUEST_EXIT = 0x00000001; // 終了要求 + static const uint32 REQUEST_MESSAGE = 1U << MessageID::MAX_REQUEST; + + // メッセージハンドラを覚えておくための構造体 + struct MessageHandler + { + Device *dev {}; + MessageCallback_t func {}; + }; public: Scheduler(); - virtual ~Scheduler(); - - virtual bool Init(); + ~Scheduler() override; - virtual bool MonitorUpdate(); + bool Init() override; + bool Init2(); - // スレッド開始 - void ThreadRun(); + void StartTime(); // スレッド終了指示 - void Terminate(); + void Terminate() override; - // スケジューラを開始 (電源オン時に vm から呼ばれる) - void Run(); + // 現在のマスター仮想時間を返す + uint64 GetVirtTime() const { return vtime; } - // 電源オフ要求 - void RequestPowerOff() { - atomic_reqflag |= REQ_POWER_OFF; - } - - // サイクル数を仮想時間 [nsec] に変換 - uint64 Cycle2Vtime(uint64 cycle) const { - return cycle * 1000 * 1000 / mpu_clock; - } - - // 仮想時間 [nsec] をサイクル数に変換 - uint64 Vtime2Cycle(uint64 vtime) const { - return vtime * mpu_clock / (1000 * 1000); - } - - // 仮想時刻取得 - uint64 GetVirtTime() const { - return Cycle2Vtime(gMPU->total_cycle()); - } - - // スケジューラが高速モードなら true を返す。 - // ここでいう高速モードは STOP 状態中でも高速モード。 - bool GetFullSpeed() const { return ((mode & SCHED_SYNC) == 0); } - - // スケジューラの動作モードを設定する。 - // true なら高速モード、false なら同期モード。 - void SetFullSpeed(bool enable); - - // イベントを登録する - void AddEvent(Event *newev); - // イベントを削除する - void DelEvent(Event *event); - - std::list eventlist {}; // 有効なイベントリスト - std::mutex evcs {}; // イベントリストのロック - - // 状態変更要求 - // スケジューラループの状態を変更したい場合はこのフラグを立てる。 - // REQ_MODE_* の相反するビット以外は同時に複数立ててもいいはず。 - // 処理されると再び 0 になる。 - std::atomic atomic_reqflag {}; - std::condition_variable cv {}; - std::mutex cvmtx {}; + // イベントを開始する + void StartEvent(Event *ev); + void RestartEvent(Event *ev); + + // 実時間間隔を指定してイベントを開始する。 + // rt_now はイベント発行者の実時間での現在時刻で、 + // rt_period は次回のイベントまでの実時間間隔。 + void StartRealtimeEvent(Event *ev, uint64 rt_now, uint64 rt_period); + + // イベントを停止する + void StopEvent(Event *ev); + + // メッセージハンドラを登録する + void ConnectMessage(MessageID, Device *, MessageCallback_t); + + // メッセージを送る。VM スレッド以外からも呼び出して良い。 + void SendMessage(MessageID, uint32 arg = 0); + + // 指定時間が経過するか、リクエストが起きるまでスリープ + void Sleep(uint64 time); private: - // スケジューラの動作モード。SCHED_* - uint32 mode = 0; + // スレッド + void ThreadRun() override; - int mpu_clock = 0; // MPU クロック [kHz] + void EnqueueSlow(Event *); + void PushSlow(Event *); + void StopSlowEvent(Event *); + + // メッセージディスパッチ + void DispatchMessage(); + void InvokeMessage(MessageID, uint32); + + DECLARE_MONITOR_SCREEN(MonitorScreen); + + // 有効なイベントキュー + uint64 slow_top_vtime {}; + Event *fast {}; + int slow_top {}; + std::array slow {}; + + // 現在の仮想経過時間 + uint64 vtime {}; + + // 外部スレッドからのリクエスト + uint32 request {}; + std::mutex mtx {}; + std::condition_variable cv {}; - uint64 rtimestart = 0; // スケジューラ開始実時刻 - uint64 rtimebase = 0; // 実時間の基準時刻 - uint64 vtimebase = 0; // 仮想時間の基準時刻 + // メッセージハンドラリスト + std::array message_handlers {}; - // 前回 RTC へ 32Hz 入力パルスを入れた時刻 (実時間の絶対時刻系) - uint64 rtc_last_clock = 0; -}; + // メッセージキュー + SPSCQueue msgq {}; -extern Scheduler *gScheduler; + EventManager *evman {}; + Syncer *syncer {}; -// 現在時刻 [nsec] を取得する -static inline uint64 GetRealTime() -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64)(tv.tv_sec * 1000 * 1000 + tv.tv_usec) * 1000; + Monitor *monitor {}; +}; + +inline Scheduler *GetScheduler() { + return Object::GetObject(OBJ_SCHEDULER); }