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