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

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2017 [email protected]
                      4: //
                      5: 
1.1.1.3 ! root        6: #include "mainapp.h"
1.1       root        7: #include "mfp.h"
                      8: #include "mpu.h"
                      9: #include "rtc.h"
                     10: #include "scheduler.h"
1.1.1.3 ! root       11: #include "config.h"
1.1       root       12: #include "vm.h"
                     13: 
                     14: // VM スケジューラは、仮想時間とイベントを管理する。
                     15: //
                     16: // 仮想時間は MPU の起動時からのクロックサイクルの経過数で表すこととする。
                     17: // このエミュレータはシングルプロセッサシステムのみがターゲットであり、
                     18: // 実行中に動作クロックが変わることはないので、これで問題ない。
                     19: // VM 界からは gMPU->total_cycle() で取得する。
                     20: 
                     21: static void *scheduler_run(void *arg);
                     22: 
1.1.1.3 ! root       23: std::unique_ptr<Scheduler> gScheduler;
1.1       root       24: 
                     25: // コンストラクタ
                     26: Scheduler::Scheduler()
                     27: {
                     28:        logname = "scheduler";
                     29:        devname = "Scheduler";
1.1.1.2   root       30:        monitor.Init(68, 30);
1.1       root       31: 
                     32:        eventlist.clear();
1.1.1.3 ! root       33: 
        !            34:        // 機種ごとに MPU クロックの初期値は異なる。単位は MHz。
        !            35:        int defval;
        !            36:        switch (gMainApp.GetVMType()) {
        !            37:         case VMTYPE_X68030:
        !            38:         case VMTYPE_LUNA88K:
        !            39:                defval = 25;
        !            40:                break;
        !            41:         case VMTYPE_LUNA:
        !            42:                defval = 20;
        !            43:                break;
        !            44:         default:
        !            45:                PANIC("unsupported vmtype");
        !            46:        }
        !            47:        gConfig->Get("mpu-clock").SetDefault(defval);
1.1       root       48: }
                     49: 
                     50: // デストラクタ
                     51: Scheduler::~Scheduler()
                     52: {
1.1.1.3 ! root       53:        Terminate();
1.1       root       54: }
                     55: 
                     56: // 初期化
                     57: // ここで mpu_clock が(非ゼロに)初期化されるとこれ以降 putlog() が使える。
                     58: // そのため、Scheduler デバイスを必ずデバイスリストの先頭に置くことで、
                     59: // これ以外のオブジェクトの Init() は putlog() を使ってよいとできる。
                     60: bool
                     61: Scheduler::Init()
                     62: {
1.1.1.3 ! root       63:        const ConfigItem& item = gConfig->Get("mpu-clock");
        !            64:        const std::string& val = item.AsString();
        !            65:        if (val.empty()) {
        !            66:                item.Err();
        !            67:                return false;
        !            68:        }
        !            69:        // 文字列を double に変換
        !            70:        char *end;
        !            71:        errno = 0;
        !            72:        double f = strtod(val.c_str(), &end);
        !            73:        if (end == val.c_str() || end[0] != '\0' || errno == ERANGE) {
        !            74:                item.Err();
        !            75:                return false;
1.1       root       76:        }
1.1.1.3 ! root       77:        // 設定の "mpu-clock" は MHz 単位だが、変数 mpu_clock は kHz 単位。
        !            78:        mpu_clock = (int)(f * 1000);
        !            79:        // 1MHz 以下はエラー。実際 10MHz 以下でもいい気がするけど
        !            80:        if (mpu_clock < 1000) {
        !            81:                item.Err();
1.1       root       82:                return false;
                     83:        }
1.1.1.3 ! root       84: 
        !            85:        // 起動時引数指定の高速モード
        !            86:        SetFullSpeed(gMainApp.fast_mode);
1.1       root       87: 
                     88:        // スケジューラ(VM)スレッド起動
1.1.1.3 ! root       89:        pthread_create(&thread, NULL, scheduler_run, NULL);
        !            90:        thread_created = true;
1.1       root       91: 
                     92:        return true;
                     93: }
                     94: 
                     95: // スケジューラを開始する
                     96: void
                     97: Scheduler::Run()
                     98: {
                     99:        std::unique_lock<std::mutex> lock(cvmtx);
                    100:        atomic_reqflag |= REQ_POWER_ON;
                    101:        cv.notify_one();
                    102: }
                    103: 
                    104: // スレッドエントリ関数
                    105: void *
                    106: scheduler_run(void *arg)
                    107: {
                    108:        pthread_setname_np("VM");
                    109: 
                    110:        gScheduler->ThreadRun();
                    111:        return NULL;
                    112: }
                    113: 
                    114: // スレッドエントリ関数(の実体)
                    115: void
                    116: Scheduler::ThreadRun()
                    117: {
                    118:        uint64 cycle;   // 今回実行するサイクル数
1.1.1.3 ! root      119:        uint32 req;
1.1       root      120: 
                    121:        // 最初の電源オンを待つ
1.1.1.3 ! root      122:        for (;;) {
1.1       root      123:                std::unique_lock<std::mutex> lock(cvmtx);
1.1.1.3 ! root      124:                cv.wait(lock, [&] { return (atomic_reqflag != 0); });
        !           125:                req = atomic_reqflag.load();
        !           126:                // 終了
        !           127:                if ((req & REQ_EXIT)) {
        !           128:                        return;
        !           129:                }
        !           130:                // REQ_POWER_ON なら他のフラグを書き戻して抜ける。
        !           131:                // REQ_{SYNC|FAST} は以降のメインループで処理するため。
        !           132:                if ((req & REQ_POWER_ON)) {
        !           133:                        req &= ~REQ_POWER_ON;
        !           134:                        atomic_reqflag.store(req);
        !           135:                        break;
        !           136:                }
1.1       root      137:        }
                    138:        // ここからは電源オン
                    139: 
                    140:        // RTC 時刻だけは最初から必要。
                    141:        rtimestart = GetRealTime();
                    142:        rtc_last_clock = rtimestart;
                    143: 
                    144:        cycle = INT64_MAX;
                    145:        mode = 0;
                    146:        for (;;) {
                    147:                req = atomic_reqflag.exchange(0);
                    148:                if (req) {
                    149:                        if ((req & REQ_SYNC)) {
                    150:                                // スケジューラを同期モードに
                    151:                                mode |= SCHED_SYNC;
                    152:                        }
                    153:                        if ((req & REQ_FAST)) {
                    154:                                // スケジューラを高速モードに
                    155:                                mode &= ~SCHED_SYNC;
                    156:                        }
                    157:                        if ((req & REQ_RUN)) {
                    158:                                // MPU が通常状態になった
                    159:                                mode &= ~SCHED_STOP;
                    160:                        }
                    161:                        if ((req & REQ_STOP)) {
                    162:                                // MPU が STOP 状態になった
                    163:                                mode |= SCHED_STOP;
                    164:                        }
                    165:                        if ((req & REQ_MODEMASK)) {
                    166:                                // mode がどれかにでも変わったら
                    167:                                static const char *modestr[] = {
                    168:                                        "高速",
                    169:                                        "高速 & STOP状態",
                    170:                                        "通常",
                    171:                                        "通常 & STOP状態",
                    172:                                };
                    173:                                putlog(1, "モード変更 mode=%d (%s)",  mode, modestr[mode]);
                    174: 
                    175:                                // 基準時刻をリセット
                    176:                                rtimebase = ::GetRealTime();
                    177:                                vtimebase = GetVirtTime();
                    178: 
                    179:                                // 該当ビットを落としておく
                    180:                                req &= ~REQ_MODEMASK;
                    181:                        }
                    182: 
                    183:                        if ((req & REQ_POWER_OFF)) {
                    184:                                // 電源オフ要求の場合
                    185: 
                    186:                                // 全デバイス電源オフ
                    187:                                gVM->DevicePowerOff();
                    188: 
                    189:                                req &= ~REQ_POWER_OFF;
                    190:                        }
                    191: 
                    192:                        if ((req & REQ_EXIT)) {
                    193:                                // 終了要求
                    194:                                // この for ブロックをいきなり抜けるだけなので
                    195:                                // もうフラグの値も関係ないのだが一応。
                    196:                                req &= ~REQ_EXIT;
                    197:                                break;
                    198:                        }
                    199: 
                    200:                        // 電源オン要求は来ないはず
1.1.1.3 ! root      201:                        assertmsg(req == 0, "req=$%x", req);
1.1       root      202:                }
                    203: 
                    204:                // RTC は常にホスト時刻で動いており
                    205:                // 32Hz = 31.25msec ごとにパルスを入れる。
                    206:                while (::GetRealTime() > rtc_last_clock + 31.25_msec) {
                    207:                        gRTC->ClockIn();
                    208:                        rtc_last_clock += 31.25_msec;
                    209:                }
                    210: 
                    211:                // まず CPU を駆動する。MPU::Run() はデバイスアクセスなどによって
                    212:                // スケジューラにイベントが登録されると、その命令の終了とともに
                    213:                // 処理を打ち切って戻ってくる。
                    214:                // ここでイベントリストを調べ、その時点から最も早く発生するイベント
                    215:                // までの必要サイクル数が、次のループでの駆動サイクル数になる。
                    216: 
                    217:                // CPU 駆動
                    218:                uint32 outer;
                    219:                outer = gMPU->Run(cycle);
                    220: 
                    221:                // イベント
                    222:                for (;;) {
                    223:                        evcs.lock();
                    224:                        if (eventlist.empty()) {
                    225:                                cycle = INT64_MAX;
                    226:                                break;
                    227:                        }
                    228:                        Event *e = eventlist.front();
                    229:                        if (gMPU->total_cycle() < e->cycle) {
                    230:                                // まだ時刻に到達していない
                    231:                                cycle = e->cycle - gMPU->total_cycle();
                    232:                                break;
                    233:                        }
                    234: 
                    235:                        // 到達したのでこのイベントをリストから削除
                    236:                        e->active = false;
                    237:                        eventlist.pop_front();
                    238:                        evcs.unlock();
                    239: 
                    240:                        // コールバック
1.1.1.3 ! root      241:                        e->dev->putlog(3, "イベント '%s' 時刻到達", e->GetName().c_str());
1.1       root      242:                        ((e->dev)->*(e->func))(e->code);
                    243: 
                    244:                        // 同時刻のイベントがあるかも知れないのでなくなるまで調べる
                    245:                }
                    246:                evcs.unlock();
                    247: 
                    248:                // 同期モードなら実時間調整。
                    249:                // あるいは高速モードであってもストップ状態なら実時間駆動する。
                    250:                if (mode != 0) {
                    251:                        // 通常モード
                    252:                        uint64 vtime;
                    253:                        uint64 rtime;
                    254: 
                    255:                        // 基準時からの経過時間
                    256:                        vtime = GetVirtTime() - vtimebase;
                    257:                        rtime = GetRealTime() - rtimebase;
                    258: 
                    259:                        if (vtime > rtime) {
                    260:                                // 仮想時間のほうが進んでいれば、スリープして待つ
                    261:                                uint64 diff = vtime - rtime;
                    262:                                struct timespec ts;
                    263:                                ts.tv_sec  = diff / (1000 * 1000 * 1000);
                    264:                                ts.tv_nsec = diff % (1000 * 1000 * 1000);
                    265:                                nanosleep(&ts, NULL);
                    266:                        } else {
                    267:                                // 実時間のほうが進んでいれば、間に合ってない
                    268: 
                    269:                                // XXX ここで間引き運転とか
                    270:                        }
                    271:                }
                    272: 
                    273:                // CPU の STOP 状態をこっちのモードに反映。
                    274:                // このために SCHED_STOP と CPU_REQ_STOP は同じビット位置にしてある。
                    275:                if (((mode ^ outer) & SCHED_STOP)) {
                    276:                        // 状態変更要求
                    277:                        if ((outer & CPU_REQ_STOP)) {
                    278:                                atomic_reqflag |= REQ_STOP;
                    279:                        } else {
                    280:                                atomic_reqflag |= REQ_RUN;
                    281:                        }
                    282:                }
                    283:        }
                    284: }
                    285: 
