Annotation of nono/vm/scheduler.cpp, revision 1.1.1.9

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.4   root        7: #include "scheduler.h"
                      8: #include "config.h"
1.1.1.3   root        9: #include "mainapp.h"
1.1       root       10: #include "mfp.h"
1.1.1.4   root       11: #include "mpu680x0.h"
                     12: #include "mpu88xx0.h"
                     13: #include "mythread.h"
1.1       root       14: #include "rtc.h"
                     15: #include "vm.h"
                     16: 
                     17: // VM スケジューラは、仮想時間とイベントを管理する。
                     18: //
1.1.1.9 ! root       19: // 仮想時間は電源オン時からの nsec で表し、MPU デバイス (実際はその向こうの
1.1.1.7   root       20: // CPU コア) が管理している。VM 界からは gMPU->GetVirtTime() で取得する。
1.1.1.9 ! root       21: //
        !            22: // 実時間はストップウォッチ gRealtime を基準とし、こちらも nsec で表す。
        !            23: // このストップウォッチ自体は Scheduler 初期化時から常に単調増加する。
        !            24: // 電源オフ中でも RTC へのパルス供給のために単調増加し続けることに注意。
        !            25: //
        !            26: // 「実経過時間」は、GetRealTime() (中身は gRealtime.Elapsed()) で取得でき
        !            27: // o (たぶん)アプリケーション起動時から、常に増加。
        !            28: // o (たぶん)ホストの時刻変更やサスペンドの影響を受けず、常に増加。
        !            29: // o VM 電源オフ時でも進行する。
        !            30: // o デバッガプロンプトで停止中は進行が停止する。
        !            31: // という性質を持つ。
        !            32: // 「仮想経過時間」は、gMPU->GetVirtTime() で取得でき
        !            33: // o 電源オン時のみ有効。
        !            34: // o (今のところ) MPU の命令境界で増加する
        !            35: // という性質を持つ。
        !            36: //
        !            37: // 同期走行モードにおいては rtime_epoch, vtime_epoch をそれぞれの基準点とし
        !            38: // これらと現在の実経過時間、仮想経過時間の差が同期するよう調整する。
        !            39: // 高速走行モードにおいてはこれらは必要ないので特に参照しない。
        !            40: //
        !            41: // 一定間隔で行うパフォーマンス測定は、前回計測時の実および仮想経過時間を
        !            42: // last_perf_rtime, last_perf_vtime として持っているので、これと現在の
        !            43: // 経過時間との比で求める。同期走行/高速走行の影響は受けない。
1.1       root       44: 
                     45: static void *scheduler_run(void *arg);
                     46: 
1.1.1.3   root       47: std::unique_ptr<Scheduler> gScheduler;
1.1       root       48: 
1.1.1.9 ! root       49: // パフォーマンス測定を行う実時間間隔
        !            50: static constexpr uint64 PERF_INTERVAL = 1000_msec;
        !            51: 
        !            52: // 実時間の基準となるストップウォッチ。
        !            53: Stopwatch gRealtime;
        !            54: 
        !            55: // 実経過時間 [nsec] を返す
        !            56: static inline uint64 GetRealTime()
        !            57: {
        !            58:        return gRealtime.Elapsed();
        !            59: }
        !            60: 
        !            61: #define FORMAT_FULL 0
        !            62: #define FORMAT_SEC 1
        !            63: 
        !            64: // 経過時間 t を文字列にして返す。
        !            65: static const std::string
        !            66: TimeToStrF(uint64 t, int format)
        !            67: {
        !            68:        char buf[32];
        !            69:        char *p;
        !            70:        size_t len;
        !            71:        int n;
        !            72: 
        !            73:        uint ns = t % 1000;
        !            74:        t /= 1000;
        !            75:        uint us = t % 1000;
        !            76:        t /= 1000;
        !            77:        uint ms = t % 1000;
        !            78:        t /= 1000;
        !            79: 
        !            80:        uint s, m, h, d;
        !            81:        if (format == FORMAT_FULL) {
        !            82:                s = t % 60;
        !            83:                t /= 60;
        !            84:                m = t % 60;
        !            85:                t /= 60;
        !            86:                h = t % 24;
        !            87:                t /= 24;
        !            88:                d = t;
        !            89:        } else {
        !            90:                s = t;
        !            91:                m = 0;
        !            92:                h = 0;
        !            93:                d = 0;
        !            94:        }
        !            95: 
        !            96:        p = buf;
        !            97:        len = sizeof(buf);
        !            98:        if (d) {
        !            99:                n = snprintf(p, len, "%3dd %02u:%02u:%02u", d, h, m, s);
        !           100:                p += n;
        !           101:                len -= n;
        !           102:        } else if (h) {
        !           103:                n = snprintf(p, len, "%2u:%02u:%02u", h, m, s);
        !           104:                p += n;
        !           105:                len -= n;
        !           106:        } else if (m) {
        !           107:                n = snprintf(p, len, "%2u:%02u", m, s);
        !           108:                p += n;
        !           109:                len -= n;
        !           110:        } else {
        !           111:                n = snprintf(p, len, "%u", s);
        !           112:                p += n;
        !           113:                len -= n;
        !           114:        }
        !           115:        n = snprintf(p, len, ".%03u'%03u'%03u", ms, us, ns);
        !           116:        p += n;
        !           117:        len -= n;
        !           118: 
        !           119:        return std::string(buf, p - buf);
        !           120: }
        !           121: 
        !           122: static const std::string
        !           123: TimeToStr(uint64 t)
        !           124: {
        !           125:        return TimeToStrF(t, FORMAT_FULL);
        !           126: }
        !           127: 
        !           128: static const std::string
        !           129: SecToStr(uint64 t)
        !           130: {
        !           131:        return TimeToStrF(t, FORMAT_SEC);
        !           132: }
        !           133: 
        !           134: //
        !           135: // スケジューラ
        !           136: //
        !           137: 
