mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 12:34:37 +03:00
optmize start stop
This commit is contained in:
@@ -359,6 +359,7 @@ namespace NekoRay {
|
||||
|
||||
bool JsonStore::Save() {
|
||||
if (callback_before_save != nullptr) callback_before_save();
|
||||
if (save_control_no_save) return false;
|
||||
|
||||
auto save_content = ToJsonBytes();
|
||||
auto changed = last_save_content != save_content;
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace NekoRay {
|
||||
QString fn;
|
||||
bool load_control_must = false; // must load from file
|
||||
bool save_control_compact = false;
|
||||
bool save_control_no_save = false;
|
||||
QByteArray last_save_content;
|
||||
|
||||
JsonStore() = default;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace NekoRay {
|
||||
int core_port = 19810;
|
||||
int started_id = -1919;
|
||||
bool core_running = false;
|
||||
bool core_prepare_exit = false;
|
||||
bool prepare_exit = false;
|
||||
bool spmode_vpn = false;
|
||||
bool spmode_system_proxy = false;
|
||||
bool need_keep_vpn_off = false;
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace NekoRay::rpc {
|
||||
|
||||
QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) {
|
||||
libcore::ErrorResp reply;
|
||||
auto status = default_grpc_channel->Call("Start", request, &reply, 3000);
|
||||
auto status = default_grpc_channel->Call("Start", request, &reply);
|
||||
|
||||
if (status == QNetworkReply::NoError) {
|
||||
*rpcOK = true;
|
||||
@@ -234,7 +234,7 @@ namespace NekoRay::rpc {
|
||||
QString Client::Stop(bool *rpcOK) {
|
||||
libcore::EmptyReq request;
|
||||
libcore::ErrorResp reply;
|
||||
auto status = default_grpc_channel->Call("Stop", request, &reply, 3000);
|
||||
auto status = default_grpc_channel->Call("Stop", request, &reply);
|
||||
|
||||
if (status == QNetworkReply::NoError) {
|
||||
*rpcOK = true;
|
||||
|
||||
@@ -97,12 +97,12 @@ namespace NekoRay::sys {
|
||||
connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) {
|
||||
NekoRay::dataStore->core_running = state == QProcess::Running;
|
||||
|
||||
if (!dataStore->core_prepare_exit && state == QProcess::NotRunning) {
|
||||
if (!dataStore->prepare_exit && state == QProcess::NotRunning) {
|
||||
if (failed_to_start) return; // no retry
|
||||
|
||||
restart_id = NekoRay::dataStore->started_id;
|
||||
MW_dialog_message("ExternalProcess", "Crashed");
|
||||
MW_show_log("[Error] core exited, restarting.\n");
|
||||
MW_show_log("[Error] " + QObject::tr("Core exited, restarting."));
|
||||
|
||||
// Restart
|
||||
setTimeout(
|
||||
|
||||
@@ -1454,6 +1454,22 @@ End: %2</source>
|
||||
<source>Restart Proxy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>If there is no response for a long time, it is recommended to restart the software.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to start profile %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to stop, please restart the program.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Core exits too frequently, stop automatic restart this profile.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProxyItem</name>
|
||||
@@ -1586,6 +1602,10 @@ Direct: %2</source>
|
||||
<source>Subscription request fininshed: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Core exited, restarting.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>
|
||||
|
||||
@@ -1461,6 +1461,22 @@ Split by line.</source>
|
||||
<source>Restart Proxy</source>
|
||||
<translation>重启代理</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to start profile %1</source>
|
||||
<translation>启动配置失败: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to stop, please restart the program.</source>
|
||||
<translation>停止失败,请重启程序。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>If there is no response for a long time, it is recommended to restart the software.</source>
|
||||
<translation>如果长时间没有反应,建议重启软件。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Core exits too frequently, stop automatic restart this profile.</source>
|
||||
<translation>Core 退出太频繁,停止自动重启。</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProxyItem</name>
|
||||
@@ -1592,6 +1608,10 @@ Release note:
|
||||
<source>Subscription request fininshed: %1</source>
|
||||
<translation>订阅请求完成: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Core exited, restarting.</source>
|
||||
<translation>Core 退出,正在重新启动。</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
QElapsedTimer coreRestartTimer;
|
||||
|
||||
void UI_InitMainWindow() {
|
||||
mainwindow = new MainWindow;
|
||||
}
|
||||
@@ -564,6 +566,16 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info)
|
||||
if (info == "Crashed") {
|
||||
neko_stop();
|
||||
} else if (info.startsWith("CoreRestarted")) {
|
||||
if (coreRestartTimer.isValid()) {
|
||||
auto elasped = coreRestartTimer.restart();
|
||||
if (elasped < 10 * 1000) {
|
||||
coreRestartTimer = QElapsedTimer();
|
||||
show_log_impl("[Error] " + tr("Core exits too frequently, stop automatic restart this profile."));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
coreRestartTimer.start();
|
||||
}
|
||||
neko_start(info.split(",")[1].toInt());
|
||||
}
|
||||
}
|
||||
@@ -615,7 +627,6 @@ void MainWindow::on_commitDataRequest() {
|
||||
NekoRay::dataStore->splitter_state = ui->splitter->saveState().toBase64();
|
||||
//
|
||||
auto last_id = NekoRay::dataStore->started_id;
|
||||
neko_stop();
|
||||
if (NekoRay::dataStore->remember_enable && last_id >= 0) {
|
||||
NekoRay::dataStore->remember_id = last_id;
|
||||
}
|
||||
@@ -625,16 +636,32 @@ void MainWindow::on_commitDataRequest() {
|
||||
}
|
||||
|
||||
void MainWindow::on_menu_exit_triggered() {
|
||||
if (mu_exit.tryLock()) {
|
||||
NekoRay::dataStore->prepare_exit = true;
|
||||
//
|
||||
neko_set_spmode_system_proxy(false, false);
|
||||
neko_set_spmode_vpn(false, false);
|
||||
if (NekoRay::dataStore->spmode_vpn) return;
|
||||
if (NekoRay::dataStore->spmode_vpn) {
|
||||
mu_exit.unlock(); // retry
|
||||
return;
|
||||
}
|
||||
RegisterHotkey(true);
|
||||
//
|
||||
on_commitDataRequest();
|
||||
//
|
||||
NekoRay::dataStore->core_prepare_exit = true;
|
||||
NekoRay::dataStore->save_control_no_save = true; // don't change datastore after this line
|
||||
neko_stop(false, true);
|
||||
//
|
||||
hide();
|
||||
runOnNewThread([=] {
|
||||
sem_stopped.acquire();
|
||||
stop_core_daemon();
|
||||
runOnUiThread([=] {
|
||||
on_menu_exit_triggered(); // continue exit progress
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
//
|
||||
MF_release_runguard();
|
||||
if (exit_reason == 1) {
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <QProcess>
|
||||
#include <QTextDocument>
|
||||
#include <QShortcut>
|
||||
#include <QSemaphore>
|
||||
#include <QMutex>
|
||||
|
||||
#include "GroupSort.hpp"
|
||||
|
||||
@@ -49,7 +51,7 @@ public:
|
||||
|
||||
void neko_start(int _id = -1);
|
||||
|
||||
void neko_stop(bool crash = false);
|
||||
void neko_stop(bool crash = false, bool sem = false);
|
||||
|
||||
void neko_set_spmode_system_proxy(bool enable, bool save = true);
|
||||
|
||||
@@ -153,6 +155,10 @@ private:
|
||||
//
|
||||
int proxy_last_order = -1;
|
||||
bool select_mode = false;
|
||||
QMutex mu_starting;
|
||||
QMutex mu_stopping;
|
||||
QMutex mu_exit;
|
||||
QSemaphore sem_stopped;
|
||||
int exit_reason = 0;
|
||||
|
||||
QMap<int, QSharedPointer<NekoRay::ProxyEntity>> get_now_selected();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "db/ConfigBuilder.hpp"
|
||||
#include "db/TrafficLooper.hpp"
|
||||
#include "rpc/gRPC.h"
|
||||
#include "ui/widget/MessageBoxTimer.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
@@ -194,6 +195,8 @@ void MainWindow::stop_core_daemon() {
|
||||
}
|
||||
|
||||
void MainWindow::neko_start(int _id) {
|
||||
if (NekoRay::dataStore->prepare_exit) return;
|
||||
|
||||
auto ents = get_now_selected();
|
||||
auto ent = (_id < 0 && !ents.isEmpty()) ? ents.first() : NekoRay::profileManager->GetProfile(_id);
|
||||
if (ent == nullptr) return;
|
||||
@@ -214,9 +217,7 @@ void MainWindow::neko_start(int _id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NekoRay::dataStore->started_id >= 0) neko_stop();
|
||||
show_log_impl(">>>>>>>> " + tr("Starting profile %1").arg(ent->bean->DisplayTypeAndName()));
|
||||
|
||||
auto neko_start_stage2 = [=] {
|
||||
#ifndef NKR_NO_GRPC
|
||||
libcore::LoadConfigReq req;
|
||||
req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString());
|
||||
@@ -229,8 +230,8 @@ void MainWindow::neko_start(int _id) {
|
||||
bool rpcOK;
|
||||
QString error = defaultClient->Start(&rpcOK, req);
|
||||
if (rpcOK && !error.isEmpty()) {
|
||||
MessageBoxWarning("LoadConfig return error", error);
|
||||
return;
|
||||
runOnUiThread([=] { MessageBoxWarning("LoadConfig return error", error); });
|
||||
return false;
|
||||
}
|
||||
//
|
||||
NekoRay::traffic::trafficLooper->proxy = result->outboundStat.get();
|
||||
@@ -246,15 +247,52 @@ void MainWindow::neko_start(int _id) {
|
||||
|
||||
NekoRay::dataStore->UpdateStartedId(ent->id);
|
||||
running = ent;
|
||||
|
||||
runOnUiThread([=] {
|
||||
refresh_status();
|
||||
refresh_proxy_list(ent->id);
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!mu_starting.tryLock()) return;
|
||||
|
||||
// timeout message
|
||||
auto restartMsgbox = new QMessageBox(QMessageBox::Question, software_name, tr("If there is no response for a long time, it is recommended to restart the software."),
|
||||
QMessageBox::Yes | QMessageBox::No, this);
|
||||
connect(restartMsgbox, &QMessageBox::accepted, this, [=] { MW_dialog_message("", "RestartProgram"); });
|
||||
auto restartMsgboxTimer = new MessageBoxTimer(this, restartMsgbox, 5000);
|
||||
|
||||
runOnNewThread([=] {
|
||||
// stop current running
|
||||
if (NekoRay::dataStore->started_id >= 0) {
|
||||
runOnUiThread([=] { neko_stop(false, true); });
|
||||
sem_stopped.acquire();
|
||||
}
|
||||
// do start
|
||||
MW_show_log(">>>>>>>> " + tr("Starting profile %1").arg(ent->bean->DisplayTypeAndName()));
|
||||
if (!neko_start_stage2()) {
|
||||
MW_show_log("<<<<<<<< " + tr("Failed to start profile %1").arg(ent->bean->DisplayTypeAndName()));
|
||||
}
|
||||
mu_starting.unlock();
|
||||
// cancel timeout
|
||||
runOnUiThread([=] {
|
||||
restartMsgboxTimer->cancel();
|
||||
restartMsgboxTimer->deleteLater();
|
||||
restartMsgbox->deleteLater();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::neko_stop(bool crash) {
|
||||
void MainWindow::neko_stop(bool crash, bool sem) {
|
||||
auto id = NekoRay::dataStore->started_id;
|
||||
if (id < 0) return;
|
||||
show_log_impl(">>>>>>>> " + tr("Stopping profile %1").arg(running->bean->DisplayTypeAndName()));
|
||||
if (id < 0) {
|
||||
if (sem) sem_stopped.release();
|
||||
return;
|
||||
}
|
||||
|
||||
auto neko_stop_stage2 = [=] {
|
||||
while (!NekoRay::sys::running_ext.isEmpty()) {
|
||||
auto extC = NekoRay::sys::running_ext.takeFirst();
|
||||
extC->Kill();
|
||||
@@ -267,7 +305,7 @@ void MainWindow::neko_stop(bool crash) {
|
||||
NekoRay::traffic::trafficLooper->UpdateAll();
|
||||
for (const auto &item: NekoRay::traffic::trafficLooper->items) {
|
||||
NekoRay::profileManager->GetProfile(item->id)->Save();
|
||||
refresh_proxy_list(item->id);
|
||||
runOnUiThread([=] { refresh_proxy_list(item->id); });
|
||||
}
|
||||
}
|
||||
NekoRay::traffic::trafficLooper->loop_mutex.unlock();
|
||||
@@ -276,8 +314,8 @@ void MainWindow::neko_stop(bool crash) {
|
||||
bool rpcOK;
|
||||
QString error = defaultClient->Stop(&rpcOK);
|
||||
if (rpcOK && !error.isEmpty()) {
|
||||
MessageBoxWarning("Stop return error", error);
|
||||
return;
|
||||
runOnUiThread([=] { MessageBoxWarning("Stop return error", error); });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -285,8 +323,41 @@ void MainWindow::neko_stop(bool crash) {
|
||||
NekoRay::dataStore->UpdateStartedId(-1919);
|
||||
NekoRay::dataStore->need_keep_vpn_off = false;
|
||||
running = nullptr;
|
||||
|
||||
runOnUiThread([=] {
|
||||
refresh_status();
|
||||
refresh_proxy_list(id);
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!mu_stopping.tryLock()) {
|
||||
if (sem) sem_stopped.release();
|
||||
return;
|
||||
}
|
||||
|
||||
// timeout message
|
||||
auto restartMsgbox = new QMessageBox(QMessageBox::Question, software_name, tr("If there is no response for a long time, it is recommended to restart the software."),
|
||||
QMessageBox::Yes | QMessageBox::No, this);
|
||||
connect(restartMsgbox, &QMessageBox::accepted, this, [=] { MW_dialog_message("", "RestartProgram"); });
|
||||
auto restartMsgboxTimer = new MessageBoxTimer(this, restartMsgbox, 5000);
|
||||
|
||||
runOnNewThread([=] {
|
||||
// do stop
|
||||
MW_show_log(">>>>>>>> " + tr("Stopping profile %1").arg(running->bean->DisplayTypeAndName()));
|
||||
if (!neko_stop_stage2()) {
|
||||
MW_show_log("<<<<<<<< " + tr("Failed to stop, please restart the program."));
|
||||
}
|
||||
mu_stopping.unlock();
|
||||
if (sem) sem_stopped.release();
|
||||
// cancel timeout
|
||||
runOnUiThread([=] {
|
||||
restartMsgboxTimer->cancel();
|
||||
restartMsgboxTimer->deleteLater();
|
||||
restartMsgbox->deleteLater();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::CheckUpdate() {
|
||||
|
||||
33
ui/widget/MessageBoxTimer.h
Normal file
33
ui/widget/MessageBoxTimer.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
class MessageBoxTimer : public QTimer {
|
||||
public:
|
||||
QMessageBox *msgbox = nullptr;
|
||||
bool showed = false;
|
||||
|
||||
explicit MessageBoxTimer(QObject *parent, QMessageBox *msgbox, int delayMs) : QTimer(parent) {
|
||||
connect(this, &QTimer::timeout, this, &MessageBoxTimer::timeoutFunc, Qt::ConnectionType::QueuedConnection);
|
||||
this->msgbox = msgbox;
|
||||
setSingleShot(true);
|
||||
setInterval(delayMs);
|
||||
start();
|
||||
};
|
||||
|
||||
void cancel() {
|
||||
QTimer::stop();
|
||||
if (msgbox != nullptr && showed) {
|
||||
msgbox->reject(); // return the timeoutFunc
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
void timeoutFunc() {
|
||||
if (msgbox == nullptr) return;
|
||||
showed = true;
|
||||
msgbox->exec();
|
||||
msgbox = nullptr;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user