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

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

unix.superglobalmegacorp.com

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