mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-05-12 15:49:39 -06:00
core: Add async filesystem operations (#2098)
This commit is contained in:
parent
921ea178b9
commit
260f08c497
@ -17,6 +17,7 @@ foreach(KEY IN ITEMS
|
||||
"use_virtual_sd"
|
||||
"use_custom_storage"
|
||||
"compress_cia_installs"
|
||||
"async_fs_operations"
|
||||
"region_value"
|
||||
"init_clock"
|
||||
"init_time"
|
||||
|
||||
@ -18,6 +18,7 @@ object SettingKeys {
|
||||
external fun enable_required_online_lle_modules(): String
|
||||
external fun use_virtual_sd(): String
|
||||
external fun compress_cia_installs(): String
|
||||
external fun async_fs_operations(): String
|
||||
external fun region_value(): String
|
||||
external fun init_clock(): String
|
||||
external fun init_time(): String
|
||||
|
||||
@ -54,6 +54,7 @@ enum class BooleanSetting(
|
||||
USE_ARTIC_BASE_CONTROLLER(SettingKeys.use_artic_base_controller(), Settings.SECTION_CONTROLS, false),
|
||||
UPRIGHT_SCREEN(SettingKeys.upright_screen(), Settings.SECTION_LAYOUT, false),
|
||||
COMPRESS_INSTALLED_CIA_CONTENT(SettingKeys.compress_cia_installs(), Settings.SECTION_STORAGE, false),
|
||||
ASYNC_FS_OPERATIONS(SettingKeys.async_fs_operations(), Settings.SECTION_STORAGE, true),
|
||||
ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false),
|
||||
APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true),
|
||||
USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false),
|
||||
@ -92,6 +93,7 @@ enum class BooleanSetting(
|
||||
SHADERS_ACCURATE_MUL,
|
||||
USE_ARTIC_BASE_CONTROLLER,
|
||||
COMPRESS_INSTALLED_CIA_CONTENT,
|
||||
ASYNC_FS_OPERATIONS,
|
||||
ANDROID_HIDE_IMAGES,
|
||||
PERF_OVERLAY_ENABLE, // Works in overlay options, but not from the settings menu
|
||||
APPLY_REGION_FREE_PATCH
|
||||
|
||||
@ -599,6 +599,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
BooleanSetting.COMPRESS_INSTALLED_CIA_CONTENT.defaultValue
|
||||
)
|
||||
)
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ASYNC_FS_OPERATIONS,
|
||||
R.string.async_fs_operations,
|
||||
R.string.async_fs_operations_description,
|
||||
BooleanSetting.ASYNC_FS_OPERATIONS.key,
|
||||
BooleanSetting.ASYNC_FS_OPERATIONS.defaultValue
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -232,6 +232,7 @@ void Config::ReadValues() {
|
||||
|
||||
// Storage
|
||||
ReadSetting("Storage", Settings::values.compress_cia_installs);
|
||||
ReadSetting("Storage", Settings::values.async_fs_operations);
|
||||
|
||||
// Utility
|
||||
ReadSetting("Utility", Settings::values.dump_textures);
|
||||
|
||||
@ -278,6 +278,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
|
||||
# 0 (default): Do not compress, 1: Compress
|
||||
)") DECLARE_KEY(compress_cia_installs) BOOST_HANA_STRING(R"(
|
||||
|
||||
# Whether to enable async filesystem operations
|
||||
# 0: Disabled, 1 (default): Enabled
|
||||
)") DECLARE_KEY(async_fs_operations) BOOST_HANA_STRING(R"(
|
||||
|
||||
# Position of the performance overlay
|
||||
# 0: Top Left
|
||||
# 1: Center Top
|
||||
|
||||
@ -233,6 +233,8 @@
|
||||
<string name="storage">Storage</string>
|
||||
<string name="compress_cia_installs">Compress installed CIA content</string>
|
||||
<string name="compress_cia_installs_description">Compresses the content of CIA files when installed to the emulated SD card. Only affects CIA content which is installed while the setting is enabled.</string>
|
||||
<string name="async_fs_operations">Asynchronous filesystem operations</string>
|
||||
<string name="async_fs_operations_description">Makes emulated filesystem accesses asynchronous. Greatly reduces filesystem related stutter, but may slightly increase load times.</string>
|
||||
|
||||
<!-- Camera settings strings -->
|
||||
<string name="inner_camera">Inner Camera</string>
|
||||
|
||||
@ -486,6 +486,7 @@ void QtConfig::ReadDataStorageValues() {
|
||||
ReadBasicSetting(Settings::values.use_virtual_sd);
|
||||
ReadBasicSetting(Settings::values.use_custom_storage);
|
||||
ReadBasicSetting(Settings::values.compress_cia_installs);
|
||||
ReadBasicSetting(Settings::values.async_fs_operations);
|
||||
|
||||
const std::string nand_dir =
|
||||
ReadSetting(Settings::QKeys::nand_directory, QStringLiteral("")).toString().toStdString();
|
||||
@ -1079,6 +1080,7 @@ void QtConfig::SaveDataStorageValues() {
|
||||
WriteBasicSetting(Settings::values.use_virtual_sd);
|
||||
WriteBasicSetting(Settings::values.use_custom_storage);
|
||||
WriteBasicSetting(Settings::values.compress_cia_installs);
|
||||
WriteBasicSetting(Settings::values.async_fs_operations);
|
||||
WriteSetting(Settings::QKeys::nand_directory,
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
|
||||
QStringLiteral(""));
|
||||
|
||||
@ -78,6 +78,7 @@ void ConfigureStorage::SetConfiguration() {
|
||||
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue());
|
||||
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue());
|
||||
ui->toggle_compress_cia->setChecked(Settings::values.compress_cia_installs.GetValue());
|
||||
ui->async_fs_operations->setChecked(Settings::values.async_fs_operations.GetValue());
|
||||
|
||||
ui->storage_group->setEnabled(!is_powered_on);
|
||||
}
|
||||
@ -86,6 +87,7 @@ void ConfigureStorage::ApplyConfiguration() {
|
||||
Settings::values.use_virtual_sd = ui->toggle_virtual_sd->isChecked();
|
||||
Settings::values.use_custom_storage = ui->toggle_custom_storage->isChecked();
|
||||
Settings::values.compress_cia_installs = ui->toggle_compress_cia->isChecked();
|
||||
Settings::values.async_fs_operations = ui->async_fs_operations->isChecked();
|
||||
|
||||
if (!Settings::values.use_custom_storage) {
|
||||
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir,
|
||||
|
||||
@ -180,7 +180,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<layout class="QVBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_compress_cia">
|
||||
<property name="text">
|
||||
@ -191,6 +191,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="async_fs_operations">
|
||||
<property name="text">
|
||||
<string>Asynchronous filesystem operations</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Makes emulated filesystem accesses asynchronous. Greatly reduces filesystem related stutter, but may slightly increase load times.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@ -484,6 +484,7 @@ struct Values {
|
||||
Setting<bool> use_virtual_sd{true, Keys::use_virtual_sd};
|
||||
Setting<bool> use_custom_storage{false, Keys::use_custom_storage};
|
||||
Setting<bool> compress_cia_installs{false, Keys::compress_cia_installs};
|
||||
Setting<bool> async_fs_operations{true, Keys::async_fs_operations};
|
||||
|
||||
// System
|
||||
SwitchableSetting<s32> region_value{REGION_VALUE_AUTO_SELECT, Keys::region_value};
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "common/serialization/boost_small_vector.hpp"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/thread_worker.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
@ -327,6 +328,55 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as RunAsync, but runs the async operation on a specific thread worker provided by the
|
||||
* caller.
|
||||
* @param worker The thread worker where the operation will be run.
|
||||
* @param async_section Callable that takes Kernel::HLERequestContext& as argument
|
||||
* and returns the amount of nanoseconds to wait before calling result_function.
|
||||
* This callable is ran asynchronously.
|
||||
* @param result_function Callable that takes Kernel::HLERequestContext& as argument
|
||||
* and doesn't return anything. This callable is ran from the emulator thread
|
||||
* and can be used to set the IPC result.
|
||||
* @param really_async If set to false, it will call both async_section and result_function
|
||||
* from the emulator thread.
|
||||
*/
|
||||
template <typename AsyncFunctor, typename ResultFunctor>
|
||||
void RunOnThreadWorker(Common::ThreadWorker& worker, AsyncFunctor async_section,
|
||||
ResultFunctor result_function, bool really_async = true) {
|
||||
|
||||
if (!Settings::values.deterministic_async_operations && really_async) {
|
||||
kernel.ReportAsyncState(true);
|
||||
|
||||
// We use packaged_task so we can retrieve a std::future to pass to AsyncWakeUpCallback
|
||||
auto task = std::make_shared<std::packaged_task<void()>>([this, async_section] {
|
||||
s64 sleep_for = async_section(*this);
|
||||
this->thread->WakeAfterDelay(sleep_for, true);
|
||||
});
|
||||
|
||||
auto future = task->get_future();
|
||||
|
||||
worker.QueueWork([task]() { (*task)(); });
|
||||
|
||||
this->SleepClientThread("RunOnThread", std::chrono::nanoseconds(-1),
|
||||
std::make_shared<AsyncWakeUpCallback<ResultFunctor>>(
|
||||
kernel, result_function, std::move(future)));
|
||||
|
||||
} else {
|
||||
// Synchronous fallback (same logic as original)
|
||||
s64 sleep_for = async_section(*this);
|
||||
if (sleep_for > 0) {
|
||||
kernel.ReportAsyncState(true);
|
||||
auto parallel_wakeup = std::make_shared<AsyncWakeUpCallback<ResultFunctor>>(
|
||||
kernel, result_function, std::move(std::future<void>()));
|
||||
this->SleepClientThread("RunOnThread", std::chrono::nanoseconds(sleep_for),
|
||||
parallel_wakeup);
|
||||
} else {
|
||||
result_function(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a object id from the request command buffer into a pointer to an object. See the
|
||||
* "HLE handle protocol" section in the class documentation for more details.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -42,27 +42,54 @@ Directory::~Directory() {}
|
||||
|
||||
void Directory::Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 count = rp.Pop<u32>();
|
||||
auto& buffer = rp.PopMappedBuffer();
|
||||
std::vector<FileSys::Entry> entries(count);
|
||||
LOG_TRACE(Service_FS, "Read {}: count={}", GetName(), count);
|
||||
// Number of entries actually read
|
||||
u32 read = backend->Read(static_cast<u32>(entries.size()), entries.data());
|
||||
buffer.Write(entries.data(), 0, read * sizeof(FileSys::Entry));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(read);
|
||||
rb.PushMappedBuffer(buffer);
|
||||
struct AsyncData {
|
||||
// Input
|
||||
u32 count;
|
||||
|
||||
// Output
|
||||
Result ret{0};
|
||||
u32 read;
|
||||
Kernel::MappedBuffer* buffer;
|
||||
};
|
||||
|
||||
auto async_data = std::make_shared<AsyncData>();
|
||||
async_data->count = rp.Pop<u32>();
|
||||
async_data->buffer = &rp.PopMappedBuffer();
|
||||
|
||||
ctx.RunAsync(
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
std::vector<FileSys::Entry> entries(async_data->count);
|
||||
LOG_TRACE(Service_FS, "Read {}: count={}", GetName(), count);
|
||||
// Number of entries actually read
|
||||
async_data->read = backend->Read(static_cast<u32>(entries.size()), entries.data());
|
||||
async_data->buffer->Write(entries.data(), 0, async_data->read * sizeof(FileSys::Entry));
|
||||
return 0;
|
||||
},
|
||||
[async_data](Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestBuilder rb(ctx, 2, 2);
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(async_data->read);
|
||||
rb.PushMappedBuffer(*async_data->buffer);
|
||||
},
|
||||
Settings::values.async_fs_operations.GetValue());
|
||||
}
|
||||
|
||||
void Directory::Close(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
LOG_TRACE(Service_FS, "Close {}", GetName());
|
||||
backend->Close();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
ctx.RunAsync(
|
||||
[this](Kernel::HLERequestContext& ctx) {
|
||||
backend->Close();
|
||||
return 0;
|
||||
},
|
||||
[](Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestBuilder rb(ctx, 1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
},
|
||||
Settings::values.async_fs_operations.GetValue());
|
||||
}
|
||||
|
||||
} // namespace Service::FS
|
||||
|
||||
@ -75,8 +75,13 @@ void File::Read(Kernel::HLERequestContext& ctx) {
|
||||
offset, length, backend->GetSize());
|
||||
}
|
||||
|
||||
const bool allows_cache_reads = backend->AllowsCachedReads();
|
||||
|
||||
// Conventional reading if the backend does not support cache.
|
||||
if (!backend->AllowsCachedReads()) {
|
||||
// Do not use asynchronous operations on file reads, as in most cases
|
||||
// there are many of them with small sizes. This causes a lot of delay
|
||||
// due to thread communication overhead.
|
||||
if (!allows_cache_reads) {
|
||||
auto& buffer = rp.PopMappedBuffer();
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
std::unique_ptr<u8[]> data = std::make_unique_for_overwrite<u8[]>(length);
|
||||
@ -115,7 +120,8 @@ void File::Read(Kernel::HLERequestContext& ctx) {
|
||||
async_data->length = length;
|
||||
async_data->offset = offset;
|
||||
async_data->cache_ready = backend->CacheReady(offset, length);
|
||||
if (!async_data->cache_ready) {
|
||||
const bool really_async = !async_data->cache_ready;
|
||||
if (really_async) {
|
||||
async_data->pre_timer = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
@ -161,7 +167,7 @@ void File::Read(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
rb.PushMappedBuffer(*async_data->buffer);
|
||||
},
|
||||
!async_data->cache_ready);
|
||||
really_async);
|
||||
}
|
||||
|
||||
void File::Write(Kernel::HLERequestContext& ctx) {
|
||||
@ -186,6 +192,7 @@ void File::Write(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
bool flush = (flags & 0xFF) != 0, update_timestamp = (flags & 0xFF00) != 0;
|
||||
|
||||
// Do not use asynchronous fs operations here for the same reason as File::Read.
|
||||
if (!backend->AllowsCachedReads()) {
|
||||
std::vector<u8> data(length);
|
||||
buffer.Read(data.data(), 0, data.size());
|
||||
@ -274,7 +281,7 @@ void File::SetSize(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!backend->AllowsCachedReads()) {
|
||||
if (!backend->AllowsCachedReads() && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
file->size = size;
|
||||
backend->SetSize(size);
|
||||
@ -303,7 +310,7 @@ void File::Close(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "Closing File backend but {} clients still connected",
|
||||
connected_sessions.size());
|
||||
|
||||
if (!backend->AllowsCachedReads()) {
|
||||
if (!backend->AllowsCachedReads() && !Settings::values.async_fs_operations) {
|
||||
backend->Close();
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
@ -334,7 +341,7 @@ void File::Flush(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!backend->AllowsCachedReads()) {
|
||||
if (!backend->AllowsCachedReads() && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
backend->Flush();
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@ -67,7 +67,7 @@ void FS_USER::OpenFile(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service_FS, "path={}, mode={} attrs={}", file_path.DebugStr(), mode.hex, attributes);
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
const auto [file_res, open_timeout_ns] =
|
||||
archives.OpenFileFromArchive(archive_handle, file_path, mode, attributes);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
@ -100,7 +100,8 @@ void FS_USER::OpenFile(Kernel::HLERequestContext& ctx) {
|
||||
async_data->attributes = attributes;
|
||||
async_data->pre_timer = std::chrono::steady_clock::now();
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->file =
|
||||
archives.OpenFileFromArchive(async_data->archive_handle, async_data->file_path,
|
||||
@ -151,7 +152,7 @@ void FS_USER::OpenFileDirectly(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
u64 program_id = GetSessionData(ctx.Session())->program_id;
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_id)) {
|
||||
if (!archives.ArchiveIsSlow(archive_id) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
|
||||
ResultVal<ArchiveHandle> archive_handle =
|
||||
@ -203,7 +204,8 @@ void FS_USER::OpenFileDirectly(Kernel::HLERequestContext& ctx) {
|
||||
async_data->attributes = attributes;
|
||||
async_data->pre_timer = std::chrono::steady_clock::now();
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->archive_handle = archives.OpenArchive(
|
||||
async_data->archive_id, async_data->archive_path, async_data->program_id);
|
||||
@ -259,7 +261,7 @@ void FS_USER::DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "type={} size={} data={}", filename_type, filename_size,
|
||||
file_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.DeleteFileFromArchive(archive_handle, file_path));
|
||||
return;
|
||||
@ -275,7 +277,8 @@ void FS_USER::DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||
async_data->archive_handle = archive_handle;
|
||||
async_data->file_path = file_path;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res =
|
||||
archives.DeleteFileFromArchive(async_data->archive_handle, async_data->file_path);
|
||||
@ -312,7 +315,7 @@ void FS_USER::RenameFile(Kernel::HLERequestContext& ctx) {
|
||||
dest_filename_size, dest_file_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(src_archive_handle) &&
|
||||
!archives.ArchiveIsSlow(dest_archive_handle)) {
|
||||
!archives.ArchiveIsSlow(dest_archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.RenameFileBetweenArchives(src_archive_handle, src_file_path,
|
||||
dest_archive_handle, dest_file_path));
|
||||
@ -333,7 +336,8 @@ void FS_USER::RenameFile(Kernel::HLERequestContext& ctx) {
|
||||
async_data->dest_archive_handle = dest_archive_handle;
|
||||
async_data->dest_file_path = dest_file_path;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.RenameFileBetweenArchives(
|
||||
async_data->src_archive_handle, async_data->src_file_path,
|
||||
@ -362,7 +366,7 @@ void FS_USER::DeleteDirectory(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "type={} size={} data={}", dirname_type, dirname_size,
|
||||
dir_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.DeleteDirectoryFromArchive(archive_handle, dir_path));
|
||||
return;
|
||||
@ -378,7 +382,8 @@ void FS_USER::DeleteDirectory(Kernel::HLERequestContext& ctx) {
|
||||
async_data->archive_handle = archive_handle;
|
||||
async_data->dir_path = dir_path;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.DeleteDirectoryFromArchive(async_data->archive_handle,
|
||||
async_data->dir_path);
|
||||
@ -406,7 +411,7 @@ void FS_USER::DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "type={} size={} data={}", dirname_type, dirname_size,
|
||||
dir_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.DeleteDirectoryRecursivelyFromArchive(archive_handle, dir_path));
|
||||
return;
|
||||
@ -422,7 +427,8 @@ void FS_USER::DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
|
||||
async_data->archive_handle = archive_handle;
|
||||
async_data->dir_path = dir_path;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.DeleteDirectoryRecursivelyFromArchive(
|
||||
async_data->archive_handle, async_data->dir_path);
|
||||
@ -452,7 +458,7 @@ void FS_USER::CreateFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "type={} attributes={} size={:x} data={}", filename_type, attributes,
|
||||
file_size, file_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.CreateFileInArchive(archive_handle, file_path, file_size, attributes));
|
||||
return;
|
||||
@ -472,7 +478,8 @@ void FS_USER::CreateFile(Kernel::HLERequestContext& ctx) {
|
||||
async_data->file_size = file_size;
|
||||
async_data->attributes = attributes;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res =
|
||||
archives.CreateFileInArchive(async_data->archive_handle, async_data->file_path,
|
||||
@ -500,7 +507,7 @@ void FS_USER::CreateDirectory(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "type={} size={} data={}", dirname_type, dirname_size,
|
||||
dir_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.CreateDirectoryFromArchive(archive_handle, dir_path, attributes));
|
||||
return;
|
||||
@ -518,7 +525,8 @@ void FS_USER::CreateDirectory(Kernel::HLERequestContext& ctx) {
|
||||
async_data->dir_path = dir_path;
|
||||
async_data->attributes = attributes;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.CreateDirectoryFromArchive(
|
||||
async_data->archive_handle, async_data->dir_path, async_data->attributes);
|
||||
@ -554,7 +562,7 @@ void FS_USER::RenameDirectory(Kernel::HLERequestContext& ctx) {
|
||||
dest_dirname_size, dest_dir_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(src_archive_handle) &&
|
||||
!archives.ArchiveIsSlow(dest_archive_handle)) {
|
||||
!archives.ArchiveIsSlow(dest_archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path,
|
||||
dest_archive_handle, dest_dir_path));
|
||||
@ -575,7 +583,8 @@ void FS_USER::RenameDirectory(Kernel::HLERequestContext& ctx) {
|
||||
async_data->dest_archive_handle = dest_archive_handle;
|
||||
async_data->dest_dir_path = dest_dir_path;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.RenameDirectoryBetweenArchives(
|
||||
async_data->src_archive_handle, async_data->src_dir_path,
|
||||
@ -602,7 +611,7 @@ void FS_USER::OpenDirectory(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "type={} size={} data={}", dirname_type, dirname_size,
|
||||
dir_path.DebugStr());
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
ResultVal<std::shared_ptr<Directory>> dir_res =
|
||||
archives.OpenDirectoryFromArchive(archive_handle, dir_path);
|
||||
@ -630,7 +639,8 @@ void FS_USER::OpenDirectory(Kernel::HLERequestContext& ctx) {
|
||||
async_data->archive_handle = archive_handle;
|
||||
async_data->dir_path = dir_path;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->dir_res =
|
||||
archives.OpenDirectoryFromArchive(async_data->archive_handle, async_data->dir_path);
|
||||
@ -669,7 +679,7 @@ void FS_USER::OpenArchive(Kernel::HLERequestContext& ctx) {
|
||||
u64 program_id = slot->program_id;
|
||||
|
||||
// Conventional opening
|
||||
if (!archives.ArchiveIsSlow(archive_id)) {
|
||||
if (!archives.ArchiveIsSlow(archive_id) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
|
||||
const ResultVal<ArchiveHandle> handle =
|
||||
archives.OpenArchive(archive_id, archive_path, program_id);
|
||||
@ -699,7 +709,8 @@ void FS_USER::OpenArchive(Kernel::HLERequestContext& ctx) {
|
||||
async_data->archive_path = archive_path;
|
||||
async_data->program_id = program_id;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->handle = archives.OpenArchive(
|
||||
async_data->archive_id, async_data->archive_path, async_data->program_id);
|
||||
@ -727,7 +738,7 @@ void FS_USER::ControlArchive(Kernel::HLERequestContext& ctx) {
|
||||
const auto input_size = rp.Pop<u32>();
|
||||
const auto output_size = rp.Pop<u32>();
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
auto input = rp.PopMappedBuffer();
|
||||
auto output = rp.PopMappedBuffer();
|
||||
std::vector<u8> in_data(input_size);
|
||||
@ -766,7 +777,8 @@ void FS_USER::ControlArchive(Kernel::HLERequestContext& ctx) {
|
||||
async_data->in_buffer = &rp.PopMappedBuffer();
|
||||
async_data->out_buffer = &rp.PopMappedBuffer();
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
std::vector<u8> in_data(async_data->in_size);
|
||||
async_data->in_buffer->Read(in_data.data(), 0, in_data.size());
|
||||
@ -792,7 +804,7 @@ void FS_USER::CloseArchive(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto archive_handle = rp.PopRaw<ArchiveHandle>();
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(archives.CloseArchive(archive_handle));
|
||||
return;
|
||||
@ -806,7 +818,8 @@ void FS_USER::CloseArchive(Kernel::HLERequestContext& ctx) {
|
||||
auto async_data = std::make_shared<AsyncData>();
|
||||
async_data->handle = archive_handle;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.CloseArchive(async_data->handle);
|
||||
return 0;
|
||||
@ -1395,7 +1408,7 @@ void FS_USER::ObsoletedSetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
const u32 unique_id = rp.Pop<u32>();
|
||||
const u8 title_variation = rp.Pop<u8>();
|
||||
|
||||
if (!secure_value_backend->BackendIsSlow()) {
|
||||
if (!secure_value_backend->BackendIsSlow() && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(secure_value_backend->ObsoletedSetSaveDataSecureValue(unique_id, title_variation,
|
||||
secure_value_slot, value));
|
||||
@ -1416,7 +1429,8 @@ void FS_USER::ObsoletedSetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
async_data->unique_id = unique_id;
|
||||
async_data->title_variation = title_variation;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = secure_value_backend->ObsoletedSetSaveDataSecureValue(
|
||||
async_data->unique_id, async_data->title_variation, async_data->secure_value_slot,
|
||||
@ -1436,7 +1450,7 @@ void FS_USER::ObsoletedGetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
const u32 unique_id = rp.Pop<u32>();
|
||||
const u8 title_variation = rp.Pop<u8>();
|
||||
|
||||
if (!secure_value_backend->BackendIsSlow()) {
|
||||
if (!secure_value_backend->BackendIsSlow() && !Settings::values.async_fs_operations) {
|
||||
auto res = secure_value_backend->ObsoletedGetSaveDataSecureValue(unique_id, title_variation,
|
||||
secure_value_slot);
|
||||
if (res.Failed()) {
|
||||
@ -1463,7 +1477,8 @@ void FS_USER::ObsoletedGetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
async_data->unique_id = unique_id;
|
||||
async_data->title_variation = title_variation;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = secure_value_backend->ObsoletedGetSaveDataSecureValue(
|
||||
async_data->unique_id, async_data->title_variation, async_data->secure_value_slot);
|
||||
@ -1511,7 +1526,7 @@ void FS_USER::SetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
const u64 value = rp.Pop<u64>();
|
||||
|
||||
if (!secure_value_backend->BackendIsSlow()) {
|
||||
if (!secure_value_backend->BackendIsSlow() && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(secure_value_backend->SetThisSaveDataSecureValue(secure_value_slot, value));
|
||||
return;
|
||||
@ -1527,7 +1542,8 @@ void FS_USER::SetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
async_data->value = value;
|
||||
async_data->secure_value_slot = secure_value_slot;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = secure_value_backend->SetThisSaveDataSecureValue(
|
||||
async_data->secure_value_slot, async_data->value);
|
||||
@ -1544,7 +1560,7 @@ void FS_USER::GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
|
||||
if (!secure_value_backend->BackendIsSlow()) {
|
||||
if (!secure_value_backend->BackendIsSlow() && !Settings::values.async_fs_operations) {
|
||||
auto res = secure_value_backend->GetThisSaveDataSecureValue(secure_value_slot);
|
||||
|
||||
if (res.Failed()) {
|
||||
@ -1569,7 +1585,8 @@ void FS_USER::GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
auto async_data = std::make_shared<AsyncData>();
|
||||
async_data->secure_value_slot = secure_value_slot;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res =
|
||||
secure_value_backend->GetThisSaveDataSecureValue(async_data->secure_value_slot);
|
||||
@ -1599,7 +1616,7 @@ void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
const u64 value = rp.Pop<u64>();
|
||||
const bool flush = rp.Pop<bool>();
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
rb.Push(archives.SetSaveDataSecureValue(archive_handle, secure_value_slot, value, flush));
|
||||
@ -1620,7 +1637,8 @@ void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
async_data->secure_value_slot = secure_value_slot;
|
||||
async_data->flush = flush;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.SetSaveDataSecureValue(async_data->archive_handle,
|
||||
async_data->secure_value_slot,
|
||||
@ -1639,7 +1657,7 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
const auto archive_handle = rp.PopRaw<ArchiveHandle>();
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
|
||||
if (!archives.ArchiveIsSlow(archive_handle)) {
|
||||
if (!archives.ArchiveIsSlow(archive_handle) && !Settings::values.async_fs_operations) {
|
||||
auto res = archives.GetSaveDataSecureValue(archive_handle, secure_value_slot);
|
||||
if (res.Failed()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
@ -1665,7 +1683,8 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
async_data->archive_handle = archive_handle;
|
||||
async_data->secure_value_slot = secure_value_slot;
|
||||
|
||||
ctx.RunAsync(
|
||||
ctx.RunOnThreadWorker(
|
||||
fs_async_worker,
|
||||
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||
async_data->res = archives.GetSaveDataSecureValue(async_data->archive_handle,
|
||||
async_data->secure_value_slot);
|
||||
@ -1900,6 +1919,9 @@ FS_USER::FS_USER(Core::System& system)
|
||||
template <class Archive>
|
||||
void Service::FS::FS_USER::serialize(Archive& ar, const unsigned int) {
|
||||
DEBUG_SERIALIZATION_POINT;
|
||||
if (!Archive::is_loading::value) {
|
||||
fs_async_worker.WaitForRequests();
|
||||
}
|
||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||
ar & priority;
|
||||
ar & secure_value_backend;
|
||||
|
||||
@ -49,6 +49,9 @@ private:
|
||||
class FS_USER final : public ServiceFramework<FS_USER, ClientSlot> {
|
||||
public:
|
||||
explicit FS_USER(Core::System& system);
|
||||
~FS_USER() {
|
||||
fs_async_worker.WaitForRequests();
|
||||
}
|
||||
|
||||
// On real HW this is part of FSReg (FSReg:Register). But since that module is only used by
|
||||
// loader and pm, which we HLEed, we can just directly use it here
|
||||
@ -756,6 +759,8 @@ private:
|
||||
|
||||
std::shared_ptr<FileSys::SecureValueBackend> secure_value_backend;
|
||||
|
||||
Common::ThreadWorker fs_async_worker{1, "FSUSER_Worker"};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user