#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class RpcExeProcessThread : public QThread { public: explicit RpcExeProcessThread(QObject* parent = nullptr) : QThread(parent) {} ~RpcExeProcessThread() override { stopExe(); } bool startExe(const QString& exePath, const QStringList& args = QStringList()) { if (isRunning()) { return true; } exePath_ = resolvePath(exePath); args_ = args; stopping_.store(false); { QMutexLocker locker(&stateMutex_); startAttempted_ = false; startedOk_ = false; startError_.clear(); } if (!QFileInfo::exists(exePath_)) { qCritical() << "RPC server exe not found:" << exePath_; return false; } start(); QMutexLocker locker(&stateMutex_); if (!startAttempted_) { startCondition_.wait(&stateMutex_, 1500); } if (!startedOk_) { qWarning() << "RPC server process did not start:" << exePath_ << startError_; } return startedOk_; } void stopExe(int timeoutMs = 1500) { stopping_.store(true); if (isRunning() && !wait(timeoutMs)) { qWarning() << "RPC server process thread stop timeout:" << exePath_; } } QString exePath() const { return exePath_; } protected: void run() override { QProcess process; HANDLE jobHandle = nullptr; auto closeJobHandle = [&]() { if (jobHandle) { CloseHandle(jobHandle); jobHandle = nullptr; } }; auto assignProcessToJob = [&]() { closeJobHandle(); const DWORD pid = process.processId(); HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!hProcess) { qWarning() << "Failed to open RPC server process:" << exePath_; return; } jobHandle = CreateJobObject(nullptr, nullptr); if (!jobHandle) { qWarning() << "Failed to create job object"; CloseHandle(hProcess); return; } JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {}; jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; SetInformationJobObject(jobHandle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)); if (!AssignProcessToJobObject(jobHandle, hProcess)) { qWarning() << "Failed to assign process to job object"; } CloseHandle(hProcess); }; std::function startChild; startChild = [&]() { if (stopping_.load()) { quit(); return; } if (process.state() != QProcess::NotRunning) { return; } process.setProgram(exePath_); process.setArguments(args_); process.start(); const bool ok = process.waitForStarted(1000); { QMutexLocker locker(&stateMutex_); startAttempted_ = true; startedOk_ = ok; startError_ = ok ? QString() : process.errorString(); startCondition_.wakeAll(); } if (!ok) { qWarning() << "Process failed to start:" << exePath_ << process.errorString(); if (!stopping_.load()) { QTimer::singleShot(1000, &process, startChild); } return; } qDebug() << "RPC server process started:" << exePath_; assignProcessToJob(); }; QObject::connect(&process, QOverload::of(&QProcess::finished), &process, [&](int exitCode, QProcess::ExitStatus exitStatus) { qWarning() << "RPC server process finished:" << exePath_ << "exitCode:" << exitCode << "exitStatus:" << exitStatus; closeJobHandle(); if (stopping_.load()) { quit(); } else { QTimer::singleShot(1000, &process, startChild); } }); QTimer stopTimer; stopTimer.setInterval(50); QObject::connect(&stopTimer, &QTimer::timeout, &process, [&]() { if (!stopping_.load()) { return; } if (process.state() == QProcess::Running) { process.kill(); process.waitForFinished(500); } closeJobHandle(); quit(); }); stopTimer.start(); startChild(); exec(); if (process.state() == QProcess::Running) { process.kill(); process.waitForFinished(500); } closeJobHandle(); } private: QString resolvePath(const QString& exePath) const { QFileInfo exeInfo(exePath); if (!exeInfo.isRelative()) { return exePath; } QString resolved = QCoreApplication::applicationDirPath() + "/" + exePath; if (!QFileInfo::exists(resolved)) { resolved = QDir::currentPath() + "/" + exePath; } return resolved; } QString exePath_; QStringList args_; std::atomic stopping_{ true }; QMutex stateMutex_; QWaitCondition startCondition_; bool startAttempted_ = false; bool startedOk_ = false; QString startError_; };