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