1.1       root      138: // コンストラクタ
                    139: Scheduler::Scheduler()
                    140: {
                    141:        logname = "scheduler";
                    142:        devname = "Scheduler";
                    143: 
1.1.1.9 ! root      144:        for (int i = 0; i < perfq.Capacity(); i++) {
        !           145:                perfq.Enqueue(1000);
        !           146:        }
        !           147: 
        !           148:        sync_event.dev = this;
        !           149:        sync_event.func = (DeviceCallback_t)&Scheduler::SyncCallback;
        !           150:        sync_event.time = 50_msec;
        !           151:        sync_event.SetName("Scheduler Sync");
1.1       root      152: }
                    153: 
                    154: // デストラクタ
                    155: Scheduler::~Scheduler()
                    156: {
1.1.1.3   root      157:        Terminate();
1.1       root      158: }
                    159: 
                    160: // 初期化
                    161: bool
                    162: Scheduler::Init()
                    163: {
1.1.1.3   root      164:        // 起動時引数指定の高速モード
                    165:        SetFullSpeed(gMainApp.fast_mode);
1.1       root      166: 
                    167:        // スケジューラ(VM)スレッド起動
1.1.1.3   root      168:        pthread_create(&thread, NULL, scheduler_run, NULL);
                    169:        thread_created = true;
1.1       root      170: 
                    171:        return true;
                    172: }
                    173: 
                    174: // スレッドエントリ関数
                    175: void *
                    176: scheduler_run(void *arg)
                    177: {
1.1.1.4   root      178:        PTHREAD_SETNAME("VM");
1.1       root      179: 
                    180:        gScheduler->ThreadRun();
                    181:        return NULL;
                    182: }
                    183: 
                    184: // スレッドエントリ関数(の実体)
                    185: void
                    186: Scheduler::ThreadRun()
                    187: {
1.1.1.9 ! root      188:        // ここが rtime の基準点。
        !           189:        // Stopwatch.Restart() は .Net 由来なので
        !           190:        // Restart() は経過時間をリセットしてからタイマーを開始の意。
        !           191:        gRealtime.Restart();
        !           192:        rtime = 0;
        !           193:        next_rtc_rtime = 31.25_msec;
1.1       root      194: 
1.1.1.3   root      195:        for (;;) {
1.1.1.9 ! root      196:                if (RunOff() == false)
        !           197:                        return;
        !           198:                if (RunOn() == false)
1.1.1.3   root      199:                        return;
1.1.1.9 ! root      200:        }
        !           201: }
        !           202: 
        !           203: // 電源オフ時のスケジューラメインループ。
        !           204: // false ならこのスレッドの終了指示。
        !           205: bool
        !           206: Scheduler::RunOff()
        !           207: {
        !           208:        uint32 oldreq;
        !           209: 
        !           210:        ispower = false;
        !           211: 
        !           212:        oldreq = -1;
        !           213:        for (;;) {
        !           214:                // RunOn() と違ってこっちは処理したフラグだけを下ろす。
        !           215: 
        !           216:                // デバッグ表示も前回から変化した時だけ。
        !           217:                uint32 req = atomic_reqflag.load();
        !           218:                if (__predict_false(req != oldreq)) {
        !           219:                        if (loglevel >= 2) {
        !           220:                                PutlogReqFlag(req);
        !           221:                        }
        !           222:                        oldreq = req;
1.1.1.3   root      223:                }
1.1.1.9 ! root      224: 
        !           225:                // 終了
        !           226:                if ((atomic_reqflag & REQ_EXIT)) {
        !           227:                        return false;
1.1.1.3   root      228:                }
1.1.1.9 ! root      229: 
        !           230:                if ((atomic_reqflag & REQ_RESTART)) {
        !           231:                        // 電源オフ中に RESTART は通常来るはずはないが、来てしまったら
        !           232:                        // メニューから RESTART 操作中に VM が電源オフしてしまったのかも
        !           233:                        // 知れないので、単に電源を入れる。
        !           234:                        atomic_reqflag &= ~REQ_RESTART;
        !           235:                        atomic_reqflag |= REQ_POWER_ON;
        !           236:                }
        !           237: 
        !           238:                // 電源オン指示
        !           239:                if ((atomic_reqflag & REQ_POWER_ON)) {
        !           240:                        // 電源オン要求の場合
        !           241: 
        !           242:                        // 全デバイス電源オン
        !           243:                        for (const auto& d : gDevices) {
        !           244:                                if (!d->PowerOn()) {
        !           245:                                        return false;
        !           246:                                }
        !           247:                        }
        !           248: 
        !           249:                        // 続いて全デバイスをリセット
        !           250:                        for (const auto& d : gDevices) {
        !           251:                                d->ResetHard();
        !           252:                        }
        !           253: 
        !           254:                        // POWER_ON は処理したので下ろす。他のフラグは触らない。
        !           255:                        atomic_reqflag &= ~REQ_POWER_ON;
        !           256:                        // 電源オンは RESET も兼ねる (RESET は RunOn() 内で行う)
        !           257:                        atomic_reqflag |= REQ_RESET;
        !           258: 
        !           259:                        return true;
        !           260:                }
        !           261: 
        !           262:                // RTC はホスト時間で 32Hz (31.25msec) ごとにカウントアップするが、
        !           263:                // usleep() はそこまでの精度がないことと、RunRTC() は前回カウント
        !           264:                // アップ時刻からの差で処理しており入力が多少ぶれても構わないので、
        !           265:                // 適当にその周期の半分強で sleep してみる。
        !           266:                usleep(16_msec / 1_usec);
        !           267: 
        !           268:                // RTC をホスト時間で進める。
        !           269:                // (電源オンするまでは gRealTime は進み続けている)
        !           270:                rtime = GetRealTime();
        !           271:                RunRTC();
1.1       root      272:        }
1.1.1.9 ! root      273:        __unreachable();
        !           274: }
