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