1.1.1.3 ! root      286: // 必要ならスレッドの終了を指示して完了まで待つ。
1.1       root      287: void
                    288: Scheduler::Terminate()
                    289: {
1.1.1.3 ! root      290:        if (thread_created) {
        !           291:                atomic_reqflag |= REQ_EXIT;
        !           292:                pthread_join(thread, NULL);
        !           293:                thread_created = false;
        !           294:        }
1.1       root      295: }
                    296: 
                    297: // 動作モードを設定する。
                    298: // true なら高速モード、false なら同期モード。
                    299: void
                    300: Scheduler::SetFullSpeed(bool enable)
                    301: {
                    302:        if (enable) {
                    303:                atomic_reqflag |= REQ_FAST;
                    304:        } else {
                    305:                atomic_reqflag |= REQ_SYNC;
                    306:        }
                    307: }
                    308: 
1.1.1.3 ! root      309: // 指定のイベントを開始する。
1.1       root      310: // すでに同イベントが登録されている場合は古いイベントを削除してから
                    311: // 新しいイベントを再登録となる。
                    312: // イベントはワンショットのみ。
                    313: void
1.1.1.3 ! root      314: Scheduler::StartEvent(Event *newev)
1.1       root      315: {
                    316:        bool inserted = false;
                    317:        bool updated = false;
                    318: 
                    319:        // 相対 time から絶対 cycle を計算
                    320:        newev->cycle = gMPU->total_cycle() + Vtime2Cycle(newev->time);
                    321: 
                    322:        evcs.lock();
                    323:        // すでにあれば削除
                    324:        if (newev->active) {
                    325:                for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    326:                        Event *e = *it;
                    327:                        if (e == newev) {
                    328:                                eventlist.erase(it);
                    329:                                updated = true;
                    330:                                break;
                    331:                        }
                    332:                }
                    333:        }
                    334:        // ソートされているところに自身を挿入
                    335:        for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    336:                Event *e = *it;
                    337:                if (newev->cycle <= e->cycle) {
                    338:                        // このイベントの前に入れる
                    339:                        eventlist.insert(it, newev);
                    340:                        inserted = true;
                    341:                        break;
                    342:                }
                    343:        }
                    344:        if (inserted == false) {
                    345:                // 最後に追加
                    346:                eventlist.push_back(newev);
                    347:        }
                    348:        newev->active = true;
                    349:        evcs.unlock();
                    350: 
                    351:        gMPU->Release();
                    352: 