1.1       root      275: 
1.1.1.9 ! root      276: // 電源オン時のスケジューラメインループ。
        !           277: // false ならこのスレッドの終了指示。
        !           278: bool
        !           279: Scheduler::RunOn()
        !           280: {
        !           281:        uint64 last_event_vtime;                        // 直近のイベント発生時刻 (統計用)
        !           282:        Statistics<uint64> event_stat;
        !           283:        uint64 last_sync_rtime;
        !           284: 
        !           285:        ispower = true;
        !           286: 
        !           287:        // パフォーマンス計測は電源オン時のみなので、ここで初期化
        !           288:        last_perf_rtime = rtime;
        !           289:        last_perf_vtime = gMPU->GetVirtTime();
        !           290:        next_perf_rtime = rtime + PERF_INTERVAL;
        !           291:        // 最初の集計が行われる起動後1秒までは、この値が表示されるので
        !           292:        // インチキして 100% にしておく。
        !           293:        perf_counter = 100;
        !           294: 
        !           295:        last_event_vtime = last_perf_vtime;
        !           296: 
        !           297:        // 実時間系の初期化
        !           298:        rtime = GetRealTime();
        !           299:        last_sync_rtime = 0;
        !           300:        overslept = 0;
        !           301:        sync_event.Start();
1.1       root      302: 
                    303:        for (;;) {
1.1.1.9 ! root      304:                vtime = gMPU->GetVirtTime();
        !           305: 
        !           306:                uint32 req = atomic_reqflag.exchange(0);
1.1.1.8   root      307:                if (__predict_false(req != 0)) {
1.1.1.9 ! root      308: 
        !           309:                        // デバッグ表示
        !           310:                        if (loglevel >= 2) {
        !           311:                                PutlogReqFlag(req);
        !           312:                        }
        !           313: 
1.1.1.8   root      314:                        uint32 newmode = mode;
                    315: 
                    316:                        // 動作モード変更要因を一つずつ調べる
                    317:                        if ((req & REQ_CPU_MODE)) {
                    318:                                // CPU 状態が new_cpumode に変更になった
                    319:                                newmode &= ~SCHED_CPU_MASK;
                    320:                                newmode |= new_cpumode;
                    321:                                req &= ~REQ_CPU_MODE;
                    322:                        }
                    323:                        if ((req & REQ_USER_MODE)) {
                    324:                                // ユーザからの高速/同期モード指示が変更になった
                    325:                                newmode &= ~SCHED_SYNC;
                    326:                                newmode |= new_usermode;
                    327:                                req &= ~REQ_USER_MODE;
                    328:                        }
1.1.1.9 ! root      329:                        if ((req & REQ_BOOT_MODE)) {
        !           330:                                // ブートページが変更になった
        !           331:                                newmode &= ~SCHED_BOOT;
        !           332:                                newmode |= new_bootmode;
        !           333:                                req &= ~REQ_BOOT_MODE;
        !           334:                        }
        !           335:                        if ((req & REQ_KEY_MODE)) {
        !           336:                                // キー入力中かどうか
        !           337:                                newmode &= ~SCHED_KEY;
        !           338:                                newmode |= new_keymode;
        !           339:                                req &= ~REQ_KEY_MODE;
        !           340:                        }
        !           341: 
        !           342:                        // いずれかでも変化していれば..
        !           343:                        if (mode != newmode) {
        !           344:                                // 動作モード変更要因を全部調べた結果、変更が必要か。
        !           345:                                // mode、newmode どちらもゼロ(高速モード)か非ゼロ(通常モード)か
        !           346:                                // なので、等値比較ではなく二値にしてから比較。
        !           347:                                if ((bool)mode != (bool)newmode) {
        !           348:                                        // モードが変わるのでここで一旦同期をとる
        !           349:                                        auto over = Sync();
        !           350:                                        if (overslept < over) {
        !           351:                                                overslept = over;
        !           352:                                        }
        !           353: 
        !           354:                                        if (newmode == 0) {
        !           355:                                                // 高速モードに
        !           356:                                                putlog(1, "高速モード");
        !           357: 
        !           358:                                                // 同期モードを抜けた時刻
        !           359:                                                last_sync_rtime = rtime;
        !           360: 
        !           361:                                                // 高速モードでは RAM は直接アクセス
        !           362:                                                ::direct_ram_size = gRAM->GetSize();
        !           363: 
        !           364:                                        } else {
        !           365:                                                // 同期モードに
        !           366:                                                putlog(1, "同期モード");
        !           367: 
        !           368:                                                // 同期モードでは RAM アクセスはデバイス経由
        !           369:                                                ::direct_ram_size = 0;
        !           370: 
        !           371:                                                // 同期用の基準点を設置。
        !           372:                                                // ゲストが STOP 命令による定常状態にいる時は
        !           373:                                                // 割り込みなど短期間だけ高速モードになるのを繰り返す。
        !           374:                                                // スリープ粒度の粗い NetBSD ホストでは同期モードに
        !           375:                                                // 切り替えるたびに基準点を移動していると、スリープ
        !           376:                                                // しすぎてしまうので、短期間だけ高速モードを出入り
        !           377:                                                // した分は基準点を移動せず無視する。
        !           378:                                                if (rtime - last_sync_rtime > overslept) {
        !           379:                                                        rtime_epoch = rtime;
        !           380:                                                        vtime_epoch = vtime;
        !           381:                                                }
        !           382:                                        }
        !           383:                                }
        !           384: 
        !           385:                                // ブール比較の後で、全ビットを維持したまま代入。
1.1.1.8   root      386:                                mode = newmode;
1.1.1.9 ! root      387:                        }
1.1.1.8   root      388: 
1.1.1.9 ! root      389:                        if ((req & REQ_RESTART)) {
        !           390:                                // 電源オン中に RESTART が来たら電源オンを予約して電源オフ。
        !           391:                                // poweroff_callback は GUI にアプリケーションを終了させる
        !           392:                                // コールバックなのでここでは呼ばない。
        !           393:                                atomic_reqflag |= REQ_POWER_ON;
1.1.1.8   root      394: 
1.1.1.9 ! root      395:                                // 全デバイス電源オフ
        !           396:                                for (const auto& d : gDevices) {
        !           397:                                        if (!d->PowerOff()) {
        !           398:                                                return false;
        !           399:                                        }
1.1.1.8   root      400:                                }
1.1       root      401: 
1.1.1.9 ! root      402:                                return true;
1.1       root      403:                        }
                    404: 
                    405:                        if ((req & REQ_POWER_OFF)) {
                    406:                                // 電源オフ要求の場合
                    407: 
                    408:                                // 全デバイス電源オフ
1.1.1.9 ! root      409:                                for (const auto& d : gDevices) {
        !           410:                                        if (!d->PowerOff()) {
        !           411:                                                return false;
        !           412:                                        }
        !           413:                                }
        !           414: 
        !           415:                                // GUI に通知
        !           416:                                if (poweroff_callback) {
        !           417:                                        poweroff_callback();
        !           418:                                }
1.1       root      419: 
1.1.1.9 ! root      420:                                return true;
1.1       root      421:                        }
                    422: 
1.1.1.6   root      423:                        if ((req & REQ_RESET)) {
                    424:                                // リセット要求の場合
                    425: 
                    426:                                // 全デバイスをリセット
1.1.1.9 ! root      427:                                for (const auto& d : gDevices) {
        !           428:                                        d->ResetHard();
        !           429:                                }
1.1.1.6   root      430: 
                    431:                                req &= ~REQ_RESET;
                    432:                        }
                    433: 
1.1       root      434:                        if ((req & REQ_EXIT)) {
                    435:                                // 終了要求
1.1.1.9 ! root      436:                                return false;
1.1       root      437:                        }
                    438: 
1.1.1.9 ! root      439:                        // これいるかな?
1.1.1.3   root      440:                        assertmsg(req == 0, "req=$%x", req);
1.1       root      441:                }
                    442: 
1.1.1.9 ! root      443:                // パフォーマンス表示
        !           444:                if (__predict_false(rtime >= next_perf_rtime)) {
        !           445:                        uint64 rperf = rtime - last_perf_rtime;
        !           446:                        uint64 vperf = vtime - last_perf_vtime;
        !           447: 
        !           448:                        // 百分率で小数以下1桁分までとなるよう整数で計算する
        !           449:                        perfq.EnqueueForce((uint32)(vperf * 1000 / rperf));
        !           450:                        // 移動平均を求める (perfq は常に要素数分埋めてある)
        !           451:                        int ma = 0;
        !           452:                        int qlen = perfq.Length();
        !           453:                        for (int i = 0; i < qlen; i++) {
        !           454:                                auto v = perfq.Peek(i);
        !           455:                                ma += v;
        !           456:                        }
        !           457:                        ma /= qlen;
        !           458:                        // 最下位を四捨五入して捨てる (整数%になる)
        !           459:                        ma = (ma + 5) / 10;
        !           460:                        perf_counter = ma;
        !           461: 
        !           462:                        // 直近1秒の Sync 回数
        !           463:                        last_sync_count = sync_count;
        !           464:                        sync_count = 0;
        !           465: 
        !           466:                        // 直近1秒のイベント統計
        !           467:                        last_event_stat = event_stat;
        !           468:                        event_stat.Clear();
        !           469: 
        !           470:                        last_perf_rtime = rtime;
        !           471:                        last_perf_vtime = vtime;
        !           472:                        next_perf_rtime = rtime + PERF_INTERVAL;
1.1       root      473:                }
                    474: 
1.1.1.6   root      475:                // イベントリストを調べ現時点から最も早く発生するイベントまでの必要
1.1.1.7   root      476:                // 時間を求めて、この時間分だけ MPU を駆動する。
1.1.1.6   root      477:                // MPU 実行中にデバイスアクセスなどによってスケジューラにイベントが
                    478:                // 登録されると、その命令の終了とともに処理を打ち切って戻ってくる。
1.1.1.7   root      479: 
                    480:                // delta_vtime が今回進める仮想時間。
                    481:                // とりあえず初期値を仮置きしとくけど、イベントは通常 20msec 以内には
                    482:                // 必ず起きるので (例えば LUNA の sysclk は 10 or 16 msec 周期)、
                    483:                // この初期値は実質上限値。20msec 程度より大きければいくらでもよくて
                    484:                // 100msec とかでいいんだけど、二進数で置数しやすいよう
                    485:                // 0x200'0000 (約33.5msec) にしておく。
                    486:                uint64 delta_vtime = 0x200'0000;
1.1       root      487: 
                    488:                // イベント
                    489:                for (;;) {
1.1.1.6   root      490:                        Event *e;
                    491:                        // ロック区間
                    492:                        {
                    493:                                std::lock_guard<std::mutex> lock(evcs);
                    494: 
1.1.1.9 ! root      495:                                if (eventq.empty()) {
1.1.1.6   root      496:                                        break;
                    497:                                }
1.1.1.9 ! root      498:                                e = eventq.top();
1.1.1.7   root      499:                                if (e->vtime > vtime) {
1.1.1.6   root      500:                                        // まだ時刻に到達していない
1.1.1.7   root      501:                                        delta_vtime = e->vtime - vtime;
1.1.1.6   root      502:                                        break;
                    503:                                }
                    504: 
                    505:                                // 到達したのでこのイベントをリストから削除
                    506:                                e->active = false;
1.1.1.9 ! root      507:                                eventq.pop();
1.1       root      508:                        }
                    509: 
1.1.1.9 ! root      510:                        // イベント発生間隔 (統計情報)
        !           511:                        // t は前回と今回のイベント予定時刻の間隔。
        !           512:                        uint64 t = e->vtime - last_event_vtime;
        !           513:                        event_stat.Add(t);
        !           514:                        last_event_vtime = e->vtime;
        !           515: 
1.1       root      516:                        // コールバック
1.1.1.8   root      517:                        e->dev->putlogf(3, lstr("イベント '%s' 時刻到達",
                    518:                                e->GetName().c_str()));
1.1.1.6   root      519:                        ((e->dev)->*(e->func))(*e);
1.1       root      520: 
                    521:                        // 同時刻のイベントがあるかも知れないのでなくなるまで調べる
                    522:                }
1.1.1.6   root      523: 
1.1.1.9 ! root      524:                // RTC をホスト時間で進める
        !           525:                // (同期イベントによって sleep したかもしれないので、その後がよかろう)
        !           526:                RunRTC();
        !           527: 
1.1.1.7   root      528:                // CPU を駆動
1.1.1.8   root      529:                new_cpumode = gMPU->Run(delta_vtime);
1.1.1.7   root      530: 
1.1.1.9 ! root      531:                // CPU 側の走行状態が変わったら、このループに通知
        !           532:                if (__predict_false(((mode ^ new_cpumode) & SCHED_CPU_MASK))) {
        !           533:                        atomic_reqflag |= REQ_CPU_MODE;
        !           534:                }
        !           535:        }
        !           536:        __unreachable();
        !           537: }
