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();
|
bool Init();
|
||||||
void Exit();
|
void Exit();
|
||||||
|
void ExitSignal();
|
||||||
|
|
||||||
using OnInstallStart = std::function<bool(const char* path)>;
|
using OnInstallStart = std::function<bool(const char* path)>;
|
||||||
using OnInstallWrite = std::function<bool(const void* buf, size_t size)>;
|
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.
|
// signal for the event to close and then join the thread.
|
||||||
void nxlinkExit();
|
void nxlinkExit();
|
||||||
|
|
||||||
|
// async the exit, call this first and then call exit later to avoid blocking.
|
||||||
|
void nxlinkSignalExit();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#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 "web.hpp"
|
||||||
#include "swkbd.hpp"
|
#include "swkbd.hpp"
|
||||||
#include "fatfs.hpp"
|
#include "fatfs.hpp"
|
||||||
|
#include "utils/profile.hpp"
|
||||||
|
|
||||||
#include <nanovg_dk.h>
|
#include <nanovg_dk.h>
|
||||||
#include <minIni.h>
|
#include <minIni.h>
|
||||||
@@ -1966,6 +1967,10 @@ App::~App() {
|
|||||||
|
|
||||||
appletUnhook(&m_appletHookCookie);
|
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
|
// destroy this first as it seems to prevent a crash when exiting the appstore
|
||||||
// when an image that was being drawn is displayed
|
// when an image that was being drawn is displayed
|
||||||
// replicate: saves -> homebrew -> misc -> appstore -> sphaira -> changelog -> exit
|
// 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
|
// 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.
|
// 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?)
|
// 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();
|
i18n::exit();
|
||||||
curl::Exit();
|
|
||||||
|
|
||||||
ini_puts("config", "theme", m_theme.meta.ini_path, CONFIG_PATH);
|
{
|
||||||
CloseTheme();
|
SCOPED_TIMESTAMP("curl exit");
|
||||||
|
curl::Exit();
|
||||||
// Free any loaded sound from memory
|
|
||||||
for (auto id : m_sound_ids) {
|
|
||||||
if (id) {
|
|
||||||
plsrPlayerFree(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// De-initialize our player
|
{
|
||||||
plsrPlayerExit();
|
SCOPED_TIMESTAMP("theme exit");
|
||||||
|
ini_puts("config", "theme", m_theme.meta.ini_path, CONFIG_PATH);
|
||||||
|
CloseTheme();
|
||||||
|
}
|
||||||
|
|
||||||
nvgDeleteDk(this->vg);
|
// Free any loaded sound from memory
|
||||||
this->renderer.reset();
|
{
|
||||||
|
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
|
#ifdef USE_NVJPG
|
||||||
m_decoder.finalize();
|
m_decoder.finalize();
|
||||||
nj::finalize();
|
nj::finalize();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// backup hbmenu if it is not sphaira
|
// backup hbmenu if it is not sphaira
|
||||||
if (App::GetReplaceHbmenuEnable() && !IsHbmenu()) {
|
{
|
||||||
NacpStruct hbmenu_nacp;
|
SCOPED_TIMESTAMP("nro copy");
|
||||||
fs::FsNativeSd fs;
|
if (App::GetReplaceHbmenuEnable() && !IsHbmenu()) {
|
||||||
Result rc;
|
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")) {
|
// todo: don't read whole nacp, only the name.
|
||||||
log_write("backing up hbmenu.nro\n");
|
// todo: keep file open and use that as part of the file copy.
|
||||||
if (R_FAILED(rc = fs.copy_entire_file("/switch/hbmenu.nro", "/hbmenu.nro"))) {
|
if (R_SUCCEEDED(rc = nro_get_nacp("/hbmenu.nro", hbmenu_nacp)) && std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||||
log_write("failed to backup hbmenu.nro\n");
|
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()))) {
|
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));
|
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 {
|
} else {
|
||||||
log_write("success with copying over root file!\n");
|
log_write("success with copying over root file!\n");
|
||||||
}
|
}
|
||||||
} else if (IsHbmenu()) {
|
} else if (IsHbmenu()) {
|
||||||
// check we have a version that's newer than current.
|
// check we have a version that's newer than current.
|
||||||
NacpStruct hbmenu_nacp;
|
NacpStruct hbmenu_nacp;
|
||||||
fs::FsNativeSd fs;
|
fs::FsNativeSd fs;
|
||||||
Result rc;
|
Result rc;
|
||||||
|
|
||||||
// ensure that are still sphaira
|
// ensure that are still sphaira
|
||||||
if (R_SUCCEEDED(rc = nro_get_nacp("/hbmenu.nro", hbmenu_nacp)) && !std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
if (R_SUCCEEDED(rc = nro_get_nacp("/hbmenu.nro", hbmenu_nacp)) && !std::strcmp(hbmenu_nacp.lang[0].name, "sphaira")) {
|
||||||
NacpStruct sphaira_nacp;
|
NacpStruct sphaira_nacp;
|
||||||
fs::FsPath sphaira_path = "/switch/sphaira/sphaira.nro";
|
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);
|
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
|
// found sphaira, now lets get compare version
|
||||||
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
|
||||||
if (IsVersionNewer(hbmenu_nacp.display_version, sphaira_nacp.display_version)) {
|
if (IsVersionNewer(hbmenu_nacp.display_version, sphaira_nacp.display_version)) {
|
||||||
if (R_FAILED(rc = fs.copy_entire_file(GetExePath(), sphaira_path))) {
|
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));
|
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 {
|
} else {
|
||||||
log_write("success with updating hbmenu!\n");
|
log_write("success with updating hbmenu!\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log_write("no longer hbmenu!\n");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log_write("no longer hbmenu!\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App::GetMtpEnable()) {
|
if (App::GetMtpEnable()) {
|
||||||
log_write("closing mtp\n");
|
SCOPED_TIMESTAMP("mtp exit");
|
||||||
haze::Exit();
|
haze::Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App::GetFtpEnable()) {
|
if (App::GetFtpEnable()) {
|
||||||
log_write("closing ftp\n");
|
SCOPED_TIMESTAMP("ftp exit");
|
||||||
ftpsrv::Exit();
|
ftpsrv::Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App::GetNxlinkEnable()) {
|
if (App::GetNxlinkEnable()) {
|
||||||
log_write("closing nxlink\n");
|
SCOPED_TIMESTAMP("nxlink exit");
|
||||||
nxlinkExit();
|
nxlinkExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App::GetHddEnable()) {
|
if (App::GetHddEnable()) {
|
||||||
log_write("closing hdd\n");
|
SCOPED_TIMESTAMP("hdd exit");
|
||||||
usbHsFsExit();
|
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());
|
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>;
|
using Value = std::pair<std::string, std::string>;
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
mutexLock(&m_mutex);
|
SCOPED_MUTEX(&m_mutex);
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
|
||||||
|
|
||||||
if (m_json) {
|
if (m_json) {
|
||||||
return true;
|
return true;
|
||||||
@@ -103,13 +102,13 @@ struct Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void exit() {
|
void exit() {
|
||||||
mutexLock(&m_mutex);
|
SCOPED_MUTEX(&m_mutex);
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
|
||||||
|
|
||||||
if (!m_json) {
|
if (!m_json) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: this takes 20ms
|
||||||
if (!yyjson_mut_write_file(JSON_PATH, m_json, YYJSON_WRITE_NOFLAG, nullptr, nullptr)) {
|
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);
|
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) {
|
void get(const fs::FsPath& path, curl::Header& header) {
|
||||||
|
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||||
|
|
||||||
const auto [etag, last_modified] = get_internal(path);
|
const auto [etag, last_modified] = get_internal(path);
|
||||||
if (!etag.empty()) {
|
if (!etag.empty()) {
|
||||||
header.m_map.emplace("if-none-match", etag);
|
header.m_map.emplace("if-none-match", etag);
|
||||||
@@ -131,7 +132,6 @@ struct Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set(const fs::FsPath& path, const curl::Header& value) {
|
void set(const fs::FsPath& path, const curl::Header& value) {
|
||||||
mutexLock(&m_mutex);
|
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||||
|
|
||||||
std::string etag_str;
|
std::string etag_str;
|
||||||
|
|||||||
@@ -405,6 +405,11 @@ void Exit() {
|
|||||||
log_write("[FTP] exitied\n");
|
log_write("[FTP] exitied\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExitSignal() {
|
||||||
|
SCOPED_MUTEX(&g_mutex);
|
||||||
|
g_should_exit = true;
|
||||||
|
}
|
||||||
|
|
||||||
#if ENABLE_NETWORK_INSTALL
|
#if ENABLE_NETWORK_INSTALL
|
||||||
void InitInstallMode(const OnInstallStart& on_start, const OnInstallWrite& on_write, const OnInstallClose& on_close) {
|
void InitInstallMode(const OnInstallStart& on_start, const OnInstallWrite& on_write, const OnInstallClose& on_close) {
|
||||||
SCOPED_MUTEX(&g_shared_data.mutex);
|
SCOPED_MUTEX(&g_shared_data.mutex);
|
||||||
|
|||||||
@@ -482,4 +482,9 @@ void nxlinkExit() {
|
|||||||
threadClose(&g_thread);
|
threadClose(&g_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nxlinkSignalExit() {
|
||||||
|
std::scoped_lock lock{g_mutex};
|
||||||
|
g_quit = true;
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
Reference in New Issue
Block a user