|
|
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.22! root 12: // 仮想時間はすべて tsec で表し、StartTime() から単調増加する。
1.1.1.14 root 13: // VM 界からは仮想時間は gScheduler->GetVirtTime() で取得する。
1.1 root 14: //
1.1.1.22! root 15: // 実時間は Syncer が管理し、こちらも tsec で表す。
1.1.1.14 root 16: // このストップウォッチも StartTime() から単調増加する。
17: // 実時間、仮想時間とも電源オフ中でも単調増加することに注意。
1.1.1.9 root 18: //
1.1.1.22! root 19: // 「実経過時間」は gRealtime.Elapsed_nsec() で取得できるが tsec で保持する。
1.1.1.14 root 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);
1.1.1.22! root 75: monitor->SetCallback(&Scheduler::MonitorScreen);
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.22! root 159: auto dev = ev->GetOwner();
! 160: auto func = ev->GetCallback();
! 161: (dev->*(func))(ev);
1.1 root 162: }
1.1.1.9 root 163: }
1.1 root 164:
1.1.1.14 root 165: // 時間の始まり
166: void
167: Scheduler::StartTime()
1.1.1.9 root 168: {
1.1.1.14 root 169: // イベントキューを空にする
1.1.1.21 root 170: for (auto *ev : evman->all) {
1.1.1.14 root 171: if (ev->active) {
1.1.1.21 root 172: StopEvent(ev);
1.1.1.9 root 173: }
1.1.1.14 root 174: ev->count = 0;
1.1.1.9 root 175: }
1.1.1.14 root 176:
177: // 時間をリセットする。
178: // 呼び出しの依存関係に注意。
179: vtime = 0; // required by Sync
1.1.1.16 root 180: syncer->StartTime(); // required by RTC
181: GetRTCDevice()->StartTime();
1.1.1.9 root 182: }
1.1 root 183:
1.1.1.14 root 184: // スレッド終了指示
185: // (当然他スレッドから呼ぶことになる)
1.1.1.9 root 186: void
1.1.1.14 root 187: Scheduler::Terminate()
1.1.1.9 root 188: {
1.1.1.21 root 189: std::lock_guard<std::mutex> lock(mtx);
1.1.1.14 root 190: request |= REQUEST_EXIT;
191: cv.notify_one();
1.1.1.9 root 192: }
1.1 root 193:
1.1.1.14 root 194: // 指定のイベントを開始する。
195: // すでに同イベントが登録されている場合は古いイベントを削除してから
196: // 新しいイベントを再登録となる。
197: // イベントはワンショットのみ。
1.1.1.9 root 198: void
1.1.1.21 root 199: Scheduler::RestartEvent(Event *ev)
1.1.1.9 root 200: {
1.1.1.21 root 201: if (ev->IsRunning()) {
1.1.1.14 root 202: StopEvent(ev);
1.1.1.9 root 203: }
1.1.1.14 root 204:
205: StartEvent(ev);
1.1 root 206: }
207:
1.1.1.14 root 208: // 指定のイベントを開始する。
209: // このイベントが登録されてないことが確定できる場合のみこちらが使える。
210: // イベントはワンショットのみ。
1.1 root 211: void
1.1.1.21 root 212: Scheduler::StartEvent(Event *ev)
1.1 root 213: {
1.1.1.14 root 214: CT(start);
215: #ifdef CT_ON
216: if (ct_start % 100'000'000 == 0) {
1.1.1.17 root 217: printf("get*=%" PRIu64 " [%" PRIu64 ", %" PRIu64 "]\n",
1.1.1.14 root 218: (ct_get1 + ct_get2), ct_get1, ct_get2);
1.1.1.17 root 219: printf("start*=%" PRIu64
220: " [%" PRIu64 ", %" PRIu64 ", %" PRIu64 ", %" PRIu64 "]\n",
1.1.1.14 root 221: ct_start, ct_start1, ct_start2, ct_start3, ct_start4);
1.1.1.17 root 222: printf("stop*=%" PRIu64 " [%" PRIu64 ", %" PRIu64 "]\n",
1.1.1.14 root 223: (ct_stop1 + ct_stop2), ct_stop1, ct_stop2);
1.1.1.17 root 224: printf("eslow=%" PRIu64 " sslow=%" PRIu64 " pslow=%" PRIu64 "\n",
1.1.1.14 root 225: ct_eslow, ct_sslow, ct_pslow);
1.1.1.17 root 226: printf("slow_top=%d vtime=%" PRIu64 "\n", slow_top, slow_top_vtime);
1.1.1.14 root 227: }
228: #endif
229:
230: // この assert を有効にすると 10 ポイント性能が低下する(502%->492%)
1.1.1.21 root 231: //assertmsg(ev->IsRunning() == false, "%s", ev->GetName().c_str());
1.1.1.14 root 232:
1.1.1.22! root 233: // vtime は仮想絶対時刻 [tsec]、time は仮想相対時間 [tsec]
1.1.1.21 root 234: ev->vtime = vtime + ev->time;
1.1.1.14 root 235:
1.1.1.21 root 236: ev->active = true;
1.1.1.14 root 237:
1.1.1.21 root 238: if (__predict_true(ev->vtime <= slow_top_vtime)) {
1.1.1.14 root 239: if (__predict_true(fast == NULL)) {
240: CT(start1);
1.1.1.21 root 241: fast = ev;
1.1.1.14 root 242: } else {
1.1.1.21 root 243: if (__predict_false(ev->vtime <= fast->vtime)) {
1.1.1.14 root 244: CT(start2);
1.1.1.21 root 245: PushSlow(fast);
246: fast = ev;
1.1.1.14 root 247: } else {
248: CT(start3);
249: PushSlow(ev);
250: }
251: }
252: } else {
253: CT(start4);
254: EnqueueSlow(ev);
1.1.1.10 root 255: }
256: }
257:
1.1.1.14 root 258: // 実時間間隔を指定してイベントを開始する。
259: // rt_now はイベント発行者の実時間での現在時刻で、
260: // rt_period は次回のイベントまでの実時間間隔。
261: void
1.1.1.21 root 262: Scheduler::StartRealtimeEvent(Event *ev, uint64 rt_now, uint64 rt_period)
1.1.1.14 root 263: {
264: // 実時間をもとに仮想時間軸上の推定を行う。
265:
1.1.1.16 root 266: uint64 rtime = syncer->GetRealTime();
1.1.1.14 root 267:
268: if (rtime > rt_now + rt_period) {
269: // 実時間がさらに 1 period 以上進んでいる。
270: // 実時間に追いつかないといけないので、イベントの
271: // 周期を短くして回復運転させる。
272: // 周期を短くしすぎるとゲストが処理できなくなるので、
273: // 10% カットに留める。
1.1.1.21 root 274: ev->time = rt_period - rt_period / 10;
1.1.1.14 root 275: } else if (rtime < rt_now - rt_period) {
276: // イベントがさらに 1 period 以上進んでいる。
277: // 実時間を追い越してしまっているので、イベントの
278: // 周期を長くして待つ。長くする方はいくらでも長くしていいので、
279: // 連続した場合はどんどん長くしていく。
1.1.1.21 root 280: ev->time += rt_period;
1.1.1.14 root 281: } else {
282: // おおむね同期している
1.1.1.21 root 283: ev->time = rt_period;
1.1.1.14 root 284: }
285:
286: RestartEvent(ev);
287: }
288:
289: // ev は slow_top には来ない前提
1.1.1.10 root 290: void
1.1.1.21 root 291: Scheduler::EnqueueSlow(Event *ev)
1.1.1.10 root 292: {
1.1.1.14 root 293: CT(eslow);
294:
295: int s = slow_top;
296: slow[++slow_top] = slow[s];
297:
298: for (; --s >= 0; ) {
1.1.1.21 root 299: if (ev->vtime <= slow[s]->vtime) {
300: slow[s + 1] = ev;
1.1.1.14 root 301: return;
302: } else {
303: slow[s + 1] = slow[s];
304: }
1.1.1.3 root 305: }
1.1.1.21 root 306: slow[0] = ev;
1.1 root 307: }
308:
1.1.1.8 root 309: void
1.1.1.21 root 310: Scheduler::PushSlow(Event *ev)
1.1.1.8 root 311: {
1.1.1.14 root 312: CT(pslow);
313:
1.1.1.21 root 314: slow[++slow_top] = ev;
315: slow_top_vtime = ev->vtime;
1.1.1.8 root 316: }
317:
1.1.1.14 root 318: // 指定のイベントを停止する。
319: // 指定されたイベントが登録されていなければ何もしない。
1.1 root 320: void
1.1.1.21 root 321: Scheduler::StopEvent(Event *ev)
1.1 root 322: {
1.1.1.21 root 323: ev->active = false;
1.1.1.14 root 324:
1.1.1.21 root 325: if (__predict_false(ev == fast)) {
1.1.1.14 root 326: CT(stop1);
327: fast = NULL;
1.1 root 328: } else {
1.1.1.14 root 329: CT(stop2);
330: StopSlowEvent(ev);
1.1 root 331: }
332: }
333:
1.1.1.9 root 334: void
1.1.1.21 root 335: Scheduler::StopSlowEvent(Event *ev)
1.1.1.9 root 336: {
1.1.1.14 root 337: CT(sslow);
338:
339: int s = slow_top;
340:
341: for (; s >= 0; s--) {
1.1.1.21 root 342: if (ev == slow[s]) {
1.1.1.14 root 343: for (; s < slow_top; s++) {
344: slow[s] = slow[s + 1];
345: }
346: slow_top--;
347: if (slow_top < 0) {
348: slow_top_vtime = UINT64_MAX;
349: } else {
350: slow_top_vtime = slow[slow_top]->vtime;
351: }
352: break;
353: }
1.1.1.9 root 354: }
355: }
356:
1.1.1.14 root 357: // メッセージハンドラを登録する。
1.1.1.9 root 358: void
1.1.1.14 root 359: Scheduler::ConnectMessage(MessageID msgid, Device *dev, MessageCallback_t func)
1.1.1.9 root 360: {
1.1.1.16 root 361: assert(msgid < MessageID::MAX);
362:
1.1.1.14 root 363: auto& mh = message_handlers[msgid];
1.1.1.16 root 364: assertmsg(mh.dev == NULL,
365: "%s: msgid=%d connected again", __func__, (int)msgid);
1.1.1.14 root 366: mh.dev = dev;
367: mh.func = func;
1.1.1.9 root 368: }
369:
1.1.1.14 root 370: // メッセージをディスパッチして、登録されていればハンドラを呼び出す。
1.1.1.9 root 371: void
1.1.1.14 root 372: Scheduler::DispatchMessage()
1.1.1.9 root 373: {
1.1.1.14 root 374: uint64 msg;
1.1.1.9 root 375:
1.1.1.14 root 376: while (msgq.Dequeue(&msg)) {
377: MessageID msgid = (MessageID)(msg & 0xff);
378: uint32 arg = (msg >> 32);
1.1.1.9 root 379:
1.1.1.16 root 380: // MessageID::MPU_TRACE_ALL だけ特別対応。
381: // MPU_TRACE メッセージの宛先は(最大)2つあるが、
382: // スレッドを越える時は1メッセージにしておきたいので、
383: // MPU_TRACE_ALL が来たらここで分岐。
384: if (msgid == MessageID::MPU_TRACE_ALL) {
385: InvokeMessage(MessageID::MPU_TRACE_MAIN, arg);
386: InvokeMessage(MessageID::MPU_TRACE_XP, arg);
387: } else {
388: assert(msgid < MessageID::MAX);
389: InvokeMessage(msgid, arg);
1.1.1.14 root 390: }
1.1.1.9 root 391: }
392: }
393:
1.1.1.16 root 394: // メッセージハンドラを呼び出す。登録されてなければ何もしない。
395: void
396: Scheduler::InvokeMessage(MessageID msgid, uint32 arg)
397: {
398: auto h = message_handlers[msgid];
399: if (h.dev != NULL) {
400: (h.dev->*(h.func))(msgid, arg);
401: }
402: }
403:
1.1.1.14 root 404: // メッセージを送る。
405: // VM スレッド以外から呼び出しても良い。
1.1 root 406: void
1.1.1.14 root 407: Scheduler::SendMessage(MessageID msgid, uint32 arg)
1.1 root 408: {
1.1.1.21 root 409: std::lock_guard<std::mutex> lock(mtx);
1.1 root 410:
1.1.1.16 root 411: if (msgid < MessageID::MAX_REQUEST) {
412: request |= 1U << (uint)msgid;
413: } else {
414: uint64 msg = (uint64)msgid | (((uint64)arg) << 32);
415: if (msgq.Enqueue(msg) == false) {
416: putlog(0, "Message queue exhausted !! msgid=%d", (int)msgid);
417: }
418: request |= REQUEST_MESSAGE;
1.1 root 419: }
1.1.1.14 root 420: cv.notify_one();
1.1 root 421: }
422:
1.1.1.14 root 423: // 指定時間が経過するか、リクエストが起きるまでスリープ
1.1 root 424: void
1.1.1.14 root 425: Scheduler::Sleep(uint64 time)
1.1 root 426: {
1.1.1.14 root 427: std::unique_lock<std::mutex> lock(mtx);
428: cv.wait_for(lock, std::chrono::nanoseconds(time), [&] {
429: return (request != 0);
430: });
431: // リクエストフラグが立ってもここでは何もしない
1.1 root 432: }
433:
1.1.1.5 root 434: void
1.1.1.22! root 435: Scheduler::MonitorScreen(Monitor *, TextScreen& screen)
1.1 root 436: {
1.1.1.14 root 437: int y;
1.1 root 438:
1.1.1.10 root 439: screen.Clear();
1.1 root 440:
1.1.1.22! root 441: y = syncer->MonitorScreenSub(screen, vtime);
1.1.1.7 root 442:
1.1.1.14 root 443: // 0 1 2 3
444: // 012345678901234567890123456789
1.1.1.15 root 445: // Event Time Remain Time
446: // 3.123'456'789 3.123'456'789
1.1.1.14 root 447: //
448: // 3 4 5 6 7
449: // 01234567890123456789012345678901234567890123456789
450: // Description Count
451: // 012345678901234567890123456789 999,999,999,999,999
452: // 18,446,744,073,709,551,615
453: y++;
454: screen.Puts(0, y, "Event Time");
455: screen.Puts(15, y, "Remain Time");
456: screen.Puts(30, y, "Description");
457: screen.Puts(75, y, "Count");
1.1 root 458: y++;
1.1.1.6 root 459:
1.1.1.21 root 460: for (auto *ev : evman->all) {
1.1 root 461: uint64 rem;
1.1.1.4 root 462: TA attr;
1.1.1.15 root 463:
1.1.1.14 root 464: if (ev->IsRunning()) {
1.1.1.15 root 465: attr = TA::Normal;
1.1.1.14 root 466: if (ev->vtime > vtime) {
467: rem = ev->vtime - vtime;
1.1.1.6 root 468: } else {
469: // XXX 実際には起きないけど、この表示をするにあたって
470: // スケジューラスレッドと一切調停していないので、仮想時刻が
1.1.1.14 root 471: // ev->vtime (イベント発生時刻) をすぎていることがある。
1.1.1.6 root 472: // すぎてるので残り時間 0 と表示しておく。
473: rem = 0;
474: }
1.1 root 475: } else {
1.1.1.15 root 476: attr = TA::Disable;
1.1 root 477: rem = 0;
478: }
1.1.1.14 root 479:
480: if (ev->count != ev->last_count) {
481: attr = TA::Normal;
482: ev->last_count = ev->count;
483: }
484:
485: // 先にイベント回数を表示。
486: // 21文字以上になったら上位桁が長い Description で消されても
487: // 大勢に影響はないだろう。
488: std::string countstr = format_number(ev->count);
489: screen.Print(54, y, attr, "%26s", countstr.c_str());
1.1.1.15 root 490: screen.Print(0, y, attr, "%14s %14s %s ",
1.1.1.14 root 491: SecToStr(ev->time).c_str(),
1.1.1.9 root 492: SecToStr(rem).c_str(),
1.1.1.14 root 493: ev->GetName().c_str());
494: y++;
1.1 root 495: }
496: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.