1.1       root      538: 
1.1.1.9 ! root      539: // 同期イベント
        !           540: void
        !           541: Scheduler::SyncCallback(Event& ev)
        !           542: {
        !           543:        Sync();
        !           544: 
        !           545:        // 次回のイベントを再登録
        !           546:        ev.Start();
        !           547: }
1.1       root      548: 
1.1.1.9 ! root      549: // 同期処理
        !           550: // ここで仮想時間を実時間と同期させる。
        !           551: // スリープしすぎた実時間を返す。
        !           552: uint64
        !           553: Scheduler::Sync()
        !           554: {
        !           555:        uint64 over = 0;
        !           556: 
        !           557:        // 実時間をここで更新
        !           558:        rtime = GetRealTime();
        !           559: 
        !           560:        // 統計情報
        !           561:        sync_count++;
        !           562: 
        !           563:        // 同期モードなら..
        !           564:        if (mode != 0) {
        !           565:                uint64 rspan = rtime - rtime_epoch;
        !           566:                uint64 vspan = vtime - vtime_epoch;
        !           567:                if (vspan > rspan) {
        !           568:                        // 仮想時間のほうが進んでいれば、スリープして待つ
        !           569:                        uint64 diff = vspan - rspan;
        !           570:                        struct timespec ts;
        !           571:                        ts.tv_sec  = diff / (1000 * 1000 * 1000);
        !           572:                        ts.tv_nsec = diff % (1000 * 1000 * 1000);
        !           573:                        nanosleep(&ts, NULL);
        !           574: 
        !           575:                        // スリープしすぎた時間を計測。
        !           576:                        // この時間にはスリープ時間を計算するための時間も含む。
        !           577:                        uint64 prev = rtime;
        !           578:                        rtime = GetRealTime();
        !           579:                        uint64 realslept = rtime - prev;
        !           580:                        if (realslept > diff) {
        !           581:                                over = realslept - diff;
1.1       root      582:                        }
1.1.1.9 ! root      583:                } else {
        !           584:                        // 実時間のほうが進んでいれば、間に合ってない
1.1       root      585: 
1.1.1.9 ! root      586:                        // XXX ここで間引き運転とか
1.1       root      587:                }
                    588:        }
1.1.1.9 ! root      589:        return over;
        !           590: }
        !           591: 
        !           592: // RTC は常にホスト時刻で動いており
        !           593: // 32Hz = 31.25msec ごとにパルスを入れる。
        !           594: // VM の電源がオフでも実行する。
        !           595: void
        !           596: Scheduler::RunRTC()
        !           597: {
        !           598:        while (rtime >= next_rtc_rtime) {
        !           599:                gRTC->ClockIn();
        !           600:                next_rtc_rtime += 31.25_msec;
        !           601:        }
1.1       root      602: }
                    603: 