1.1.1.3 ! root      353:        newev->dev->putlog(3, "イベント '%s' %s %d.%03d usec 後",
1.1.1.2   root      354:                newev->GetName().c_str(),
1.1.1.3 ! root      355:                updated ? "更新" : "開始",
1.1       root      356:                (int)(newev->time / 1000),
                    357:                (int)(newev->time % 1000));
                    358: }
                    359: 
1.1.1.3 ! root      360: // 指定のイベントを停止する。
1.1       root      361: // 指定されたイベントが登録されていなければ何もしない。
                    362: void
1.1.1.3 ! root      363: Scheduler::StopEvent(Event *event)
1.1       root      364: {
                    365:        bool found = false;
                    366: 
                    367:        evcs.lock();
                    368:        for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    369:                Event *e = *it;
                    370:                if (e == event) {
                    371:                        e->active = false;
                    372:                        eventlist.erase(it);
                    373:                        found = true;
                    374:                        break;
                    375:                }
                    376:        }
                    377:        evcs.unlock();
                    378: 
1.1.1.3 ! root      379:        // イベントを停止した場合は MPU の実行中断はしなくてよい。
1.1       root      380:        // 1msec 後にイベントを追加した後、やっぱりそのイベントを取り消した
                    381:        // 場合 (LUNA の電源オフとか) はここで MPU 処理を中断するよりも
                    382:        // そのまま 1msec 走って問題ない。
                    383: 
                    384:        if (found) {
1.1.1.3 ! root      385:                event->dev->putlog(3, "イベント  '%s' 停止", event->GetName().c_str());
1.1       root      386:        }
                    387: }
                    388: 
                    389: // 経過時間 t を文字列にして返す。
                    390: static const std::string
                    391: TimeToStr(uint64 t)
                    392: {
                    393:        char buf[32];
                    394:        char *p;
                    395:        size_t len;
                    396:        int n;
                    397: 
                    398:        uint ns = t % 1000;
                    399:        t /= 1000;
                    400:        uint us = t % 1000;
                    401:        t /= 1000;
                    402:        uint ms = t % 1000;
                    403:        t /= 1000;
                    404:        uint s = t % 60;
                    405:        t /= 60;
                    406:        uint m = t % 60;
                    407:        t /= 60;
                    408:        uint h = t;
                    409: 
                    410:        p = buf;
                    411:        len = sizeof(buf);
                    412:        if (h) {
                    413:                n = snprintf(p, len, "%d:%02d:%02d", h, m, s);
                    414:                p += n;
                    415:                len -= n;
                    416:        } else if (m) {
                    417:                n = snprintf(p, len, "%d:%02d", m, s);
                    418:                p += n;
                    419:                len -= n;
                    420:        } else {
                    421:                n = snprintf(p, len, "%d", s);
                    422:                p += n;
                    423:                len -= n;
                    424:        }
                    425:        n = snprintf(p, len, ".%03d'%03d'%03d", ms, us, ns);
                    426:        p += n;
                    427:        len -= n;
                    428: 
                    429:        return std::string(buf, p - buf);
                    430: }
                    431: 
                    432: bool
                    433: Scheduler::MonitorUpdate()
                    434: {
                    435:        int x, y;
                    436: 
                    437:        monitor.Clear();
                    438:        x = 0;
                    439:        y = 0;
                    440: 
                    441:        monitor.Print(0, y++, "Mode: %s %s",
                    442:                (mode & SCHED_SYNC) ? "Sync" : "Full",
                    443:                (mode & SCHED_STOP) ? "STOP" : "Run ");
                    444:        monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag);
                    445:        monitor.Print(0, y++, "MPU Speed: %d.%03dMHz",
                    446:                (mpu_clock / 1000), (mpu_clock % 1000));
                    447:        uint64 cycle = gMPU->total_cycle();
                    448:        uint64 vt = GetVirtTime();
                    449:        uint64 rt = GetRealTime();
                    450:        uint64 relvt = vt - vtimebase;
                    451:        uint64 relrt = rt - rtimebase;
                    452:        // RealTime は実時間なのでスケジューラ開始からの時間に変換
                    453:        rt -= rtimestart;
                    454:        monitor.Print(0, y++, "Total  Real Time: %18s", TimeToStr(rt).c_str());
                    455:        monitor.Print(0, y++, "    Virtual Time: %18s", TimeToStr(vt).c_str());
                    456:        double ratio = (double)vt / rt * 100;
                    457:        monitor.Print(0, y++, "    Ratio       : %3d.%01d%%",
                    458:                (int)ratio, ((int)(ratio * 10) % 10));
                    459:        monitor.Print(0, y++, "Moment Real Time: %18s", TimeToStr(relrt).c_str());
                    460:        monitor.Print(0, y++, "    Virtual Time: %18s", TimeToStr(relvt).c_str());
                    461:        // XXX 移動平均をとる
                    462:        double relratio = (double)relvt / relrt * 100;
                    463:        monitor.Print(0, y++, "    Ratio       : %3d.%01d%%",
                    464:                (int)relratio, ((int)(relratio * 10) % 10));
                    465: 
                    466:        // 0         1         2         3         4         5         6
