perf: async signal exit ftpsrv and nxlink thread in order to not block. add perf logging for exit.
This commit is contained in:
@@ -6,6 +6,7 @@ namespace sphaira::ftpsrv {
|
||||
|
||||
bool Init();
|
||||
void Exit();
|
||||
void ExitSignal();
|
||||
|
||||
using OnInstallStart = std::function<bool(const char* path)>;
|
||||
using OnInstallWrite = std::function<bool(const void* buf, size_t size)>;
|
||||
|
||||
@@ -44,6 +44,9 @@ bool nxlinkInitialize(NxlinkCallback callback);
|
||||
// signal for the event to close and then join the thread.
|
||||
void nxlinkExit();
|
||||
|
||||
// async the exit, call this first and then call exit later to avoid blocking.
|
||||
void nxlinkSignalExit();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
24
sphaira/include/utils/profile.hpp
Normal file
24
sphaira/include/utils/profile.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/types.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
namespace sphaira::utils {
|
||||
|
||||
struct ScopedTimestampProfile {
|
||||
ScopedTimestampProfile(const std::string& name) : m_name{name} {
|
||||
|
||||
}
|
||||
|
||||
~ScopedTimestampProfile() {
|
||||
log_write("\t[%s] time taken: %.2fs %.2fms\n", m_name.c_str(), m_ts.GetSecondsD(), m_ts.GetMsD());
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_name;
|
||||
TimeStamp m_ts{};
|
||||
};
|
||||
|
||||
#define SCOPED_TIMESTAMP(name) sphaira::utils::ScopedTimestampProfile ANONYMOUS_VARIABLE(SCOPE_PROFILE_STATE_){name};
|
||||
|
||||
} // namespace sphaira::utils
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "web.hpp"
|
||||
#include "swkbd.hpp"
|
||||
#include "fatfs.hpp"
|
||||
#include "utils/profile.hpp"
|
||||
|
||||
#include <nanovg_dk.h>
|
||||
#include <minIni.h>
|
||||
@@ -1966,6 +1967,10 @@ App::~App() {
|
||||
|
||||
appletUnhook(&m_appletHookCookie);
|
||||
|
||||
// async exit as these threads sleep every 100ms.
|
||||
nxlinkSignalExit();
|
||||
ftpsrv::ExitSignal();
|
||||
|
||||
// destroy this first as it seems to prevent a crash when exiting the appstore
|
||||
// when an image that was being drawn is displayed
|
||||
// replicate: saves -> homebrew -> misc -> appstore -> sphaira -> changelog -> exit
|
||||
@@ -1975,109 +1980,133 @@ App::~App() {
|
||||
// this has to be called before any cleanup to ensure the lifetime of
|
||||
// nvg is still active as some widgets may need to free images.
|
||||
// clear in reverse order as the widgets are a stack (todo: just use a stack?)
|
||||
while (!m_widgets.empty()) {
|
||||
m_widgets.pop_back();
|
||||
{
|
||||
SCOPED_TIMESTAMP("widget exit");
|
||||
while (!m_widgets.empty()) {
|
||||
m_widgets.pop_back();
|
||||
}
|
||||
}
|
||||
nvgDeleteImage(vg, m_default_image);
|
||||
|
||||
i18n::exit();
|
||||
curl::Exit();
|
||||
|
||||
ini_puts("config", "theme", m_theme.meta.ini_path, CONFIG_PATH);
|
||||
CloseTheme();
|
||||
|
||||
// Free any loaded sound from memory
|
||||
for (auto id : m_sound_ids) {
|
||||
if (id) {
|
||||
plsrPlayerFree(id);
|
||||
}
|
||||
{
|
||||
SCOPED_TIMESTAMP("curl exit");
|
||||
curl::Exit();
|
||||
}
|
||||
|
||||
// De-initialize our player
|
||||
plsrPlayerExit();
|
||||
{
|
||||
SCOPED_TIMESTAMP("theme exit");
|
||||
ini_puts("config", "theme", m_theme.meta.ini_path, CONFIG_PATH);
|
||||
CloseTheme();
|
||||
}
|
||||
|
||||
nvgDeleteDk(this->vg);
|
||||
this->renderer.reset();
|
||||
// Free any loaded sound from memory
|
||||
{
|
||||
SCOPED_TIMESTAMP("plsr exit");
|
||||
// for (auto id : m_sound_ids) {
|
||||
// if (id) {
|
||||
// plsrPlayerFree(id);
|
||||
// }
|
||||
// }
|
||||
|
||||
// De-initialize our player
|
||||
plsrPlayerExit();
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TIMESTAMP("nvg exit");
|
||||
nvgDeleteImage(vg, m_default_image);
|
||||
nvgDeleteDk(this->vg);
|
||||
this->renderer.reset();
|
||||
|
||||
#ifdef USE_NVJPG
|
||||
m_decoder.finalize();
|
||||
nj::finalize();
|
||||
m_decoder.finalize();
|
||||
nj::finalize();
|
||||
#endif
|
||||
}
|
||||
|
||||
// backup hbmenu if it is not sphaira
|
||||
if (App::GetReplaceHbmenuEnable() && !IsHbmenu()) {
|
||||
NacpStruct hbmenu_nacp;
|
||||
fs::FsNativeSd fs;
|
||||
Result rc;
|
||||
{
|
||||
SCOPED_TIMESTAMP("nro copy");
|
||||
if (App::GetReplaceHbmenuEnable() && !IsHbmenu()) {
|
||||
NacpStruct hbmenu_nacp;
|
||||
fs::FsNativeSd fs;
|
||||
Result rc;
|
||||
|
||||
if (R_SUCCEEDED(rc = nro_get_nacp("/hbmenu.nro", hbmenu_nacp)) && std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||
log_write("backing up hbmenu.nro\n");
|
||||
if (R_FAILED(rc = fs.copy_entire_file("/switch/hbmenu.nro", "/hbmenu.nro"))) {
|
||||
log_write("failed to backup hbmenu.nro\n");
|
||||
// todo: don't read whole nacp, only the name.
|
||||
// todo: keep file open and use that as part of the file copy.
|
||||
if (R_SUCCEEDED(rc = nro_get_nacp("/hbmenu.nro", hbmenu_nacp)) && std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||
log_write("backing up hbmenu.nro\n");
|
||||
if (R_FAILED(rc = fs.copy_entire_file("/switch/hbmenu.nro", "/hbmenu.nro"))) {
|
||||
log_write("failed to backup hbmenu.nro\n");
|
||||
}
|
||||
} else {
|
||||
log_write("not backing up\n");
|
||||
}
|
||||
} else {
|
||||
log_write("not backing up\n");
|
||||
}
|
||||
|
||||
if (R_FAILED(rc = fs.copy_entire_file("/hbmenu.nro", GetExePath()))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", GetExePath().s, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with copying over root file!\n");
|
||||
}
|
||||
} else if (IsHbmenu()) {
|
||||
// check we have a version that's newer than current.
|
||||
NacpStruct hbmenu_nacp;
|
||||
fs::FsNativeSd fs;
|
||||
Result rc;
|
||||
if (R_FAILED(rc = fs.copy_entire_file("/hbmenu.nro", GetExePath()))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", GetExePath().s, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with copying over root file!\n");
|
||||
}
|
||||
} else if (IsHbmenu()) {
|
||||
// check we have a version that's newer than current.
|
||||
NacpStruct hbmenu_nacp;
|
||||
fs::FsNativeSd fs;
|
||||
Result rc;
|
||||
|
||||
// ensure that are still sphaira
|
||||
if (R_SUCCEEDED(rc = nro_get_nacp("/hbmenu.nro", hbmenu_nacp)) && !std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||
NacpStruct sphaira_nacp;
|
||||
fs::FsPath sphaira_path = "/switch/sphaira/sphaira.nro";
|
||||
// ensure that are still sphaira
|
||||
if (R_SUCCEEDED(rc = nro_get_nacp("/hbmenu.nro", hbmenu_nacp)) && !std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||
NacpStruct sphaira_nacp;
|
||||
fs::FsPath sphaira_path = "/switch/sphaira/sphaira.nro";
|
||||
|
||||
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
|
||||
if (R_FAILED(rc) || std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
sphaira_path = "/switch/sphaira.nro";
|
||||
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
|
||||
}
|
||||
if (R_FAILED(rc) || std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
sphaira_path = "/switch/sphaira.nro";
|
||||
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
|
||||
}
|
||||
|
||||
// found sphaira, now lets get compare version
|
||||
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
if (IsVersionNewer(hbmenu_nacp.display_version, sphaira_nacp.display_version)) {
|
||||
if (R_FAILED(rc = fs.copy_entire_file(GetExePath(), sphaira_path))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path.s, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with updating hbmenu!\n");
|
||||
// found sphaira, now lets get compare version
|
||||
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||
if (IsVersionNewer(hbmenu_nacp.display_version, sphaira_nacp.display_version)) {
|
||||
if (R_FAILED(rc = fs.copy_entire_file(GetExePath(), sphaira_path))) {
|
||||
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path.s, rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
} else {
|
||||
log_write("success with updating hbmenu!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_write("no longer hbmenu!\n");
|
||||
}
|
||||
} else {
|
||||
log_write("no longer hbmenu!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (App::GetMtpEnable()) {
|
||||
log_write("closing mtp\n");
|
||||
SCOPED_TIMESTAMP("mtp exit");
|
||||
haze::Exit();
|
||||
}
|
||||
|
||||
if (App::GetFtpEnable()) {
|
||||
log_write("closing ftp\n");
|
||||
SCOPED_TIMESTAMP("ftp exit");
|
||||
ftpsrv::Exit();
|
||||
}
|
||||
|
||||
if (App::GetNxlinkEnable()) {
|
||||
log_write("closing nxlink\n");
|
||||
SCOPED_TIMESTAMP("nxlink exit");
|
||||
nxlinkExit();
|
||||
}
|
||||
|
||||
if (App::GetHddEnable()) {
|
||||
log_write("closing hdd\n");
|
||||
SCOPED_TIMESTAMP("hdd exit");
|
||||
usbHsFsExit();
|
||||
}
|
||||
|
||||
fatfs::UnmountAll();
|
||||
romfsUnmount("Qlaunch_romfs");
|
||||
{
|
||||
SCOPED_TIMESTAMP("fatfs exit");
|
||||
fatfs::UnmountAll();
|
||||
romfsUnmount("Qlaunch_romfs");
|
||||
}
|
||||
|
||||
log_write("\t[EXIT] time taken: %.2fs %zums\n", ts.GetSecondsD(), ts.GetMs());
|
||||
|
||||
|
||||
@@ -79,8 +79,7 @@ struct Cache {
|
||||
using Value = std::pair<std::string, std::string>;
|
||||
|
||||
bool init() {
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
|
||||
if (m_json) {
|
||||
return true;
|
||||
@@ -103,13 +102,13 @@ struct Cache {
|
||||
}
|
||||
|
||||
void exit() {
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
|
||||
if (!m_json) {
|
||||
return;
|
||||
}
|
||||
|
||||
// note: this takes 20ms
|
||||
if (!yyjson_mut_write_file(JSON_PATH, m_json, YYJSON_WRITE_NOFLAG, nullptr, nullptr)) {
|
||||
log_write("failed to write etag json: %s\n", JSON_PATH.s);
|
||||
}
|
||||
@@ -120,6 +119,8 @@ struct Cache {
|
||||
}
|
||||
|
||||
void get(const fs::FsPath& path, curl::Header& header) {
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
|
||||
const auto [etag, last_modified] = get_internal(path);
|
||||
if (!etag.empty()) {
|
||||
header.m_map.emplace("if-none-match", etag);
|
||||
@@ -131,7 +132,6 @@ struct Cache {
|
||||
}
|
||||
|
||||
void set(const fs::FsPath& path, const curl::Header& value) {
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
|
||||
std::string etag_str;
|
||||
|
||||
@@ -405,6 +405,11 @@ void Exit() {
|
||||
log_write("[FTP] exitied\n");
|
||||
}
|
||||
|
||||
void ExitSignal() {
|
||||
SCOPED_MUTEX(&g_mutex);
|
||||
g_should_exit = true;
|
||||
}
|
||||
|
||||
#if ENABLE_NETWORK_INSTALL
|
||||
void InitInstallMode(const OnInstallStart& on_start, const OnInstallWrite& on_write, const OnInstallClose& on_close) {
|
||||
SCOPED_MUTEX(&g_shared_data.mutex);
|
||||
|
||||
@@ -482,4 +482,9 @@ void nxlinkExit() {
|
||||
threadClose(&g_thread);
|
||||
}
|
||||
|
||||
void nxlinkSignalExit() {
|
||||
std::scoped_lock lock{g_mutex};
|
||||
g_quit = true;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
Reference in New Issue
Block a user