1.1.1.3   root      604: // 必要ならスレッドの終了を指示して完了まで待つ。
1.1       root      605: void
                    606: Scheduler::Terminate()
                    607: {
1.1.1.3   root      608:        if (thread_created) {
1.1.1.8   root      609:                RequestExit();
1.1.1.3   root      610:                pthread_join(thread, NULL);
                    611:                thread_created = false;
                    612:        }
1.1       root      613: }
                    614: 
1.1.1.8   root      615: // スレッド終了を指示する。
                    616: void
                    617: Scheduler::RequestExit()
                    618: {
                    619:        atomic_reqflag |= REQ_EXIT;
                    620: }
                    621: 
1.1       root      622: // 動作モードを設定する。
                    623: // true なら高速モード、false なら同期モード。
                    624: void
                    625: Scheduler::SetFullSpeed(bool enable)
                    626: {
                    627:        if (enable) {
1.1.1.8   root      628:                new_usermode = 0;
1.1       root      629:        } else {
1.1.1.8   root      630:                new_usermode = SCHED_SYNC;
1.1       root      631:        }
1.1.1.8   root      632:        atomic_reqflag |= REQ_USER_MODE;
1.1       root      633: }
                    634: 
1.1.1.9 ! root      635: // ブートページモードを設定する。
        !           636: // 戻り値は現在の動作モードがブートモード(ROM)なら true。
        !           637: // (VM::SwitchBootPage から呼ぶので個別には呼ばないこと)
        !           638: void
        !           639: Scheduler::RequestBootPageMode(bool isrom)
        !           640: {
        !           641:        if (isrom) {
        !           642:                new_bootmode = SCHED_BOOT;
        !           643:        } else {
        !           644:                new_bootmode = 0;
        !           645:        }
        !           646:        atomic_reqflag |= REQ_BOOT_MODE;
        !           647: }
        !           648: 
        !           649: // キー入力中かどうかを設定する。
        !           650: // true ならキー入力中、false なら入力中でない。
        !           651: void
        !           652: Scheduler::RequestKeyPressed(bool pressed)
        !           653: {
        !           654:        if (pressed) {
        !           655:                new_keymode = SCHED_KEY;
        !           656:        } else {
        !           657:                new_keymode = 0;
        !           658:        }
        !           659:        atomic_reqflag |= REQ_KEY_MODE;
        !           660: }
        !           661: 
        !           662: // 電源オフ時のコールバックを設定する
        !           663: void
        !           664: Scheduler::SetPowerOffCallback(void (*callback)())
        !           665: {
        !           666:        poweroff_callback = callback;
        !           667: }
        !           668: 
        !           669: // REQ_* フラグのデバッグ表示
        !           670: void
        !           671: Scheduler::PutlogReqFlag(uint32 req) const
        !           672: {
        !           673:        static const char * const cpumode_str[] = {
        !           674:                "Normal",
        !           675:                "STOP",
        !           676:                "HALT",
        !           677:                "HALTXXX",
        !           678:        };
        !           679:        char buf[128];
        !           680:        int len = 0;
        !           681: 
        !           682:        buf[0] = '\0';
        !           683: 
        !           684: #define SET(FL, MSG)   \
        !           685:        if ((req & (FL))) len += snprintf(buf + len, sizeof(buf) - len, ",%s", MSG)
        !           686: 
        !           687:        SET(REQ_EXIT,           "EXIT");
        !           688:        SET(REQ_RESTART,        "RESTART");
        !           689:        SET(REQ_RESET,          "RESET");
        !           690:        SET(REQ_POWER_OFF,      "POWER_OFF");
        !           691:        SET(REQ_POWER_ON,       "POWER_ON");
        !           692:        if ((req & REQ_USER_MODE)) {
        !           693:                len += snprintf(buf + len, sizeof(buf) - len, ",USER:%s",
        !           694:                        (new_usermode == 0) ? "Fast" : "Sync");
        !           695:        }
        !           696:        if ((req & REQ_CPU_MODE)) {
        !           697:                len += snprintf(buf + len, sizeof(buf) - len, ",CPU:%s",
        !           698:                        cpumode_str[new_cpumode & 3]);
        !           699:        }
        !           700:        if ((req & REQ_BOOT_MODE)) {
        !           701:                len += snprintf(buf + len, sizeof(buf) - len, ",BOOT:%s",
        !           702:                        (new_bootmode == 0) ? "Normal" : "Boot");
        !           703:        }
        !           704:        if ((req & REQ_KEY_MODE)) {
        !           705:                len += snprintf(buf + len, sizeof(buf) - len, ",KEY:%s",
        !           706:                        (new_keymode == 0) ? "-" : "Pressed");
        !           707:        }
        !           708: 
        !           709:        if (buf[0] != '\0') {
        !           710:                buf[0] = '<';
        !           711:                strlcat(buf, ">", sizeof(buf));
        !           712:        }
        !           713:        putlogn("%sreq=$%04x%s", (ispower ? "" : "Off "), req, buf);
        !           714: }
        !           715: 
