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() {
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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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(

View File

@@ -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>

View File

@@ -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>

View File

@@ -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() {
neko_set_spmode_system_proxy(false, false);
neko_set_spmode_vpn(false, false);
if (NekoRay::dataStore->spmode_vpn) return;
RegisterHotkey(true);
//
on_commitDataRequest();
//
NekoRay::dataStore->core_prepare_exit = true;
hide();
stop_core_daemon();
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) {
mu_exit.unlock(); // retry
return;
}
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();
if (exit_reason == 1) {

View File

@@ -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();

View File

@@ -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,79 +217,147 @@ 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());
req.set_enable_nekoray_connections(NekoRay::dataStore->connection_statistics);
if (NekoRay::dataStore->traffic_loop_interval > 0) {
req.add_stats_outbounds("proxy");
req.add_stats_outbounds("bypass");
}
//
bool rpcOK;
QString error = defaultClient->Start(&rpcOK, req);
if (rpcOK && !error.isEmpty()) {
MessageBoxWarning("LoadConfig return error", error);
return;
}
//
NekoRay::traffic::trafficLooper->proxy = result->outboundStat.get();
NekoRay::traffic::trafficLooper->items = result->outboundStats;
NekoRay::dataStore->ignoreConnTag = result->ignoreConnTag;
NekoRay::traffic::trafficLooper->loop_enabled = true;
libcore::LoadConfigReq req;
req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString());
req.set_enable_nekoray_connections(NekoRay::dataStore->connection_statistics);
if (NekoRay::dataStore->traffic_loop_interval > 0) {
req.add_stats_outbounds("proxy");
req.add_stats_outbounds("bypass");
}
//
bool rpcOK;
QString error = defaultClient->Start(&rpcOK, req);
if (rpcOK && !error.isEmpty()) {
runOnUiThread([=] { MessageBoxWarning("LoadConfig return error", error); });
return false;
}
//
NekoRay::traffic::trafficLooper->proxy = result->outboundStat.get();
NekoRay::traffic::trafficLooper->items = result->outboundStats;
NekoRay::dataStore->ignoreConnTag = result->ignoreConnTag;
NekoRay::traffic::trafficLooper->loop_enabled = true;
#endif
for (const auto &ext: result->exts) {
NekoRay::sys::running_ext.push_back(ext.second);
ext.second->Start();
}
for (const auto &ext: result->exts) {
NekoRay::sys::running_ext.push_back(ext.second);
ext.second->Start();
}
NekoRay::dataStore->UpdateStartedId(ent->id);
running = ent;
refresh_status();
refresh_proxy_list(ent->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()));
while (!NekoRay::sys::running_ext.isEmpty()) {
auto extC = NekoRay::sys::running_ext.takeFirst();
extC->Kill();
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();
}
#ifndef NKR_NO_GRPC
NekoRay::traffic::trafficLooper->loop_enabled = false;
NekoRay::traffic::trafficLooper->loop_mutex.lock();
if (NekoRay::dataStore->traffic_loop_interval != 0) {
NekoRay::traffic::trafficLooper->UpdateAll();
for (const auto &item: NekoRay::traffic::trafficLooper->items) {
NekoRay::profileManager->GetProfile(item->id)->Save();
refresh_proxy_list(item->id);
NekoRay::traffic::trafficLooper->loop_enabled = false;
NekoRay::traffic::trafficLooper->loop_mutex.lock();
if (NekoRay::dataStore->traffic_loop_interval != 0) {
NekoRay::traffic::trafficLooper->UpdateAll();
for (const auto &item: NekoRay::traffic::trafficLooper->items) {
NekoRay::profileManager->GetProfile(item->id)->Save();
runOnUiThread([=] { refresh_proxy_list(item->id); });
}
}
}
NekoRay::traffic::trafficLooper->loop_mutex.unlock();
NekoRay::traffic::trafficLooper->loop_mutex.unlock();
if (!crash) {
bool rpcOK;
QString error = defaultClient->Stop(&rpcOK);
if (rpcOK && !error.isEmpty()) {
MessageBoxWarning("Stop return error", error);
return;
if (!crash) {
bool rpcOK;
QString error = defaultClient->Stop(&rpcOK);
if (rpcOK && !error.isEmpty()) {
runOnUiThread([=] { MessageBoxWarning("Stop return error", error); });
return false;
}
}
}
#endif
NekoRay::dataStore->UpdateStartedId(-1919);
NekoRay::dataStore->need_keep_vpn_off = false;
running = nullptr;
refresh_status();
refresh_proxy_list(id);
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() {

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