Annotation of nono/vm/thread.cpp, revision 1.1.1.6

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2021 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // スレッドを持つデバイス
                      9: //
                     10: 
                     11: #include "thread.h"
1.1.1.3   root       12: #include "mainapp.h"
1.1       root       13: #include "mythread.h"
1.1.1.6 ! root       14: #include <cpuid.h>
        !            15: #include <time.h>
        !            16: #include <algorithm>
        !            17: 
        !            18: static void PrintSuggestion(int) __unused;
1.1       root       19: 
                     20: // コンストラクタ
1.1.1.3   root       21: ThreadDevice::ThreadDevice(uint objid_)
1.1.1.2   root       22:        : inherited(objid_)
1.1       root       23: {
1.1.1.5   root       24:        // SetName() はこのクラスでオーバーライドしているので
                     25:        // ここで(改めて)実行する必要がある。
                     26:        SetName(GetName());
1.1       root       27: }
                     28: 
                     29: // デストラクタ
                     30: ThreadDevice::~ThreadDevice()
                     31: {
                     32:        // ここで virtual の Terminate() は呼べないので、
                     33:        // 継承側のデストラクタで TerminateThread() をそれぞれ呼ぶこと。
                     34: }
                     35: 
1.1.1.5   root       36: // オブジェクト名を設定(更新)するとともにスレッド名も更新する。
                     37: // Object::SetName() のオーバーロード。
1.1       root       38: void
1.1.1.5   root       39: ThreadDevice::SetName(const std::string& newname)
                     40: {
                     41:        inherited::SetName(newname);
                     42:        SetThreadName(newname);
                     43: }
                     44: 
                     45: // スレッドにつけるスレッド名を覚えておく
                     46: // (このスレッドにスレッド名を設定する、ではない)。
                     47: void
                     48: ThreadDevice::SetThreadName(const std::string& threadname_)
1.1       root       49: {
                     50:        threadname = threadname_;
                     51: }
                     52: 
                     53: // スレッド開始
                     54: bool
                     55: ThreadDevice::StartThread()
                     56: {
1.1.1.5   root       57:        // ここまでに名前はセットしておくこと。
                     58:        assert(threadname.empty() == false);
                     59: 
1.1.1.6 ! root       60:        // スレッド起動。
1.1       root       61:        std::lock_guard<std::mutex> lock(thread_starter);
1.1.1.4   root       62:        try {
1.1.1.6 ! root       63:                thread.reset(new std::thread(&ThreadDevice::OnStart, this));
1.1.1.4   root       64:        } catch (...) { }
1.1       root       65:        if ((bool)thread == false) {
1.1.1.5   root       66:                warnx("Failed to initialize thread(%s) at %s",
                     67:                        threadname.c_str(), __method__);
1.1       root       68:                return false;
                     69:        }
                     70:        return true;
                     71: }
                     72: 
1.1.1.6 ! root       73: // 開始されたスレッドでのエントリポイント。
        !            74: void
        !            75: ThreadDevice::OnStart()
        !            76: {
        !            77:        // このスレッドにスレッド名を設定。
        !            78:        PTHREAD_SETNAME(threadname.c_str());
        !            79: 
        !            80:        // パフォーマンス計測のためのリストに追加。
        !            81:        // デバッガスレッドはなくてもいいか。
        !            82:        if (GetId() != OBJ_DEBUGGER && GetId() != OBJ_HOSTCOM_DBG) {
        !            83:                auto thman = gMainApp.GetThreadManager();
        !            84:                thman->RegistThread(this, threadname);
        !            85:        }
        !            86: 
        !            87:        // 実行開始。
        !            88:        std::lock_guard<std::mutex> lock_sub(this->thread_starter);
        !            89:        ThreadRun();
        !            90: }
        !            91: 
1.1       root       92: // スレッドに終了を要求し、その終了を待つ。
                     93: // スレッド外から呼ぶこと。
                     94: void
                     95: ThreadDevice::TerminateThread()
                     96: {
                     97:        if ((bool)thread) {
1.1.1.6 ! root       98:                auto thman = gMainApp.GetThreadManager();
        !            99:                thman->UnregistThread(this);
        !           100: 
1.1       root      101:                Terminate();
                    102: 
                    103:                if (thread->joinable()) {
                    104:                        thread->join();
                    105:                }
                    106: 
                    107:                thread.reset();
                    108:        }
                    109: }