1.1.1.3   root      716: // 指定のイベントを開始する。
1.1       root      717: // すでに同イベントが登録されている場合は古いイベントを削除してから
                    718: // 新しいイベントを再登録となる。
                    719: // イベントはワンショットのみ。
                    720: void
1.1.1.3   root      721: Scheduler::StartEvent(Event *newev)
1.1       root      722: {
                    723:        bool updated = false;
                    724: 
1.1.1.7   root      725:        // time は仮想相対時間 [nsec]、vtime は仮想絶対時刻 [nsec]
                    726:        newev->vtime = gMPU->GetVirtTime() + newev->time;
1.1       root      727: 
                    728:        evcs.lock();
1.1.1.9 ! root      729:        // すでにあれば削除して...
1.1       root      730:        if (newev->active) {
1.1.1.9 ! root      731:                updated = eventq.Erase(newev);
1.1       root      732:        }
1.1.1.9 ! root      733:        // 自身を追加
        !           734:        eventq.push(newev);
1.1       root      735:        newev->active = true;
                    736: 
1.1.1.9 ! root      737:        // 先頭なら再スケジュールのため CPU 実行をここで打ち切り
        !           738:        if (eventq.top() == newev) {
        !           739:                gMPU->Release();
        !           740:        }
        !           741:        evcs.unlock();
1.1       root      742: 
1.1.1.8   root      743:        newev->dev->putlogf(3, lstr("イベント '%s' %s %d.%03d usec 後",
1.1.1.2   root      744:                newev->GetName().c_str(),
1.1.1.3   root      745:                updated ? "更新" : "開始",
1.1       root      746:                (int)(newev->time / 1000),
1.1.1.8   root      747:                (int)(newev->time % 1000)));
1.1       root      748: }
                    749: 
