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