--- nono/vm/scheduler.cpp 2026/04/29 17:04:53 1.1.1.9 +++ nono/vm/scheduler.cpp 2026/04/29 17:04:56 1.1.1.10 @@ -62,6 +62,22 @@ static inline uint64 GetRealTime() #define FORMAT_SEC 1 // 経過時間 t を文字列にして返す。 +// 文字列長は、t の大きさ(とフォーマット指定)によって以下の通り。 +// +// 0 1 2 +// 01234567890123456789012345 +// "0.mmm'uuu'nnn" 10秒未満、または FORMAT_SEC なら13桁 +// "59.mmm'uuu'nnn" 1分未満なら14桁 +// " 9:59.mmm'uuu'nnn" 1時間未満なら17桁 +// " 9:59:59.mmm'uuu'nnn" 24時間未満なら20桁 +// "999d 23:59:59.mmm'uuu'nnn" 1日以上なら25桁。これが最大幅。 +// +// FORMAT_SEC は1桁秒以下の場合 (実際には1秒未満の場合) に用いる。 +// FORMAT_FULL は 1000日経過すると桁がずれるけど、それはもういいだろう。 +// +// 10秒未満の場合だけ %2d ではなく %d で1桁切り詰めているが、これは +// FORMAT_SEC との互換性のため。その必要のない10分未満と10時間未満は +// どちらも %2d で表記し桁数を維持することに努める。 static const std::string TimeToStrF(uint64 t, int format) { @@ -119,7 +135,7 @@ TimeToStrF(uint64 t, int format) return std::string(buf, p - buf); } -static const std::string +const std::string TimeToStr(uint64 t) { return TimeToStrF(t, FORMAT_FULL); @@ -137,18 +153,19 @@ SecToStr(uint64 t) // コンストラクタ Scheduler::Scheduler() + : inherited("Scheduler") { - logname = "scheduler"; - devname = "Scheduler"; - for (int i = 0; i < perfq.Capacity(); i++) { perfq.Enqueue(1000); } - sync_event.dev = this; sync_event.func = (DeviceCallback_t)&Scheduler::SyncCallback; sync_event.time = 50_msec; sync_event.SetName("Scheduler Sync"); + + monitor.func = (MonitorCallback_t)&Scheduler::MonitorUpdate; + // サイズは Init で決まる + monitor.Regist(ID_MONITOR_SCHEDULER); } // デストラクタ @@ -161,6 +178,9 @@ Scheduler::~Scheduler() bool Scheduler::Init() { + // この時点でイベントの個数は確定しているはず(してなければならない) + monitor.SetSize(72, 10 + gEvents.size()); + // 起動時引数指定の高速モード SetFullSpeed(gMainApp.fast_mode); @@ -607,6 +627,15 @@ Scheduler::Terminate() { if (thread_created) { RequestExit(); + Wait(); + } +} + +// スレッドの完了まで待つ。 +void +Scheduler::Wait() +{ + if (thread_created) { pthread_join(thread, NULL); thread_created = false; } @@ -770,50 +799,40 @@ Scheduler::StopEvent(Event *event) } } - -// モニターサイズ取得 -nnSize -Scheduler::GetMonitorSize() -{ - // 本当は Init() 完了時点でイベント個数は確定しているはずだけど、 - // そのタイミングで一度だけ呼ばれるフックが今の所ない。 - return nnSize(72, 10 + gEvents.size()); -} - void -Scheduler::MonitorUpdate(TextScreen& monitor) +Scheduler::MonitorUpdate(Monitor *, TextScreen& screen) { int x, y; - monitor.Clear(); + screen.Clear(); x = 0; y = 0; int clock_khz = gMPU->GetClockSpeed(); - monitor.Print(0, y, "Mode: 0x%04x (", mode); + screen.Print(0, y, "Mode: 0x%04x (", mode); if ((mode & SCHED_SYNC)) { - monitor.Puts(14, y, TA::On, "Sync"); + screen.Puts(14, y, TA::On, "Sync"); } else { - monitor.Puts(14, y, "Full"); + screen.Puts(14, y, "Full"); } switch (mode & SCHED_CPU_MASK) { case SCHED_CPU_NORMAL: - monitor.Puts(19, y, "Run"); + screen.Puts(19, y, "Run"); break; case SCHED_CPU_STOP: - monitor.Puts(19, y, TA::On, "STOP"); + screen.Puts(19, y, TA::On, "STOP"); break; case SCHED_CPU_HALT: - monitor.Puts(19, y, TA::On, "HALT"); + screen.Puts(19, y, TA::On, "HALT"); break; } - monitor.Puts(24, y, TA::OnOff((mode & SCHED_BOOT)), "Boot"); - monitor.Puts(29, y, TA::OnOff((mode & SCHED_KEY)), "Key"); - monitor.Puts(33, y, ")"); + screen.Puts(24, y, TA::OnOff((mode & SCHED_BOOT)), "Boot"); + screen.Puts(29, y, TA::OnOff((mode & SCHED_KEY)), "Key"); + screen.Puts(33, y, ")"); y++; - monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag); - monitor.Print(0, y++, "MPU Speed: %d.%03dMHz", + screen.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag); + screen.Print(0, y++, "MPU Speed: %d.%03dMHz", (clock_khz / 1000), (clock_khz % 1000)); uint64 rt = rtime; uint64 vt = vtime; @@ -829,26 +848,26 @@ Scheduler::MonitorUpdate(TextScreen& mon s_sync_vt = TimeToStr(sync_vt); } // 日 まで表示しようとするときは桁位置を動かす - int width = std::max(rt, vt) > 86400_sec ? 25 : 20; + int width = std::max(rt, vt) < 86400_sec ? 20 : 25; - monitor.Print(0, y++, "Total Real Time: %*s", width, s_rt.c_str()); - monitor.Print(4, y++, "Virtual Time: %*s", width, s_vt.c_str()); - monitor.Print(0, y++, "Sync Real Time: %*s", width, s_sync_rt.c_str()); - monitor.Print(4, y++, "Virtual Time: %*s", width, s_sync_vt.c_str()); + screen.Print(0, y++, "Total Real Time: %*s", width, s_rt.c_str()); + screen.Print(4, y++, "Virtual Time: %*s", width, s_vt.c_str()); + screen.Print(0, y++, "Sync Real Time: %*s", width, s_sync_rt.c_str()); + screen.Print(4, y++, "Virtual Time: %*s", width, s_sync_vt.c_str()); - monitor.Print(0, y++, "Performance : %4d%%", perf_counter); + screen.Print(0, y++, "Performance : %4d%%", perf_counter); x = 44; y = 0; - monitor.Print(x, y++, "Max OverSlept: %13s", SecToStr(overslept).c_str()); - monitor.Print(x, y++, "Sync per sec : %4u", last_sync_count); - monitor.Print(x, y++, "Event per sec: %8u", + screen.Print(x, y++, "Max OverSlept: %13s", SecToStr(overslept).c_str()); + screen.Print(x, y++, "Sync per sec : %4u", last_sync_count); + screen.Print(x, y++, "Event per sec: %8u", (uint)last_event_stat.Count()); - monitor.Print(x, y++, "Evt Intvl Min: %13s", + screen.Print(x, y++, "Evt Intvl Min: %13s", SecToStr(last_event_stat.Min()).c_str()); - monitor.Print(x, y++, "Evt Intvl Avg: %13s", + screen.Print(x, y++, "Evt Intvl Avg: %13s", SecToStr(last_event_stat.Average()).c_str()); - monitor.Print(x, y++, "Evt Intvl Max: %13s", + screen.Print(x, y++, "Evt Intvl Max: %13s", SecToStr(last_event_stat.Max()).c_str()); // 0 1 2 3 4 5 6 @@ -857,9 +876,9 @@ Scheduler::MonitorUpdate(TextScreen& mon // 3.123'456'789 3.123'456'789 012345678901234567890123456789 x = 0; y = 9; - monitor.Puts(x + 0, y, "Event Time"); - monitor.Puts(x + 15, y, "Remain Time"); - monitor.Puts(x + 30, y, "Description"); + screen.Puts(x + 0, y, "Event Time"); + screen.Puts(x + 15, y, "Remain Time"); + screen.Puts(x + 30, y, "Description"); y++; for (const auto& e : gEvents) { @@ -880,7 +899,7 @@ Scheduler::MonitorUpdate(TextScreen& mon attr = TA::Disable; rem = 0; } - monitor.Print(x, y++, attr, + screen.Print(x, y++, attr, "%13s %13s %-30s", SecToStr(e->time).c_str(), SecToStr(rem).c_str(),