--- nono/vm/scheduler.cpp 2026/04/29 17:05:14 1.1.1.15 +++ nono/vm/scheduler.cpp 2026/04/29 17:05:38 1.1.1.19 @@ -12,7 +12,7 @@ // 仮想時間はすべて nsec で表し、StartTime() から単調増加する。 // VM 界からは仮想時間は gScheduler->GetVirtTime() で取得する。 // -// 実時間は Sync が管理し、こちらも nsec で表す。 +// 実時間は Syncer が管理し、こちらも nsec で表す。 // このストップウォッチも StartTime() から単調増加する。 // 実時間、仮想時間とも電源オフ中でも単調増加することに注意。 // @@ -31,9 +31,9 @@ // 経過時間との比で求める。同期走行/高速走行の影響は受けない。 #include "scheduler.h" -#include "mpu.h" +#include "monitor.h" #include "rtc.h" -#include "sync.h" +#include "syncer.h" //#define CT_ON @@ -55,9 +55,6 @@ static uint64 ct_pslow; #define CT(name) #endif -// グローバル参照用 -Scheduler *gScheduler; - #define FORMAT_FULL 0 #define FORMAT_SEC 1 @@ -75,9 +72,9 @@ Scheduler *gScheduler; // FORMAT_SEC は1桁秒以下の場合 (実際には1秒未満の場合) に用いる。 // FORMAT_FULL は 1000日経過すると桁がずれるけど、それはもういいだろう。 // -// 10秒未満の場合だけ %2d ではなく %d で1桁切り詰めているが、これは +// 10秒未満の場合だけ %2u ではなく %u で1桁切り詰めているが、これは // FORMAT_SEC との互換性のため。その必要のない10分未満と10時間未満は -// どちらも %2d で表記し桁数を維持することに努める。 +// どちらも %2u で表記し桁数を維持することに努める。 static const std::string TimeToStrF(uint64 t, int format) { @@ -112,7 +109,7 @@ TimeToStrF(uint64 t, int format) p = buf; len = sizeof(buf); if (d) { - n = snprintf(p, len, "%3dd %02u:%02u:%02u", d, h, m, s); + n = snprintf(p, len, "%3ud %02u:%02u:%02u", d, h, m, s); p += n; len -= n; } else if (h) { @@ -153,7 +150,7 @@ SecToStr(uint64 t) // コンストラクタ Scheduler::Scheduler() - : inherited("Scheduler") + : inherited(OBJ_SCHEDULER) { // オブジェクト名は Scheduler だがここのスレッド名は VM くらいのほうが // 通りがよさそうだ。 @@ -162,24 +159,32 @@ Scheduler::Scheduler() slow_top = -1; slow_top_vtime = UINT64_MAX; - monitor.func = ToMonitorCallback(&Scheduler::MonitorUpdate); + monitor = gMonitorManager->Regist(ID_MONITOR_SCHEDULER, this); + monitor->func = ToMonitorCallback(&Scheduler::MonitorUpdate); // サイズは Init で決まる - monitor.Regist(ID_MONITOR_SCHEDULER); } // デストラクタ Scheduler::~Scheduler() { TerminateThread(); - gScheduler = NULL; } // 初期化 bool Scheduler::Init() { - // この時点でイベントの個数は確定しているはず(してなければならない) - monitor.SetSize(80, 9 + all_events.size()); + syncer = GetSyncer(); + + return true; +} + +// 初期化(VM から呼ばれる) +bool +Scheduler::Init2() +{ + // イベントの個数が確定した(してなければならない) + monitor->SetSize(80, 9 + all_events.size()); return true; } @@ -188,6 +193,8 @@ Scheduler::Init() void Scheduler::ThreadRun() { + SetThreadAffinityHint(AffinityClass::Heavy); + // 電源オンオフに関わらず、ここが rtime, vtime の基準点 StartTime(); @@ -206,8 +213,14 @@ Scheduler::ThreadRun() if ((req & REQUEST_EXIT)) { break; } - if ((req & REQUEST_MESSAGE)) { - DispatchMessage(); + + for (; req; req &= req - 1) { + uint id = __builtin_ctz(req); + if (id < MessageID::MAX_REQUEST) { + InvokeMessage(id, 0); + } else { + DispatchMessage(); + } } } @@ -248,8 +261,8 @@ Scheduler::StartTime() // 時間をリセットする。 // 呼び出しの依存関係に注意。 vtime = 0; // required by Sync - gSync->StartTime(); // required by RTC - gRTC->StartTime(); + syncer->StartTime(); // required by RTC + GetRTCDevice()->StartTime(); } // スレッド終了指示 @@ -297,15 +310,16 @@ Scheduler::StartEvent(Event& ev) CT(start); #ifdef CT_ON if (ct_start % 100'000'000 == 0) { - printf("get*=%lu [%lu, %lu]\n", + printf("get*=%" PRIu64 " [%" PRIu64 ", %" PRIu64 "]\n", (ct_get1 + ct_get2), ct_get1, ct_get2); - printf("start*=%lu [%lu, %lu, %lu, %lu]\n", + printf("start*=%" PRIu64 + " [%" PRIu64 ", %" PRIu64 ", %" PRIu64 ", %" PRIu64 "]\n", ct_start, ct_start1, ct_start2, ct_start3, ct_start4); - printf("stop*=%lu [%lu, %lu]\n", + printf("stop*=%" PRIu64 " [%" PRIu64 ", %" PRIu64 "]\n", (ct_stop1 + ct_stop2), ct_stop1, ct_stop2); - printf("eslow=%lu sslow=%lu pslow=%lu\n", + printf("eslow=%" PRIu64 " sslow=%" PRIu64 " pslow=%" PRIu64 "\n", ct_eslow, ct_sslow, ct_pslow); - printf("slow_top=%d vtime=%lu\n", slow_top, slow_top_vtime); + printf("slow_top=%d vtime=%" PRIu64 "\n", slow_top, slow_top_vtime); } #endif @@ -345,7 +359,7 @@ Scheduler::StartRealtimeEvent(Event& ev, { // 実時間をもとに仮想時間軸上の推定を行う。 - uint64 rtime = gSync->GetRealTime(); + uint64 rtime = syncer->GetRealTime(); if (rtime > rt_now + rt_period) { // 実時間がさらに 1 period 以上進んでいる。 @@ -440,8 +454,11 @@ Scheduler::StopSlowEvent(Event& ev) void Scheduler::ConnectMessage(MessageID msgid, Device *dev, MessageCallback_t func) { + assert(msgid < MessageID::MAX); + auto& mh = message_handlers[msgid]; - assert(mh.dev == NULL); + assertmsg(mh.dev == NULL, + "%s: msgid=%d connected again", __func__, (int)msgid); mh.dev = dev; mh.func = func; } @@ -456,15 +473,30 @@ Scheduler::DispatchMessage() MessageID msgid = (MessageID)(msg & 0xff); uint32 arg = (msg >> 32); - assert(0 <= msgid && msgid < MessageID::MAX); - - auto& mh = message_handlers[msgid]; - if (mh.dev != NULL) { - ((mh.dev)->*(mh.func))(msgid, arg); + // MessageID::MPU_TRACE_ALL だけ特別対応。 + // MPU_TRACE メッセージの宛先は(最大)2つあるが、 + // スレッドを越える時は1メッセージにしておきたいので、 + // MPU_TRACE_ALL が来たらここで分岐。 + if (msgid == MessageID::MPU_TRACE_ALL) { + InvokeMessage(MessageID::MPU_TRACE_MAIN, arg); + InvokeMessage(MessageID::MPU_TRACE_XP, arg); + } else { + assert(msgid < MessageID::MAX); + InvokeMessage(msgid, arg); } } } +// メッセージハンドラを呼び出す。登録されてなければ何もしない。 +void +Scheduler::InvokeMessage(MessageID msgid, uint32 arg) +{ + auto h = message_handlers[msgid]; + if (h.dev != NULL) { + (h.dev->*(h.func))(msgid, arg); + } +} + // メッセージを送る。 // VM スレッド以外から呼び出しても良い。 void @@ -472,11 +504,15 @@ Scheduler::SendMessage(MessageID msgid, { std::unique_lock lock(mtx); - uint64 msg = (uint64)msgid | (((uint64)arg) << 32); - if (msgq.Enqueue(msg) == false) { - putlog(0, "Message queue exhausted !!"); + if (msgid < MessageID::MAX_REQUEST) { + request |= 1U << (uint)msgid; + } else { + uint64 msg = (uint64)msgid | (((uint64)arg) << 32); + if (msgq.Enqueue(msg) == false) { + putlog(0, "Message queue exhausted !! msgid=%d", (int)msgid); + } + request |= REQUEST_MESSAGE; } - request |= REQUEST_MESSAGE; cv.notify_one(); } @@ -498,11 +534,7 @@ Scheduler::MonitorUpdate(Monitor *, Text screen.Clear(); - y = gSync->MonitorUpdateSub(screen, vtime); - - int clock_khz = gMPU->GetClockSpeed(); - screen.Print(0, y++, "MPU Speed: %d.%03dMHz", - (clock_khz / 1000), (clock_khz % 1000)); + y = syncer->MonitorUpdateSub(screen, vtime); // 0 1 2 3 // 012345678901234567890123456789