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

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

unix.superglobalmegacorp.com

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