1.1.1.3   root      750: // 指定のイベントを停止する。
1.1       root      751: // 指定されたイベントが登録されていなければ何もしない。
                    752: void
1.1.1.3   root      753: Scheduler::StopEvent(Event *event)
1.1       root      754: {
                    755:        bool found = false;
                    756: 
                    757:        evcs.lock();
1.1.1.9 ! root      758:        found = eventq.Erase(event);
1.1       root      759:        evcs.unlock();
1.1.1.9 ! root      760:        event->active = false;
1.1       root      761: 
1.1.1.3   root      762:        // イベントを停止した場合は MPU の実行中断はしなくてよい。
1.1       root      763:        // 1msec 後にイベントを追加した後、やっぱりそのイベントを取り消した
                    764:        // 場合 (LUNA の電源オフとか) はここで MPU 処理を中断するよりも
                    765:        // そのまま 1msec 走って問題ない。
                    766: 
                    767:        if (found) {
1.1.1.8   root      768:                event->dev->putlogf(3, lstr("イベント  '%s' 停止",
                    769:                        event->GetName().c_str()));
1.1       root      770:        }
                    771: }
                    772: 
                    773: 
1.1.1.5   root      774: // モニターサイズ取得
                    775: nnSize
                    776: Scheduler::GetMonitorSize()
                    777: {
                    778:        // 本当は Init() 完了時点でイベント個数は確定しているはずだけど、
                    779:        // そのタイミングで一度だけ呼ばれるフックが今の所ない。
1.1.1.9 ! root      780:        return nnSize(72, 10 + gEvents.size());
1.1.1.5   root      781: }
                    782: 
                    783: void
                    784: Scheduler::MonitorUpdate(TextScreen& monitor)
