--- nono/vm/thread.cpp 2026/04/29 17:05:09 1.1.1.1 +++ nono/vm/thread.cpp 2026/04/29 17:05:49 1.1.1.5 @@ -9,15 +9,16 @@ // #include "thread.h" +#include "mainapp.h" #include "mythread.h" // コンストラクタ -ThreadDevice::ThreadDevice(const std::string& objname_) - : inherited(objname_) +ThreadDevice::ThreadDevice(uint objid_) + : inherited(objid_) { - // スレッド名はデフォルトでこのオブジェクトと同じ名前にしておく。 - // 継承側コンストラクタ等で SetThreadName() で変更してもよい。 - SetThreadName(GetName().c_str()); + // SetName() はこのクラスでオーバーライドしているので + // ここで(改めて)実行する必要がある。 + SetName(GetName()); } // デストラクタ @@ -27,12 +28,20 @@ ThreadDevice::~ThreadDevice() // 継承側のデストラクタで TerminateThread() をそれぞれ呼ぶこと。 } -// スレッド名を設定する -// (ポインタしか管理しないので実体は呼び出し側で責任を持つこと) +// オブジェクト名を設定(更新)するとともにスレッド名も更新する。 +// Object::SetName() のオーバーロード。 void -ThreadDevice::SetThreadName(const char *threadname_) +ThreadDevice::SetName(const std::string& newname) +{ + inherited::SetName(newname); + SetThreadName(newname); +} + +// スレッドにつけるスレッド名を覚えておく +// (このスレッドにスレッド名を設定する、ではない)。 +void +ThreadDevice::SetThreadName(const std::string& threadname_) { - assert(threadname_); threadname = threadname_; } @@ -40,8 +49,11 @@ ThreadDevice::SetThreadName(const char * bool ThreadDevice::StartThread() { + // ここまでに名前はセットしておくこと。 + assert(threadname.empty() == false); + auto func = [this]() { - PTHREAD_SETNAME(threadname); + PTHREAD_SETNAME(threadname.c_str()); std::lock_guard lock_sub(this->thread_starter); this->ThreadRun(); }; @@ -49,8 +61,12 @@ ThreadDevice::StartThread() // スレッド起動 std::lock_guard lock(thread_starter); - thread.reset(new std::thread(func)); + try { + thread.reset(new std::thread(func)); + } catch (...) { } if ((bool)thread == false) { + warnx("Failed to initialize thread(%s) at %s", + threadname.c_str(), __method__); return false; } return true; @@ -71,3 +87,85 @@ ThreadDevice::TerminateThread() thread.reset(); } } + +// このスレッドアフィニティを設定する。 +/*static*/ void +ThreadBase::SetThreadAffinityHint(AffinityClass hint) +{ +#if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_MICPUSET) + auto aff = gMainApp.cpu_affinity; + if (aff == MainApp::CPUAffinity::None) { + return; + } + + // hint, aff, cpu_list から cset を作成する。 + // + // aff hint list + // ----- ---- ---- + // Low Heavy 0 - + // Low Heavy 1 - + // High Light 0 - + // High Light 1 - + // ↑このケースは CPU を固定しないので cset の収集自体不要。 + // + // Low Light 0 - + // Low Light 1 set + // High Heavy 0 - + // High Heavy 1 set + // ↑このケースは list[] が true なところを固定。 + // + // Perf Light 0 set + // Perf Light 1 - + // Perf Heavy 0 - + // Perf Heavy 1 set + // ↑このケースは (bool)hint と list[] が一致したところを固定。 + + // このスレッドは CPU を固定しない。 + if ((hint == AffinityClass::Heavy && aff == MainApp::CPUAffinity::Low ) || + (hint == AffinityClass::Light && aff == MainApp::CPUAffinity::High)) + { + return; + } + + const std::vector& cpu_list = gMainApp.cpu_list; + MICPUSet cset; + for (int i = 0, end = cpu_list.size(); i < end; i++) { + if (aff == MainApp::CPUAffinity::Perf) { + // Light,Heavy と list が一致していたらセット。 + bool hintb = (hint == AffinityClass::Heavy); + if (hintb == cpu_list[i]) { + cset.Set(i); + } + } else { + // Low,High と Light,Heavy が一致しているので立ってるほうをセット。 + if (cpu_list[i]) { + cset.Set(i); + } + } + } + + int r = PTHREAD_SETAFFINITY(pthread_self(), cset); + + if (gMainApp.hostcpu->loglevel >= 1) { + std::string csetstr; + for (int i = 0, end = cpu_list.size(); i < end; i++) { + csetstr += '0' + (uint)cset.Get(i); + } + gMainApp.hostcpu->putmsgn("SetAffinity:%-12s(%s) %s%s%s", + PTHREAD_GETNAME().c_str(), + (hint == AffinityClass::Light ? "Light" : "Heavy"), + csetstr.c_str(), + (r == 0 ? "" : " = "), + (r == 0 ? "" : strerror(r))); + } + + // エラーなら errno ではなく戻り値にエラー番号が返ってくる。 + // NetBSD では sysctl で許可しないと一般ユーザはこの機能を使えないため + // EPERM で失敗したら以降はもう何もしない。 + if (r == EPERM) { + gMainApp.cpu_affinity = MainApp::CPUAffinity::None; + } +#else + // なければ無視する。 +#endif +}