|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.14! root 7: //
! 8: // スケジューラ
! 9: //
1.1 root 10:
11: // VM スケジューラは、仮想時間とイベントを管理する。
1.1.1.14! root 12: // 仮想時間はすべて nsec で表し、StartTime() から単調増加する。
! 13: // VM 界からは仮想時間は gScheduler->GetVirtTime() で取得する。
1.1 root 14: //
1.1.1.14! root 15: // 実時間は Sync が管理し、こちらも nsec で表す。
! 16: // このストップウォッチも StartTime() から単調増加する。
! 17: // 実時間、仮想時間とも電源オフ中でも単調増加することに注意。
1.1.1.9 root 18: //
1.1.1.14! root 19: // 「実経過時間」は gRealtime.Elapsed() で取得でき
! 20: // o (たぶん) Scheduler スレッド開始時から、常に増加。
1.1.1.9 root 21: // o (たぶん)ホストの時刻変更やサスペンドの影響を受けず、常に増加。
22: // o デバッガプロンプトで停止中は進行が停止する。
23: // という性質を持つ。
24: //
25: // 同期走行モードにおいては rtime_epoch, vtime_epoch をそれぞれの基準点とし
26: // これらと現在の実経過時間、仮想経過時間の差が同期するよう調整する。
27: // 高速走行モードにおいてはこれらは必要ないので特に参照しない。
28: //
29: // 一定間隔で行うパフォーマンス測定は、前回計測時の実および仮想経過時間を
30: // last_perf_rtime, last_perf_vtime として持っているので、これと現在の
31: // 経過時間との比で求める。同期走行/高速走行の影響は受けない。
1.1 root 32:
1.1.1.14! root 33: #include "scheduler.h"
! 34: #include "mpu.h"
! 35: #include "rtc.h"
! 36: #include "sync.h"
1.1 root 37:
1.1.1.14! root 38: //#define CT_ON
1.1.1.9 root 39:
1.1.1.14! root 40: #ifdef CT_ON
! 41: static uint64 ct_get1;
! 42: static uint64 ct_get2;
! 43: static uint64 ct_start;
! 44: static uint64 ct_start1;
! 45: static uint64 ct_start2;
! 46: static uint64 ct_start3;
! 47: static uint64 ct_start4;
! 48: static uint64 ct_stop1;
! 49: static uint64 ct_stop2;
! 50: static uint64 ct_eslow;
! 51: static uint64 ct_sslow;
! 52: static uint64 ct_pslow;
! 53: #define CT(name) ct_##name += 1
! 54: #else
! 55: #define CT(name)
! 56: #endif
1.1.1.9 root 57:
1.1.1.14! root 58: // グローバル参照用
! 59: Scheduler *gScheduler;
1.1.1.9 root 60:
61: #define FORMAT_FULL 0
62: #define FORMAT_SEC 1
63:
64: // 経過時間 t を文字列にして返す。
1.1.1.10 root 65: // 文字列長は、t の大きさ(とフォーマット指定)によって以下の通り。
66: //
67: // 0 1 2
68: // 01234567890123456789012345
69: // "0.mmm'uuu'nnn" 10秒未満、または FORMAT_SEC なら13桁
70: // "59.mmm'uuu'nnn" 1分未満なら14桁
71: // " 9:59.mmm'uuu'nnn" 1時間未満なら17桁
72: // " 9:59:59.mmm'uuu'nnn" 24時間未満なら20桁
73: // "999d 23:59:59.mmm'uuu'nnn" 1日以上なら25桁。これが最大幅。
74: //
75: // FORMAT_SEC は1桁秒以下の場合 (実際には1秒未満の場合) に用いる。
76: // FORMAT_FULL は 1000日経過すると桁がずれるけど、それはもういいだろう。
77: //
78: // 10秒未満の場合だけ %2d ではなく %d で1桁切り詰めているが、これは
79: // FORMAT_SEC との互換性のため。その必要のない10分未満と10時間未満は
80: // どちらも %2d で表記し桁数を維持することに努める。
1.1.1.9 root 81: static const std::string
82: TimeToStrF(uint64 t, int format)
83: {
84: char buf[32];
85: char *p;
86: size_t len;
87: int n;
88:
89: uint ns = t % 1000;
90: t /= 1000;
91: uint us = t % 1000;
92: t /= 1000;
93: uint ms = t % 1000;
94: t /= 1000;
95:
96: uint s, m, h, d;
97: if (format == FORMAT_FULL) {
98: s = t % 60;
99: t /= 60;
100: m = t % 60;
101: t /= 60;
102: h = t % 24;
103: t /= 24;
104: d = t;
105: } else {
106: s = t;
107: m = 0;
108: h = 0;
109: d = 0;
110: }
111:
112: p = buf;
113: len = sizeof(buf);
114: if (d) {
115: n = snprintf(p, len, "%3dd %02u:%02u:%02u", d, h, m, s);
116: p += n;
117: len -= n;
118: } else if (h) {
119: n = snprintf(p, len, "%2u:%02u:%02u", h, m, s);
120: p += n;
121: len -= n;
122: } else if (m) {
123: n = snprintf(p, len, "%2u:%02u", m, s);
124: p += n;
125: len -= n;
126: } else {
127: n = snprintf(p, len, "%u", s);
128: p += n;
129: len -= n;
130: }
131: n = snprintf(p, len, ".%03u'%03u'%03u", ms, us, ns);
132: p += n;
133: len -= n;
134:
135: return std::string(buf, p - buf);
136: }
137:
1.1.1.10 root 138: const std::string
1.1.1.9 root 139: TimeToStr(uint64 t)
140: {
141: return TimeToStrF(t, FORMAT_FULL);
142: }
143:
1.1.1.14! root 144: const std::string
1.1.1.9 root 145: SecToStr(uint64 t)
146: {
147: return TimeToStrF(t, FORMAT_SEC);
148: }
149:
150: //
151: // スケジューラ
152: //
153:
1.1 root 154: // コンストラクタ
155: Scheduler::Scheduler()
1.1.1.10 root 156: : inherited("Scheduler")
1.1 root 157: {
1.1.1.14! root 158: // オブジェクト名は Scheduler だがここのスレッド名は VM くらいのほうが
! 159: // 通りがよさそうだ。
! 160: SetThreadName("VM");
1.1.1.9 root 161:
1.1.1.14! root 162: slow_top = -1;
! 163: slow_top_vtime = UINT64_MAX;
1.1.1.10 root 164:
1.1.1.14! root 165: monitor.func = ToMonitorCallback(&Scheduler::MonitorUpdate);
1.1.1.10 root 166: // サイズは Init で決まる
167: monitor.Regist(ID_MONITOR_SCHEDULER);
1.1 root 168: }
169:
170: // デストラクタ
171: Scheduler::~Scheduler()
172: {
1.1.1.14! root 173: TerminateThread();
! 174: gScheduler = NULL;
1.1 root 175: }
176:
177: // 初期化
178: bool
179: Scheduler::Init()
180: {
1.1.1.10 root 181: // この時点でイベントの個数は確定しているはず(してなければならない)
1.1.1.14! root 182: monitor.SetSize(80, 9 + all_events.size());
1.1 root 183:
184: return true;
185: }
186:
187: // スレッドエントリ関数
188: void
189: Scheduler::ThreadRun()
190: {
1.1.1.14! root 191: // 電源オンオフに関わらず、ここが rtime, vtime の基準点
! 192: StartTime();
1.1 root 193:
1.1.1.3 root 194: for (;;) {
1.1.1.14! root 195: // 条件変数の正しい使い方としては request の参照にも mtx ロックが必要
! 196: // だが、ここでロック取らずに参照したことによって request が立った
! 197: // ことを見落としても、ループ一回回ってもう一度ここに来るだけなので
! 198: // たぶん致命的なことはないはず。
! 199: if (__predict_false(request)) {
! 200: uint32 req;
! 201: {
! 202: std::lock_guard<std::mutex> lock(mtx);
! 203: req = request;
! 204: request = 0;
1.1.1.9 root 205: }
1.1.1.14! root 206: if ((req & REQUEST_EXIT)) {
! 207: break;
1.1.1.9 root 208: }
1.1.1.14! root 209: if ((req & REQUEST_MESSAGE)) {
! 210: DispatchMessage();
1.1.1.9 root 211: }
1.1.1.14! root 212: }
1.1.1.9 root 213:
1.1.1.14! root 214: // 先頭のイベントを取得して停止
! 215: Event *ev;
! 216: if (__predict_true(fast != NULL)) {
! 217: CT(get1);
! 218: ev = fast;
! 219: fast = NULL;
! 220: } else {
! 221: CT(get2);
! 222: ev = slow[slow_top--];
! 223: slow_top_vtime = slow[slow_top]->vtime;
1.1.1.9 root 224: }
1.1.1.14! root 225: ev->active = false;
1.1.1.9 root 226:
1.1.1.14! root 227: // 時刻更新
! 228: vtime = ev->vtime;
! 229:
! 230: // コールバック
! 231: ev->count++;
! 232: ((ev->dev)->*(ev->func))(*ev);
1.1 root 233: }
1.1.1.9 root 234: }
1.1 root 235:
1.1.1.14! root 236: // 時間の始まり
! 237: void
! 238: Scheduler::StartTime()
1.1.1.9 root 239: {
1.1.1.14! root 240: // イベントキューを空にする
! 241: for (auto ev : all_events) {
! 242: if (ev->active) {
! 243: StopEvent(*ev);
1.1.1.9 root 244: }
1.1.1.14! root 245: ev->count = 0;
1.1.1.9 root 246: }
1.1.1.14! root 247:
! 248: // 時間をリセットする。
! 249: // 呼び出しの依存関係に注意。
! 250: vtime = 0; // required by Sync
! 251: gSync->StartTime(); // required by RTC
! 252: gRTC->StartTime();
1.1.1.9 root 253: }
1.1 root 254:
1.1.1.14! root 255: // スレッド終了指示
! 256: // (当然他スレッドから呼ぶことになる)
1.1.1.9 root 257: void
1.1.1.14! root 258: Scheduler::Terminate()
1.1.1.9 root 259: {
1.1.1.14! root 260: std::unique_lock<std::mutex> lock(mtx);
! 261: request |= REQUEST_EXIT;
! 262: cv.notify_one();
1.1.1.9 root 263: }
1.1 root 264:
1.1.1.14! root 265: // イベントを登録する。
! 266: // 登録時点では実は type だけ確定していればよい。
! 267: //
! 268: // ここで管理してるのはポインタだけで、実体は呼び出し元クラスのメンバ
! 269: // インスタンスのはずなので、登録しっぱなしにする。
! 270: void
! 271: Scheduler::RegistEvent(Event& ev)
1.1.1.9 root 272: {
1.1.1.14! root 273: // イベントリストに追加
! 274: all_events.push_back(&ev);
1.1.1.9 root 275: }
276:
1.1.1.14! root 277: // 指定のイベントを開始する。
! 278: // すでに同イベントが登録されている場合は古いイベントを削除してから
! 279: // 新しいイベントを再登録となる。
! 280: // イベントはワンショットのみ。
1.1.1.9 root 281: void
1.1.1.14! root 282: Scheduler::RestartEvent(Event& ev)
1.1.1.9 root 283: {
1.1.1.14! root 284: if (ev.IsRunning()) {
! 285: StopEvent(ev);
1.1.1.9 root 286: }
1.1.1.14! root 287:
! 288: StartEvent(ev);
1.1 root 289: }
290:
1.1.1.14! root 291: // 指定のイベントを開始する。
! 292: // このイベントが登録されてないことが確定できる場合のみこちらが使える。
! 293: // イベントはワンショットのみ。
1.1 root 294: void
1.1.1.14! root 295: Scheduler::StartEvent(Event& ev)
1.1 root 296: {
1.1.1.14! root 297: CT(start);
! 298: #ifdef CT_ON
! 299: if (ct_start % 100'000'000 == 0) {
! 300: printf("get*=%lu [%lu, %lu]\n",
! 301: (ct_get1 + ct_get2), ct_get1, ct_get2);
! 302: printf("start*=%lu [%lu, %lu, %lu, %lu]\n",
! 303: ct_start, ct_start1, ct_start2, ct_start3, ct_start4);
! 304: printf("stop*=%lu [%lu, %lu]\n",
! 305: (ct_stop1 + ct_stop2), ct_stop1, ct_stop2);
! 306: printf("eslow=%lu sslow=%lu pslow=%lu\n",
! 307: ct_eslow, ct_sslow, ct_pslow);
! 308: printf("slow_top=%d vtime=%lu\n", slow_top, slow_top_vtime);
! 309: }
! 310: #endif
! 311:
! 312: // この assert を有効にすると 10 ポイント性能が低下する(502%->492%)
! 313: //assertmsg(ev.IsRunning() == false, "%s", ev.GetName().c_str());
! 314:
! 315: // vtime は仮想絶対時刻 [nsec]、time は仮想相対時間 [nsec]
! 316: ev.vtime = vtime + ev.time;
! 317:
! 318: ev.active = true;
! 319:
! 320: if (__predict_true(ev.vtime <= slow_top_vtime)) {
! 321: if (__predict_true(fast == NULL)) {
! 322: CT(start1);
! 323: fast = &ev;
! 324: } else {
! 325: if (__predict_false(ev.vtime <= fast->vtime)) {
! 326: CT(start2);
! 327: PushSlow(*fast);
! 328: fast = &ev;
! 329: } else {
! 330: CT(start3);
! 331: PushSlow(ev);
! 332: }
! 333: }
! 334: } else {
! 335: CT(start4);
! 336: EnqueueSlow(ev);
1.1.1.10 root 337: }
338: }
339:
1.1.1.14! root 340: // 実時間間隔を指定してイベントを開始する。
! 341: // rt_now はイベント発行者の実時間での現在時刻で、
! 342: // rt_period は次回のイベントまでの実時間間隔。
! 343: void
! 344: Scheduler::StartRealtimeEvent(Event& ev, uint64 rt_now, uint64 rt_period)
! 345: {
! 346: // 実時間をもとに仮想時間軸上の推定を行う。
! 347:
! 348: uint64 rtime = gSync->GetRealTime();
! 349:
! 350: if (rtime > rt_now + rt_period) {
! 351: // 実時間がさらに 1 period 以上進んでいる。
! 352: // 実時間に追いつかないといけないので、イベントの
! 353: // 周期を短くして回復運転させる。
! 354: // 周期を短くしすぎるとゲストが処理できなくなるので、
! 355: // 10% カットに留める。
! 356: ev.time = rt_period - rt_period / 10;
! 357: } else if (rtime < rt_now - rt_period) {
! 358: // イベントがさらに 1 period 以上進んでいる。
! 359: // 実時間を追い越してしまっているので、イベントの
! 360: // 周期を長くして待つ。長くする方はいくらでも長くしていいので、
! 361: // 連続した場合はどんどん長くしていく。
! 362: ev.time += rt_period;
! 363: } else {
! 364: // おおむね同期している
! 365: ev.time = rt_period;
! 366: }
! 367:
! 368: RestartEvent(ev);
! 369: }
! 370:
! 371: // ev は slow_top には来ない前提
1.1.1.10 root 372: void
1.1.1.14! root 373: Scheduler::EnqueueSlow(Event& ev)
1.1.1.10 root 374: {
1.1.1.14! root 375: CT(eslow);
! 376:
! 377: int s = slow_top;
! 378: slow[++slow_top] = slow[s];
! 379:
! 380: for (; --s >= 0; ) {
! 381: if (ev.vtime <= slow[s]->vtime) {
! 382: slow[s + 1] = &ev;
! 383: return;
! 384: } else {
! 385: slow[s + 1] = slow[s];
! 386: }
1.1.1.3 root 387: }
1.1.1.14! root 388: slow[0] = &ev;
1.1 root 389: }
390:
1.1.1.8 root 391: void
1.1.1.14! root 392: Scheduler::PushSlow(Event& ev)
1.1.1.8 root 393: {
1.1.1.14! root 394: CT(pslow);
! 395:
! 396: slow[++slow_top] = &ev;
! 397: slow_top_vtime = ev.vtime;
1.1.1.8 root 398: }
399:
1.1.1.14! root 400: // 指定のイベントを停止する。
! 401: // 指定されたイベントが登録されていなければ何もしない。
1.1 root 402: void
1.1.1.14! root 403: Scheduler::StopEvent(Event& ev)
1.1 root 404: {
1.1.1.14! root 405: ev.active = false;
! 406:
! 407: if (__predict_false(&ev == fast)) {
! 408: CT(stop1);
! 409: fast = NULL;
1.1 root 410: } else {
1.1.1.14! root 411: CT(stop2);
! 412: StopSlowEvent(ev);
1.1 root 413: }
414: }
415:
1.1.1.9 root 416: void
1.1.1.14! root 417: Scheduler::StopSlowEvent(Event& ev)
1.1.1.9 root 418: {
1.1.1.14! root 419: CT(sslow);
! 420:
! 421: int s = slow_top;
! 422:
! 423: for (; s >= 0; s--) {
! 424: if (&ev == slow[s]) {
! 425: for (; s < slow_top; s++) {
! 426: slow[s] = slow[s + 1];
! 427: }
! 428: slow_top--;
! 429: if (slow_top < 0) {
! 430: slow_top_vtime = UINT64_MAX;
! 431: } else {
! 432: slow_top_vtime = slow[slow_top]->vtime;
! 433: }
! 434: break;
! 435: }
1.1.1.9 root 436: }
437: }
438:
1.1.1.14! root 439: // メッセージハンドラを登録する。
1.1.1.9 root 440: void
1.1.1.14! root 441: Scheduler::ConnectMessage(MessageID msgid, Device *dev, MessageCallback_t func)
1.1.1.9 root 442: {
1.1.1.14! root 443: auto& mh = message_handlers[msgid];
! 444: assert(mh.dev == NULL);
! 445: mh.dev = dev;
! 446: mh.func = func;
1.1.1.9 root 447: }
448:
1.1.1.14! root 449: // メッセージをディスパッチして、登録されていればハンドラを呼び出す。
1.1.1.9 root 450: void
1.1.1.14! root 451: Scheduler::DispatchMessage()
1.1.1.9 root 452: {
1.1.1.14! root 453: uint64 msg;
1.1.1.9 root 454:
1.1.1.14! root 455: while (msgq.Dequeue(&msg)) {
! 456: MessageID msgid = (MessageID)(msg & 0xff);
! 457: uint32 arg = (msg >> 32);
1.1.1.9 root 458:
1.1.1.14! root 459: assert(0 <= msgid && msgid < MessageID::MAX);
1.1.1.9 root 460:
1.1.1.14! root 461: auto& mh = message_handlers[msgid];
! 462: if (mh.dev != NULL) {
! 463: ((mh.dev)->*(mh.func))(msgid, arg);
! 464: }
1.1.1.9 root 465: }
466: }
467:
1.1.1.14! root 468: // メッセージを送る。
! 469: // VM スレッド以外から呼び出しても良い。
1.1 root 470: void
1.1.1.14! root 471: Scheduler::SendMessage(MessageID msgid, uint32 arg)
1.1 root 472: {
1.1.1.14! root 473: std::unique_lock<std::mutex> lock(mtx);
1.1 root 474:
1.1.1.14! root 475: uint64 msg = (uint64)msgid | (((uint64)arg) << 32);
! 476: if (msgq.Enqueue(msg) == false) {
! 477: putlog(0, "Message queue exhausted !!");
1.1 root 478: }
1.1.1.14! root 479: request |= REQUEST_MESSAGE;
! 480: cv.notify_one();
1.1 root 481: }
482:
1.1.1.14! root 483: // 指定時間が経過するか、リクエストが起きるまでスリープ
1.1 root 484: void
1.1.1.14! root 485: Scheduler::Sleep(uint64 time)
1.1 root 486: {
1.1.1.14! root 487: std::unique_lock<std::mutex> lock(mtx);
! 488: cv.wait_for(lock, std::chrono::nanoseconds(time), [&] {
! 489: return (request != 0);
! 490: });
! 491: // リクエストフラグが立ってもここでは何もしない
1.1 root 492: }
493:
1.1.1.5 root 494: void
1.1.1.10 root 495: Scheduler::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 496: {
1.1.1.14! root 497: int y;
1.1 root 498:
1.1.1.10 root 499: screen.Clear();
1.1 root 500:
1.1.1.14! root 501: y = gSync->MonitorUpdateSub(screen, vtime);
1.1.1.7 root 502:
1.1.1.14! root 503: int clock_khz = gMPU->GetClockSpeed();
1.1.1.10 root 504: screen.Print(0, y++, "MPU Speed: %d.%03dMHz",
1.1.1.7 root 505: (clock_khz / 1000), (clock_khz % 1000));
1.1.1.9 root 506:
1.1.1.14! root 507: // 0 1 2 3
! 508: // 012345678901234567890123456789
! 509: // Set Time Remain Time
! 510: // 3.123'456'789 3.123'456'789
! 511: //
! 512: // 3 4 5 6 7
! 513: // 01234567890123456789012345678901234567890123456789
! 514: // Description Count
! 515: // 012345678901234567890123456789 999,999,999,999,999
! 516: // 18,446,744,073,709,551,615
! 517: y++;
! 518: screen.Puts(0, y, "Event Time");
! 519: screen.Puts(15, y, "Remain Time");
! 520: screen.Puts(30, y, "Description");
! 521: screen.Puts(75, y, "Count");
1.1 root 522: y++;
1.1.1.6 root 523:
1.1.1.14! root 524: for (const auto ev : all_events) {
1.1 root 525: uint64 rem;
1.1.1.4 root 526: TA attr;
1.1.1.14! root 527: if (ev->IsRunning()) {
! 528: if (ev->vtime > vtime) {
! 529: rem = ev->vtime - vtime;
1.1.1.6 root 530: } else {
531: // XXX 実際には起きないけど、この表示をするにあたって
532: // スケジューラスレッドと一切調停していないので、仮想時刻が
1.1.1.14! root 533: // ev->vtime (イベント発生時刻) をすぎていることがある。
1.1.1.6 root 534: // すぎてるので残り時間 0 と表示しておく。
535: rem = 0;
536: }
1.1 root 537: } else {
538: rem = 0;
539: }
1.1.1.14! root 540:
! 541: if (ev->count != ev->last_count) {
! 542: attr = TA::Normal;
! 543: ev->last_count = ev->count;
! 544: } else {
! 545: attr = TA::Disable;
! 546: }
! 547:
! 548: // 先にイベント回数を表示。
! 549: // 21文字以上になったら上位桁が長い Description で消されても
! 550: // 大勢に影響はないだろう。
! 551: std::string countstr = format_number(ev->count);
! 552: screen.Print(54, y, attr, "%26s", countstr.c_str());
! 553: screen.Print(0, y, attr, "%13s %13s %s ",
! 554: SecToStr(ev->time).c_str(),
1.1.1.9 root 555: SecToStr(rem).c_str(),
1.1.1.14! root 556: ev->GetName().c_str());
! 557: y++;
1.1 root 558: }
559: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.