mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-03-22 17:46:08 -04:00
fix: Race Condition w/ Shutdown Logic
This commit is contained in:
@@ -2144,6 +2144,13 @@ void GameList::RefreshGameDirectory() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameList::CancelPopulation() {
|
||||||
|
if (current_worker) {
|
||||||
|
current_worker->Cancel();
|
||||||
|
}
|
||||||
|
current_worker.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void GameList::ToggleFavorite(u64 program_id) {
|
void GameList::ToggleFavorite(u64 program_id) {
|
||||||
if (!UISettings::values.favorited_ids.contains(program_id)) {
|
if (!UISettings::values.favorited_ids.contains(program_id)) {
|
||||||
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
|
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
|
||||||
#include "citron/compatibility_list.h"
|
#include "citron/compatibility_list.h"
|
||||||
#include "citron/multiplayer/state.h"
|
#include "citron/multiplayer/state.h"
|
||||||
#include "citron/play_time_manager.h"
|
#include "citron/play_time_manager.h"
|
||||||
@@ -35,7 +34,6 @@
|
|||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "uisettings.h"
|
#include "uisettings.h"
|
||||||
|
|
||||||
|
|
||||||
class ControllerNavigation;
|
class ControllerNavigation;
|
||||||
class GameListWorker;
|
class GameListWorker;
|
||||||
class GameListSearchField;
|
class GameListSearchField;
|
||||||
@@ -107,6 +105,7 @@ public:
|
|||||||
|
|
||||||
void LoadCompatibilityList();
|
void LoadCompatibilityList();
|
||||||
void PopulateAsync(QVector<UISettings::GameDir>& game_dirs);
|
void PopulateAsync(QVector<UISettings::GameDir>& game_dirs);
|
||||||
|
void CancelPopulation();
|
||||||
|
|
||||||
void SaveInterfaceLayout();
|
void SaveInterfaceLayout();
|
||||||
void LoadInterfaceLayout();
|
void LoadInterfaceLayout();
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
|
||||||
#include "citron/compatibility_list.h"
|
#include "citron/compatibility_list.h"
|
||||||
#include "citron/game_list.h"
|
#include "citron/game_list.h"
|
||||||
#include "citron/game_list_p.h"
|
#include "citron/game_list_p.h"
|
||||||
@@ -40,7 +39,6 @@
|
|||||||
#include "core/file_sys/submission_package.h"
|
#include "core/file_sys/submission_package.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Structure to hold cached game metadata
|
// Structure to hold cached game metadata
|
||||||
@@ -485,6 +483,11 @@ GameListWorker::~GameListWorker() {
|
|||||||
processing_completed.Wait();
|
processing_completed.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameListWorker::Cancel() {
|
||||||
|
this->disconnect();
|
||||||
|
stop_requested.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
void GameListWorker::ProcessEvents(GameList* game_list) {
|
void GameListWorker::ProcessEvents(GameList* game_list) {
|
||||||
while (true) {
|
while (true) {
|
||||||
std::function<void(GameList*)> func;
|
std::function<void(GameList*)> func;
|
||||||
@@ -542,6 +545,10 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [slot, game] : installed_games) {
|
for (const auto& [slot, game] : installed_games) {
|
||||||
|
if (stop_requested) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (slot == ContentProviderUnionSlot::FrontendManual) {
|
if (slot == ContentProviderUnionSlot::FrontendManual) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -580,10 +587,16 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
|||||||
const std::map<u64, std::pair<int, int>>& online_stats,
|
const std::map<u64, std::pair<int, int>>& online_stats,
|
||||||
int& processed_files, int total_files) {
|
int& processed_files, int total_files) {
|
||||||
const auto callback = [this, target, parent_dir, &online_stats, &processed_files,
|
const auto callback = [this, target, parent_dir, &online_stats, &processed_files,
|
||||||
total_files](const std::filesystem::path& path) -> bool {
|
total_files](const std::filesystem::directory_entry& dir_entry) -> bool {
|
||||||
if (stop_requested)
|
if (stop_requested) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir_entry.is_directory()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& path = dir_entry.path();
|
||||||
const auto physical_name = Common::FS::PathToUTF8String(path);
|
const auto physical_name = Common::FS::PathToUTF8String(path);
|
||||||
|
|
||||||
if (physical_name.find("/nand/") != std::string::npos ||
|
if (physical_name.find("/nand/") != std::string::npos ||
|
||||||
@@ -714,7 +727,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
|||||||
|
|
||||||
if (deep_scan) {
|
if (deep_scan) {
|
||||||
Common::FS::IterateDirEntriesRecursively(dir_path, callback,
|
Common::FS::IterateDirEntriesRecursively(dir_path, callback,
|
||||||
Common::FS::DirEntryFilter::File);
|
Common::FS::DirEntryFilter::All);
|
||||||
} else {
|
} else {
|
||||||
Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File);
|
Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File);
|
||||||
}
|
}
|
||||||
@@ -745,7 +758,16 @@ void GameListWorker::run() {
|
|||||||
if (game_dir.path == "SDMC" || game_dir.path == "UserNAND" || game_dir.path == "SysNAND")
|
if (game_dir.path == "SDMC" || game_dir.path == "UserNAND" || game_dir.path == "SysNAND")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto count_callback = [&](const std::filesystem::path& path) -> bool {
|
auto count_callback = [&](const std::filesystem::directory_entry& dir_entry) -> bool {
|
||||||
|
if (stop_requested) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir_entry.is_directory()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& path = dir_entry.path();
|
||||||
const std::string physical_name = Common::FS::PathToUTF8String(path);
|
const std::string physical_name = Common::FS::PathToUTF8String(path);
|
||||||
if (HasSupportedFileExtension(physical_name)) {
|
if (HasSupportedFileExtension(physical_name)) {
|
||||||
total_files++;
|
total_files++;
|
||||||
@@ -755,7 +777,7 @@ void GameListWorker::run() {
|
|||||||
|
|
||||||
if (game_dir.deep_scan) {
|
if (game_dir.deep_scan) {
|
||||||
Common::FS::IterateDirEntriesRecursively(game_dir.path, count_callback,
|
Common::FS::IterateDirEntriesRecursively(game_dir.path, count_callback,
|
||||||
Common::FS::DirEntryFilter::File);
|
Common::FS::DirEntryFilter::All);
|
||||||
} else {
|
} else {
|
||||||
Common::FS::IterateDirEntries(game_dir.path, count_callback,
|
Common::FS::IterateDirEntries(game_dir.path, count_callback,
|
||||||
Common::FS::DirEntryFilter::File);
|
Common::FS::DirEntryFilter::File);
|
||||||
|
|||||||
@@ -6,22 +6,24 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <map> // Required for the online_stats map
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map> // Required for the online_stats map
|
|
||||||
#include <utility> // Required for std::pair
|
#include <utility> // Required for std::pair
|
||||||
|
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "common/thread.h"
|
|
||||||
#include "citron/compatibility_list.h"
|
#include "citron/compatibility_list.h"
|
||||||
#include "citron/play_time_manager.h"
|
|
||||||
#include "citron/multiplayer/state.h"
|
#include "citron/multiplayer/state.h"
|
||||||
|
#include "citron/play_time_manager.h"
|
||||||
|
#include "common/thread.h"
|
||||||
#include "network/announce_multiplayer_session.h"
|
#include "network/announce_multiplayer_session.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
@@ -62,6 +64,9 @@ public:
|
|||||||
/// Starts the processing of directory tree information.
|
/// Starts the processing of directory tree information.
|
||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
|
/// Request the worker to stop.
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Synchronously processes any events queued by the worker.
|
* Synchronously processes any events queued by the worker.
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
#include "common/x64/cpu_detect.h"
|
#include "common/x64/cpu_detect.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "audio_core/audio_core.h"
|
||||||
|
#include "audio_core/sink/sink.h"
|
||||||
#include "citron/about_dialog.h"
|
#include "citron/about_dialog.h"
|
||||||
#include "citron/bootmanager.h"
|
#include "citron/bootmanager.h"
|
||||||
#include "citron/compatdb.h"
|
#include "citron/compatdb.h"
|
||||||
@@ -2100,6 +2102,8 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP
|
|||||||
StartGameType type) {
|
StartGameType type) {
|
||||||
LOG_INFO(Frontend, "citron starting...");
|
LOG_INFO(Frontend, "citron starting...");
|
||||||
|
|
||||||
|
game_list->CancelPopulation();
|
||||||
|
|
||||||
RegisterAutoloaderContents();
|
RegisterAutoloaderContents();
|
||||||
|
|
||||||
if (params.program_id == 0 ||
|
if (params.program_id == 0 ||
|
||||||
@@ -2342,9 +2346,15 @@ void GMainWindow::OnEmulationStopped() {
|
|||||||
LOG_INFO(Frontend,
|
LOG_INFO(Frontend,
|
||||||
"Mirroring: Emulation stopped. Re-arming startup sync for next game list refresh.");
|
"Mirroring: Emulation stopped. Re-arming startup sync for next game list refresh.");
|
||||||
|
|
||||||
|
// This is necessary to stop the game list worker from accessing the filesystem.
|
||||||
|
game_list->CancelPopulation();
|
||||||
|
|
||||||
// This is necessary to reset the in-memory state for the next launch.
|
// This is necessary to reset the in-memory state for the next launch.
|
||||||
system->GetFileSystemController().CreateFactories(*vfs, true);
|
system->GetFileSystemController().CreateFactories(*vfs, true);
|
||||||
|
|
||||||
|
// Refresh the game list now that the filesystem is valid again.
|
||||||
|
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||||
|
|
||||||
discord_rpc->Update();
|
discord_rpc->Update();
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
@@ -5746,6 +5756,14 @@ void GMainWindow::UpdateVolumeUI() {
|
|||||||
volume_button->setChecked(true);
|
volume_button->setChecked(true);
|
||||||
volume_button->setText(tr("VOLUME: %1%", "Volume percentage (e.g. 50%)").arg(volume_value));
|
volume_button->setText(tr("VOLUME: %1%", "Volume percentage (e.g. 50%)").arg(volume_value));
|
||||||
}
|
}
|
||||||
|
float volume_scale = static_cast<float>(volume_value) / 100.0f;
|
||||||
|
if (Settings::values.audio_muted) {
|
||||||
|
volume_scale = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (system && system->IsPoweredOn()) {
|
||||||
|
system->AudioCore().GetOutputSink().SetSystemVolume(volume_scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateStatusButtons() {
|
void GMainWindow::UpdateStatusButtons() {
|
||||||
|
|||||||
@@ -437,18 +437,17 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
|
|||||||
|
|
||||||
std::error_code status_ec;
|
std::error_code status_ec;
|
||||||
const auto st = entry.status(status_ec);
|
const auto st = entry.status(status_ec);
|
||||||
if (status_ec) continue;
|
if (status_ec)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (True(filter & DirEntryFilter::File) &&
|
if (True(filter & DirEntryFilter::File) && st.type() == fs::file_type::regular) {
|
||||||
st.type() == fs::file_type::regular) {
|
|
||||||
if (!callback(entry)) {
|
if (!callback(entry)) {
|
||||||
callback_error = true;
|
callback_error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (True(filter & DirEntryFilter::Directory) &&
|
if (True(filter & DirEntryFilter::Directory) && st.type() == fs::file_type::directory) {
|
||||||
st.type() == fs::file_type::directory) {
|
|
||||||
if (!callback(entry)) {
|
if (!callback(entry)) {
|
||||||
callback_error = true;
|
callback_error = true;
|
||||||
break;
|
break;
|
||||||
@@ -467,14 +466,16 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
|
|||||||
PathToUTF8String(path));
|
PathToUTF8String(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IterateDirEntriesRecursivelyInternal(const std::filesystem::path& path,
|
bool IterateDirEntriesRecursivelyInternal(const std::filesystem::path& path,
|
||||||
const DirEntryCallable& callback, DirEntryFilter filter,
|
const DirEntryCallable& callback, DirEntryFilter filter,
|
||||||
int depth) {
|
int depth) {
|
||||||
if (depth > 12) return;
|
if (depth > 12)
|
||||||
|
return true;
|
||||||
|
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
auto it = fs::directory_iterator(path, ec);
|
auto it = fs::directory_iterator(path, ec);
|
||||||
if (ec) return;
|
if (ec)
|
||||||
|
return true;
|
||||||
|
|
||||||
while (it != fs::directory_iterator() && !ec) {
|
while (it != fs::directory_iterator() && !ec) {
|
||||||
const auto& entry = *it;
|
const auto& entry = *it;
|
||||||
@@ -483,8 +484,8 @@ void IterateDirEntriesRecursivelyInternal(const std::filesystem::path& path,
|
|||||||
const std::string filename = entry.path().filename().string();
|
const std::string filename = entry.path().filename().string();
|
||||||
if (filename[0] == '$' || filename == "Windows" || filename == "Program Files" ||
|
if (filename[0] == '$' || filename == "Windows" || filename == "Program Files" ||
|
||||||
filename == "Program Files (x86)" || filename == "System Volume Information" ||
|
filename == "Program Files (x86)" || filename == "System Volume Information" ||
|
||||||
filename == "ProgramData" || filename == "Application Data" ||
|
filename == "ProgramData" || filename == "Application Data" || filename == "Users" ||
|
||||||
filename == "Users" || filename == "SteamLibrary") {
|
filename == "SteamLibrary") {
|
||||||
it.increment(ec);
|
it.increment(ec);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -492,16 +493,30 @@ void IterateDirEntriesRecursivelyInternal(const std::filesystem::path& path,
|
|||||||
|
|
||||||
std::error_code status_ec;
|
std::error_code status_ec;
|
||||||
if (entry.is_directory(status_ec)) {
|
if (entry.is_directory(status_ec)) {
|
||||||
if (True(filter & DirEntryFilter::Directory)) { if (!callback(entry)) break; }
|
if (True(filter & DirEntryFilter::Directory)) {
|
||||||
IterateDirEntriesRecursivelyInternal(entry.path(), callback, filter, depth + 1);
|
if (!callback(entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!IterateDirEntriesRecursivelyInternal(entry.path(), callback, filter, depth + 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (True(filter & DirEntryFilter::File)) { if (!callback(entry)) break; }
|
if (True(filter & DirEntryFilter::File)) {
|
||||||
|
if (!callback(entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it.increment(ec);
|
it.increment(ec);
|
||||||
if (ec) { ec.clear(); break; }
|
if (ec) {
|
||||||
|
ec.clear();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
||||||
const DirEntryCallable& callback, DirEntryFilter filter) {
|
const DirEntryCallable& callback, DirEntryFilter filter) {
|
||||||
|
|||||||
@@ -400,9 +400,9 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
|||||||
const DirEntryCallable& callback,
|
const DirEntryCallable& callback,
|
||||||
DirEntryFilter filter = DirEntryFilter::All);
|
DirEntryFilter filter = DirEntryFilter::All);
|
||||||
|
|
||||||
void IterateDirEntriesRecursivelyInternal(const std::filesystem::path& path,
|
bool IterateDirEntriesRecursivelyInternal(const std::filesystem::path& path,
|
||||||
const DirEntryCallable& callback,
|
const DirEntryCallable& callback, DirEntryFilter filter,
|
||||||
DirEntryFilter filter, int depth);
|
int depth);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
template <typename Path>
|
template <typename Path>
|
||||||
|
|||||||
Reference in New Issue
Block a user