1.1       root      785: {
                    786:        int x, y;
                    787: 
                    788:        monitor.Clear();
                    789:        x = 0;
                    790:        y = 0;
                    791: 
1.1.1.7   root      792:        int clock_khz = gMPU->GetClockSpeed();
                    793: 
1.1.1.8   root      794:        monitor.Print(0, y, "Mode: 0x%04x (", mode);
                    795:        if ((mode & SCHED_SYNC)) {
                    796:                monitor.Puts(14, y, TA::On, "Sync");
                    797:        } else {
                    798:                monitor.Puts(14, y, "Full");
                    799:        }
                    800:        switch (mode & SCHED_CPU_MASK) {
                    801:         case SCHED_CPU_NORMAL:
                    802:                monitor.Puts(19, y, "Run");
                    803:                break;
                    804:         case SCHED_CPU_STOP:
                    805:                monitor.Puts(19, y, TA::On, "STOP");
                    806:                break;
                    807:         case SCHED_CPU_HALT:
                    808:                monitor.Puts(19, y, TA::On, "HALT");
                    809:                break;
                    810:        }
1.1.1.9 ! root      811:        monitor.Puts(24, y, TA::OnOff((mode & SCHED_BOOT)), "Boot");
        !           812:        monitor.Puts(29, y, TA::OnOff((mode & SCHED_KEY)), "Key");
        !           813:        monitor.Puts(33, y, ")");
1.1.1.8   root      814:        y++;
1.1       root      815:        monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag);
                    816:        monitor.Print(0, y++, "MPU Speed: %d.%03dMHz",
1.1.1.7   root      817:                (clock_khz / 1000), (clock_khz % 1000));
1.1.1.9 ! root      818:        uint64 rt = rtime;
        !           819:        uint64 vt = vtime;
        !           820: 
        !           821:        auto s_rt = TimeToStr(rt);
        !           822:        auto s_vt = TimeToStr(vt);
        !           823:        std::string s_sync_rt {};
        !           824:        std::string s_sync_vt {};
        !           825:        if (mode != 0) {
        !           826:                uint64 sync_rt = rtime - rtime_epoch;
        !           827:                uint64 sync_vt = vtime - vtime_epoch;
        !           828:                s_sync_rt = TimeToStr(sync_rt);
        !           829:                s_sync_vt = TimeToStr(sync_vt);
        !           830:        }
        !           831:        // 日 まで表示しようとするときは桁位置を動かす
        !           832:        int width = std::max(rt, vt) > 86400_sec ? 25 : 20;
        !           833: 
        !           834:        monitor.Print(0, y++, "Total  Real Time: %*s", width, s_rt.c_str());
        !           835:        monitor.Print(4, y++,     "Virtual Time: %*s", width, s_vt.c_str());
        !           836:        monitor.Print(0, y++, "Sync   Real Time: %*s", width, s_sync_rt.c_str());
        !           837:        monitor.Print(4, y++,     "Virtual Time: %*s", width, s_sync_vt.c_str());
        !           838: 
        !           839:        monitor.Print(0, y++, "Performance     : %4d%%", perf_counter);
        !           840: 
        !           841:        x = 44;
        !           842:        y = 0;
        !           843:        monitor.Print(x, y++, "Max OverSlept: %13s", SecToStr(overslept).c_str());
        !           844:        monitor.Print(x, y++, "Sync per sec :          %4u", last_sync_count);
        !           845:        monitor.Print(x, y++, "Event per sec:      %8u",
        !           846:                (uint)last_event_stat.Count());
        !           847:        monitor.Print(x, y++, "Evt Intvl Min: %13s",
        !           848:                SecToStr(last_event_stat.Min()).c_str());
        !           849:        monitor.Print(x, y++, "Evt Intvl Avg: %13s",
        !           850:                SecToStr(last_event_stat.Average()).c_str());
        !           851:        monitor.Print(x, y++, "Evt Intvl Max: %13s",
        !           852:                SecToStr(last_event_stat.Max()).c_str());
1.1       root      853: 
                    854:        // 0         1         2         3         4         5         6
1.1.1.9 ! root      855:        // 01234567890123456789012345678901234567890123456789012345678901234568
        !           856:        // Set Time       Remain Time    Description
        !           857:        // 3.123'456'789  3.123'456'789  012345678901234567890123456789
1.1       root      858:        x = 0;
1.1.1.9 ! root      859:        y = 9;
        !           860:        monitor.Puts(x +  0, y, "Event Time");
        !           861:        monitor.Puts(x + 15, y, "Remain Time");
        !           862:        monitor.Puts(x + 30, y, "Description");
1.1       root      863:        y++;
1.1.1.6   root      864: 
1.1       root      865:        for (const auto& e : gEvents) {
                    866:                uint64 rem;
1.1.1.4   root      867:                TA attr;
1.1       root      868:                if (e->active) {
1.1.1.4   root      869:                        attr = TA::Normal;
1.1.1.7   root      870:                        if (e->vtime > vtime) {
                    871:                                rem = e->vtime - vtime;
1.1.1.6   root      872:                        } else {
                    873:                                // XXX 実際には起きないけど、この表示をするにあたって
                    874:                                // スケジューラスレッドと一切調停していないので、仮想時刻が
                    875:                                // e->cycle (イベント発生時刻) をすぎていることがある。
                    876:                                // すぎてるので残り時間 0 と表示しておく。
                    877:                                rem = 0;
                    878:                        }
1.1       root      879:                } else {
                    880:                        attr = TA::Disable;
                    881:                        rem = 0;
                    882:                }
                    883:                monitor.Print(x, y++, attr,
1.1.1.9 ! root      884:                        "%13s  %13s  %-30s",
        !           885:                        SecToStr(e->time).c_str(),
        !           886:                        SecToStr(rem).c_str(),
        !           887:                        e->GetName().c_str());
1.1       root      888:        }
                    889: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.