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