1.1.1.2   root      467:        // 0123456789012345678901234567890123456789012345678901234567890123456
                    468:        // Set Time        Remain Time     Description               Code
                    469:        //   3.123'456'789   3.123'456'789 0123456789012345678901234 $01234567
1.1       root      470:        x = 0;
                    471:        y++;
                    472:        monitor.Print(x, y, "Set Time");
                    473:        monitor.Print(x + 16, y, "Remain Time");
1.1.1.2   root      474:        monitor.Print(x + 32, y, "Description");
                    475:        monitor.Print(x + 58, y, "Code");
1.1       root      476:        y++;
                    477:        for (const auto& e : gEvents) {
                    478:                uint64 rem;
                    479:                uint attr;
                    480:                if (e->active) {
                    481:                        attr = 0;
                    482:                        rem = Cycle2Vtime(e->cycle - cycle);
                    483:                } else {
                    484:                        attr = TA::Disable;
                    485:                        rem = 0;
                    486:                }
                    487:                monitor.Print(x, y++, attr,
1.1.1.2   root      488:                        "%3u.%03u'%03u'%03u %3u.%03u'%03u'%03u %-25s $%08x",
1.1       root      489:                        (uint)(e->time / (1000 * 1000 * 1000)),
                    490:                        (uint)((e->time / 1000 / 1000) % 1000),
                    491:                        (uint)((e->time / 1000) % 1000),
                    492:                        (uint)(e->time % 1000),
                    493:                        (uint)(rem / (1000 * 1000 * 1000)),
                    494:                        (uint)((rem / 1000 / 1000) % 1000),
                    495:                        (uint)((rem / 1000) % 1000),
                    496:                        (uint)(rem % 1000),
1.1.1.2   root      497:                        e->GetName().c_str(),
1.1       root      498:                        e->code);
                    499:        }
                    500: 
                    501:        return true;
                    502: }

unix.superglobalmegacorp.com

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