1.1.1.3   root      110: 
1.1.1.6 ! root      111: // このスレッドのスレッドアフィニティを設定する。
1.1.1.5   root      112: /*static*/ void
                    113: ThreadBase::SetThreadAffinityHint(AffinityClass hint)
1.1.1.3   root      114: {
                    115: #if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_MICPUSET)
1.1.1.6 ! root      116:        std::lock_guard<std::mutex> lock(exlock);
1.1.1.3   root      117: 
1.1.1.6 ! root      118:        // policy, hint から cpuset を作成する。
1.1.1.3   root      119:        //
1.1.1.6 ! root      120:        // policy       hint            動作
        !           121:        // -------      -----           ----
        !           122:        // Neutral      *                       nop
        !           123:        // Binding      Light           set Lo  (if 高性能コア数 < 3)
        !           124:        // Binding      Light           nop             (if 高性能コア数 >= 3)
        !           125:        // Binding      Heavy           set Hi
        !           126: 
        !           127:        if (gMainApp.affinity_policy == MainApp::AffinityPolicy::Neutral) {
1.1.1.3   root      128:                return;
                    129:        }
                    130: 
1.1.1.6 ! root      131:        const std::vector<bool>& fastcore = gMainApp.fastcore;
        !           132: 
        !           133:        // 高性能コアが少ない場合、Light スレッドに高性能コアを奪われたくないので
        !           134:        // Light スレッドを低性能コアに縛っておきたい。
        !           135:        // 高性能コアの数が多ければ、そこまでする必要もないか。
        !           136:        if (hint == AffinityClass::Light) {
        !           137:                uint nhigh = std::count(fastcore.begin(), fastcore.end(), true);
        !           138:                if (nhigh >= 3) {
        !           139:                        return;
        !           140:                }
        !           141:        }
        !           142: 
        !           143:        // ここまで来ると、このスレッドが Heavy なら高性能コアに、
        !           144:        // Light なら低性能コアにバインドすることが確定。
1.1.1.3   root      145:        MICPUSet cset;
1.1.1.6 ! root      146:        bool target = (hint == AffinityClass::Heavy);
        !           147:        for (uint i = 0, end = fastcore.size(); i < end; i++) {
        !           148:                if (fastcore[i] == target) {
        !           149:                        cset.Set(i);
1.1.1.3   root      150:                }
                    151:        }
                    152: 
1.1.1.6 ! root      153:        // 設定。
1.1.1.3   root      154:        int r = PTHREAD_SETAFFINITY(pthread_self(), cset);
                    155: 
                    156:        if (gMainApp.hostcpu->loglevel >= 1) {
                    157:                std::string csetstr;
1.1.1.6 ! root      158:                for (uint i = 0, end = fastcore.size(); i < end; i++) {
        !           159:                        // 読みやすさのための区切り。先頭の1つは表示時に捨てる。
        !           160:                        uint n = i % 16;
        !           161:                        if (n == 0) {
        !           162:                                csetstr += '\'';
        !           163:                        }
        !           164:                        if (cset.Get(i)) {
        !           165:                                csetstr += "0123456789ABCDEF"[n];
        !           166:                        } else {
        !           167:                                csetstr += '_';
        !           168:                        }
1.1.1.3   root      169:                }
                    170:                gMainApp.hostcpu->putmsgn("SetAffinity:%-12s(%s) %s%s%s",
                    171:                        PTHREAD_GETNAME().c_str(),
                    172:                        (hint == AffinityClass::Light ? "Light" : "Heavy"),
1.1.1.6 ! root      173:                        csetstr.c_str() + 1,
1.1.1.3   root      174:                        (r == 0 ? "" : " = "),
                    175:                        (r == 0 ? "" : strerror(r)));
1.1.1.6 ! root      176: 
        !           177:                // この後のエラーメッセージより先にログスレッドが動いたら嬉しい。
        !           178:                usleep(1);
1.1.1.3   root      179:        }
                    180: 
                    181:        // エラーなら errno ではなく戻り値にエラー番号が返ってくる。
1.1.1.6 ! root      182:        // NetBSD では sysctl で許可しないと一般ユーザはこの機能を使えない。
        !           183:        if (r != 0) {
        !           184:                errno = r;
        !           185:                warn("PTHREAD_SETAFFINITY");
        !           186:                PrintSuggestion(r);
        !           187:                // 本当は終了したいが、この時点からではちょっと面倒なので放置。
        !           188:                // 以降は何もしない。
        !           189:                gMainApp.affinity_policy = MainApp::AffinityPolicy::Neutral;
1.1.1.3   root      190:        }
                    191: #else
                    192:        // なければ無視する。
                    193: #endif
                    194: }
