optmize start stop

This commit is contained in:
arm64v8a
2023-05-17 10:28:18 +09:00
parent a4b5656dc5
commit 28d2a029da
11 changed files with 254 additions and 75 deletions

View File

@@ -359,6 +359,7 @@ namespace NekoRay {
bool JsonStore::Save() { bool JsonStore::Save() {
if (callback_before_save != nullptr) callback_before_save(); if (callback_before_save != nullptr) callback_before_save();
if (save_control_no_save) return false;
auto save_content = ToJsonBytes(); auto save_content = ToJsonBytes();
auto changed = last_save_content != save_content; auto changed = last_save_content != save_content;

View File

@@ -36,6 +36,7 @@ namespace NekoRay {
QString fn; QString fn;
bool load_control_must = false; // must load from file bool load_control_must = false; // must load from file
bool save_control_compact = false; bool save_control_compact = false;
bool save_control_no_save = false;
QByteArray last_save_content; QByteArray last_save_content;
JsonStore() = default; JsonStore() = default;

View File

@@ -67,7 +67,7 @@ namespace NekoRay {
int core_port = 19810; int core_port = 19810;
int started_id = -1919; int started_id = -1919;
bool core_running = false; bool core_running = false;
bool core_prepare_exit = false; bool prepare_exit = false;
bool spmode_vpn = false; bool spmode_vpn = false;
bool spmode_system_proxy = false; bool spmode_system_proxy = false;
bool need_keep_vpn_off = false; bool need_keep_vpn_off = false;

View File

@@ -220,7 +220,7 @@ namespace NekoRay::rpc {
QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) { QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) {
libcore::ErrorResp reply; 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) { if (status == QNetworkReply::NoError) {
*rpcOK = true; *rpcOK = true;
@@ -234,7 +234,7 @@ namespace NekoRay::rpc {
QString Client::Stop(bool *rpcOK) { QString Client::Stop(bool *rpcOK) {
libcore::EmptyReq request; libcore::EmptyReq request;
libcore::ErrorResp reply; 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) { if (status == QNetworkReply::NoError) {
*rpcOK = true; *rpcOK = true;

View File

@@ -97,12 +97,12 @@ namespace NekoRay::sys {
connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) { connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) {
NekoRay::dataStore->core_running = state == QProcess::Running; 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 if (failed_to_start) return; // no retry
restart_id = NekoRay::dataStore->started_id; restart_id = NekoRay::dataStore->started_id;
MW_dialog_message("ExternalProcess", "Crashed"); MW_dialog_message("ExternalProcess", "Crashed");
MW_show_log("[Error] core exited, restarting.\n"); MW_show_log("[Error] " + QObject::tr("Core exited, restarting."));
// Restart // Restart
setTimeout( setTimeout(

View File

@@ -1454,6 +1454,22 @@ End: %2</source>
<source>Restart Proxy</source> <source>Restart Proxy</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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>
<context> <context>
<name>ProxyItem</name> <name>ProxyItem</name>
@@ -1586,6 +1602,10 @@ Direct: %2</source>
<source>Subscription request fininshed: %1</source> <source>Subscription request fininshed: %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Core exited, restarting.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name> <name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>

View File

@@ -1461,6 +1461,22 @@ Split by line.</source>
<source>Restart Proxy</source> <source>Restart Proxy</source>
<translation></translation> <translation></translation>
</message> </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>
<context> <context>
<name>ProxyItem</name> <name>ProxyItem</name>
@@ -1592,6 +1608,10 @@ Release note:
<source>Subscription request fininshed: %1</source> <source>Subscription request fininshed: %1</source>
<translation>: %1</translation> <translation>: %1</translation>
</message> </message>
<message>
<source>Core exited, restarting.</source>
<translation>Core 退</translation>
</message>
</context> </context>
<context> <context>
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name> <name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>

View File

@@ -46,6 +46,8 @@
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
QElapsedTimer coreRestartTimer;
void UI_InitMainWindow() { void UI_InitMainWindow() {
mainwindow = new MainWindow; mainwindow = new MainWindow;
} }
@@ -564,6 +566,16 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info)
if (info == "Crashed") { if (info == "Crashed") {
neko_stop(); neko_stop();
} else if (info.startsWith("CoreRestarted")) { } 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()); neko_start(info.split(",")[1].toInt());
} }
} }
@@ -615,7 +627,6 @@ void MainWindow::on_commitDataRequest() {
NekoRay::dataStore->splitter_state = ui->splitter->saveState().toBase64(); NekoRay::dataStore->splitter_state = ui->splitter->saveState().toBase64();
// //
auto last_id = NekoRay::dataStore->started_id; auto last_id = NekoRay::dataStore->started_id;
neko_stop();
if (NekoRay::dataStore->remember_enable && last_id >= 0) { if (NekoRay::dataStore->remember_enable && last_id >= 0) {
NekoRay::dataStore->remember_id = last_id; NekoRay::dataStore->remember_id = last_id;
} }
@@ -625,16 +636,32 @@ void MainWindow::on_commitDataRequest() {
} }
void MainWindow::on_menu_exit_triggered() { void MainWindow::on_menu_exit_triggered() {
neko_set_spmode_system_proxy(false, false); if (mu_exit.tryLock()) {
neko_set_spmode_vpn(false, false); NekoRay::dataStore->prepare_exit = true;
if (NekoRay::dataStore->spmode_vpn) return; //
RegisterHotkey(true); neko_set_spmode_system_proxy(false, false);
// neko_set_spmode_vpn(false, false);
on_commitDataRequest(); if (NekoRay::dataStore->spmode_vpn) {
// mu_exit.unlock(); // retry
NekoRay::dataStore->core_prepare_exit = true; return;
hide(); }
stop_core_daemon(); RegisterHotkey(true);
//
on_commitDataRequest();
//
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(); MF_release_runguard();
if (exit_reason == 1) { if (exit_reason == 1) {

View File

@@ -13,6 +13,8 @@
#include <QProcess> #include <QProcess>
#include <QTextDocument> #include <QTextDocument>
#include <QShortcut> #include <QShortcut>
#include <QSemaphore>
#include <QMutex>
#include "GroupSort.hpp" #include "GroupSort.hpp"
@@ -49,7 +51,7 @@ public:
void neko_start(int _id = -1); 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); void neko_set_spmode_system_proxy(bool enable, bool save = true);
@@ -153,6 +155,10 @@ private:
// //
int proxy_last_order = -1; int proxy_last_order = -1;
bool select_mode = false; bool select_mode = false;
QMutex mu_starting;
QMutex mu_stopping;
QMutex mu_exit;
QSemaphore sem_stopped;
int exit_reason = 0; int exit_reason = 0;
QMap<int, QSharedPointer<NekoRay::ProxyEntity>> get_now_selected(); QMap<int, QSharedPointer<NekoRay::ProxyEntity>> get_now_selected();

View File

@@ -5,6 +5,7 @@
#include "db/ConfigBuilder.hpp" #include "db/ConfigBuilder.hpp"
#include "db/TrafficLooper.hpp" #include "db/TrafficLooper.hpp"
#include "rpc/gRPC.h" #include "rpc/gRPC.h"
#include "ui/widget/MessageBoxTimer.h"
#include <QTimer> #include <QTimer>
#include <QThread> #include <QThread>
@@ -194,6 +195,8 @@ void MainWindow::stop_core_daemon() {
} }
void MainWindow::neko_start(int _id) { void MainWindow::neko_start(int _id) {
if (NekoRay::dataStore->prepare_exit) return;
auto ents = get_now_selected(); auto ents = get_now_selected();
auto ent = (_id < 0 && !ents.isEmpty()) ? ents.first() : NekoRay::profileManager->GetProfile(_id); auto ent = (_id < 0 && !ents.isEmpty()) ? ents.first() : NekoRay::profileManager->GetProfile(_id);
if (ent == nullptr) return; if (ent == nullptr) return;
@@ -214,79 +217,147 @@ void MainWindow::neko_start(int _id) {
return; return;
} }
if (NekoRay::dataStore->started_id >= 0) neko_stop(); auto neko_start_stage2 = [=] {
show_log_impl(">>>>>>>> " + tr("Starting profile %1").arg(ent->bean->DisplayTypeAndName()));
#ifndef NKR_NO_GRPC #ifndef NKR_NO_GRPC
libcore::LoadConfigReq req; libcore::LoadConfigReq req;
req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString()); req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString());
req.set_enable_nekoray_connections(NekoRay::dataStore->connection_statistics); req.set_enable_nekoray_connections(NekoRay::dataStore->connection_statistics);
if (NekoRay::dataStore->traffic_loop_interval > 0) { if (NekoRay::dataStore->traffic_loop_interval > 0) {
req.add_stats_outbounds("proxy"); req.add_stats_outbounds("proxy");
req.add_stats_outbounds("bypass"); req.add_stats_outbounds("bypass");
} }
// //
bool rpcOK; bool rpcOK;
QString error = defaultClient->Start(&rpcOK, req); QString error = defaultClient->Start(&rpcOK, req);
if (rpcOK && !error.isEmpty()) { if (rpcOK && !error.isEmpty()) {
MessageBoxWarning("LoadConfig return error", error); runOnUiThread([=] { MessageBoxWarning("LoadConfig return error", error); });
return; return false;
} }
// //
NekoRay::traffic::trafficLooper->proxy = result->outboundStat.get(); NekoRay::traffic::trafficLooper->proxy = result->outboundStat.get();
NekoRay::traffic::trafficLooper->items = result->outboundStats; NekoRay::traffic::trafficLooper->items = result->outboundStats;
NekoRay::dataStore->ignoreConnTag = result->ignoreConnTag; NekoRay::dataStore->ignoreConnTag = result->ignoreConnTag;
NekoRay::traffic::trafficLooper->loop_enabled = true; NekoRay::traffic::trafficLooper->loop_enabled = true;
#endif #endif
for (const auto &ext: result->exts) { for (const auto &ext: result->exts) {
NekoRay::sys::running_ext.push_back(ext.second); NekoRay::sys::running_ext.push_back(ext.second);
ext.second->Start(); ext.second->Start();
} }
NekoRay::dataStore->UpdateStartedId(ent->id); NekoRay::dataStore->UpdateStartedId(ent->id);
running = ent; running = ent;
refresh_status();
refresh_proxy_list(ent->id); 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; auto id = NekoRay::dataStore->started_id;
if (id < 0) return; if (id < 0) {
show_log_impl(">>>>>>>> " + tr("Stopping profile %1").arg(running->bean->DisplayTypeAndName())); if (sem) sem_stopped.release();
return;
while (!NekoRay::sys::running_ext.isEmpty()) {
auto extC = NekoRay::sys::running_ext.takeFirst();
extC->Kill();
} }
auto neko_stop_stage2 = [=] {
while (!NekoRay::sys::running_ext.isEmpty()) {
auto extC = NekoRay::sys::running_ext.takeFirst();
extC->Kill();
}
#ifndef NKR_NO_GRPC #ifndef NKR_NO_GRPC
NekoRay::traffic::trafficLooper->loop_enabled = false; NekoRay::traffic::trafficLooper->loop_enabled = false;
NekoRay::traffic::trafficLooper->loop_mutex.lock(); NekoRay::traffic::trafficLooper->loop_mutex.lock();
if (NekoRay::dataStore->traffic_loop_interval != 0) { if (NekoRay::dataStore->traffic_loop_interval != 0) {
NekoRay::traffic::trafficLooper->UpdateAll(); NekoRay::traffic::trafficLooper->UpdateAll();
for (const auto &item: NekoRay::traffic::trafficLooper->items) { for (const auto &item: NekoRay::traffic::trafficLooper->items) {
NekoRay::profileManager->GetProfile(item->id)->Save(); NekoRay::profileManager->GetProfile(item->id)->Save();
refresh_proxy_list(item->id); runOnUiThread([=] { refresh_proxy_list(item->id); });
}
} }
} NekoRay::traffic::trafficLooper->loop_mutex.unlock();
NekoRay::traffic::trafficLooper->loop_mutex.unlock();
if (!crash) { if (!crash) {
bool rpcOK; bool rpcOK;
QString error = defaultClient->Stop(&rpcOK); QString error = defaultClient->Stop(&rpcOK);
if (rpcOK && !error.isEmpty()) { if (rpcOK && !error.isEmpty()) {
MessageBoxWarning("Stop return error", error); runOnUiThread([=] { MessageBoxWarning("Stop return error", error); });
return; return false;
}
} }
}
#endif #endif
NekoRay::dataStore->UpdateStartedId(-1919); NekoRay::dataStore->UpdateStartedId(-1919);
NekoRay::dataStore->need_keep_vpn_off = false; NekoRay::dataStore->need_keep_vpn_off = false;
running = nullptr; running = nullptr;
refresh_status();
refresh_proxy_list(id); 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() { void MainWindow::CheckUpdate() {

View 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;
}
};