1.1.1.6 ! root      195: 
        !           196: /*static*/ std::mutex ThreadBase::exlock;
        !           197: 
        !           198: 
        !           199: //
        !           200: // スレッドマネージャ
        !           201: //
        !           202: 
        !           203: // コンストラクタ
        !           204: ThreadManager::ThreadManager()
        !           205:        : inherited(OBJ_NONE)
        !           206: {
        !           207:        SetName("ThreadManager");
        !           208: }
        !           209: 
        !           210: // デストラクタ
        !           211: ThreadManager::~ThreadManager()
        !           212: {
        !           213: }
        !           214: 
        !           215: // 表示順のスレッド名
        !           216: static const std::vector<const char *>
        !           217: disp_order = {
        !           218:        "Main",
        !           219:        "Logger",
        !           220:        "SignalThread",
        !           221:        "VM",
        !           222:        "VirtIOBlock0",
        !           223:        "VirtIOBlock1",
        !           224:        "VirtIOBlock2",
        !           225:        "VirtIOBlock3",
        !           226:        "VirtIOBlock4",
        !           227:        "VirtIOBlock5",
        !           228:        "VirtIOBlock6",
        !           229:        "VirtIOBlock7",
        !           230:        "VirtIONet",
        !           231:        "VirtIOSCSI",
        !           232:        "VirtIOEntropy",
        !           233:        "VideoRenderer",
        !           234:        "SoundRenderer",
        !           235:        "HostNet0",
        !           236:        "HostNet1",
        !           237:        "HostCOM0",
        !           238:        "HostCOM1",
        !           239:        "HostCOM2",
        !           240:        "HostSound",
        !           241: };
        !           242: 
        !           243: // スレッドリストに登録する。
        !           244: // 登録したいスレッド自身から呼ぶこと。
        !           245: void
        !           246: ThreadManager::RegistThread(Object *obj, const std::string& name)
        !           247: {
        !           248:        // パフォーマンス計測のため自スレッドの clockid を取得。
        !           249:        clockid_t clockid;
        !           250:        pthread_getcpuclockid(pthread_self(), &clockid);
        !           251: 
        !           252:        std::lock_guard<std::mutex> lock(threads_mutex);
        !           253:        threads.emplace_back(obj, name, clockid);
        !           254:        // 都度表示順にソートする。
        !           255:        std::sort(threads.begin(), threads.end(),
        !           256:                [](const ThreadInfo& a, const ThreadInfo& b) {
        !           257:                        auto ia = std::find(disp_order.begin(), disp_order.end(), a.name);
        !           258:                        auto ib = std::find(disp_order.begin(), disp_order.end(), b.name);
        !           259:                        return ia < ib;
        !           260:                }
        !           261:        );
        !           262: }
        !           263: 
        !           264: // スレッドリストから obj を削除する。
        !           265: void
        !           266: ThreadManager::UnregistThread(Object *obj)
        !           267: {
        !           268:        std::lock_guard<std::mutex> lock(threads_mutex);
        !           269: 
        !           270:        for (auto it = threads.begin(); it != threads.end(); ++it) {
        !           271:                if ((*it).obj == obj) {
        !           272:                        threads.erase(it);
        !           273:                        break;
        !           274:                }
        !           275:        }
        !           276: }
        !           277: 
        !           278: // スレッドの CPU 利用率を記録する。
        !           279: void
        !           280: ThreadManager::CalcPerf(uint64 rtime)
        !           281: {
        !           282:        std::lock_guard<std::mutex> lock(threads_mutex);
        !           283: 
        !           284:        for (auto& info : threads) {
        !           285:                struct timespec ts;
        !           286:                clock_gettime(info.clockid, &ts);
        !           287:                uint64 t = (uint64)ts.tv_sec * 1_sec + ts.tv_nsec * 1_nsec;
        !           288:                if (__predict_true(last_rtime != 0)) {
        !           289:                        // 2回目以降
        !           290:                        uint percent = (t - info.last_clock) * 100 / (rtime - last_rtime);
        !           291:                        info.load->EnqueueForce(percent);
        !           292:                }
        !           293:                info.last_clock = t;
        !           294:        }
        !           295:        last_rtime = rtime;
        !           296: }
        !           297: 
        !           298: #define hostcpu_putmsg(lv, fmt...)     do {    \
        !           299:        if (hostcpu->loglevel >= (lv))          \
        !           300:                hostcpu->putmsgn(fmt);                  \
        !           301: } while (0)
        !           302: 
        !           303: #if defined(__x86_64__)
        !           304: // x86_64 CPU の P コア、E コアを調べて fastcore 配列に返す。
        !           305: // fastcore は事前にコア数分確保してあること。
        !           306: // 検出できれば fastcore を更新して true を返す。
        !           307: // 検出できなければ fastcore は不定で false を返す。
        !           308: // 失敗ならエラーメッセージも表示する。
        !           309: // MainApp から呼ばれるが、スレッド関連なのでこっちに置く。
        !           310: /*static*/ bool
        !           311: ThreadManager::DetectCPUAffinity_x86(std::vector<bool>& fastcore)
        !           312: {
        !           313:        Object *hostcpu = gMainApp.hostcpu.get();
        !           314: 
        !           315:        // CPUID(0x1a, 0) は Intel Core 12世代以降でハイブリッドアーキテクチャ
        !           316:        // 情報を返す。eax の bit31-24 (最上位バイト) が 0x40 なら P コア、
        !           317:        // 0x20 なら E コア。0 ならたぶんハイブリッド構成でない CPU。
        !           318:        //
        !           319:        // まずどのコアでもいいので現在のコアで実行してみる。
        !           320:        // ここでハイブリッド構成だと分かれば、個別のコアについて調べるのだが、
        !           321:        // ただしそのためのスレッドアフィニティ指定には特権が必要な場合があるので、
        !           322:        // 特権を要求する作業が必要になるかどうかを先に調べておきたいということ。
        !           323: 
        !           324: #if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_MICPUSET)
        !           325:        // まず現在のコアで。
        !           326:        uint32 eax, ebx, ecx, edx;
        !           327:        __cpuid_count(0x1a, 0, eax, ebx, ecx, edx);
        !           328: 
        !           329:        if ((eax >> 24) == 0) {
        !           330:                // CPU はハイブリッド構成ではない。
        !           331:                hostcpu_putmsg(1, "%s: Not a hybrid type core", __func__);
        !           332:                return false;
        !           333:        } else {
        !           334:                // CPU はハイブリッド構成なので、個別のコアについて調べる。
        !           335:                for (uint i = 0, end = fastcore.size(); i < end; i++) {
        !           336:                        int r;
        !           337:                        std::thread th([&] {
        !           338:                                MICPUSet cset;
        !           339:                                cset.Set(i);
        !           340:                                r = PTHREAD_SETAFFINITY(pthread_self(), cset);
        !           341:                                __cpuid_count(0x1a, 0, eax, ebx, ecx, edx);
        !           342:                                if ((eax >> 24) == 0x40) {
        !           343:                                        fastcore[i] = true;
        !           344:                                }
        !           345:                        });
        !           346:                        th.join();
        !           347: 
        !           348:                        if (r != 0) {
        !           349:                                errno = r;
        !           350:                                warn("PTHREAD_SETAFFINITY");
        !           351:                                PrintSuggestion(r);
        !           352:                                return false;
        !           353:                        }
        !           354:                        hostcpu_putmsg(1, "%s: Core%u is %c",
        !           355:                                __func__, i, (fastcore[i] ? 'P' : 'E'));
        !           356:                }
        !           357:                return true;
        !           358:        }
        !           359: #else
        !           360:        hostcpu_putmsg(1, "%s: No PTHREAD_SETAFFINITY", __func__);
        !           361:        return false;
        !           362: #endif
        !           363: }
        !           364: #endif // __x86_64__
        !           365: 
        !           366: // PTHREAD_SETAFFINITY() が EPERM になった時に表示するお知らせ。
        !           367: // 2箇所から呼ばれるので関数にしてある。
        !           368: static void
        !           369: PrintSuggestion(int errorcode)
        !           370: {
        !           371: #if defined(__NetBSD__)
        !           372:        if (errorcode == EPERM) {
        !           373:                fprintf(stderr,
        !           374:                " ** To allow non-privileged users to set thread affinity on NetBSD:\n"
        !           375:                " **  # sysctl -w security.models.extensions.user_set_cpu_affinity=1\n"
        !           376:                );
        !           377:        }
        !           378: #endif
        !           379: }

unix.superglobalmegacorp.com

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