From f26dfc2306ba01fd8c668adff92f2cde88870ce2 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Mon, 5 Jul 2021 23:19:14 +0200 Subject: [PATCH] early-access version 1847 --- README.md | 2 +- src/common/CMakeLists.txt | 2 +- src/common/fs/file.cpp | 29 ++++++- src/common/fs/file.h | 11 ++- src/common/logging/backend.cpp | 21 +++--- src/common/thread_worker.h | 102 +++++++++++++++++++++++-- src/common/unique_function.h | 64 ++++++++++++++++ src/core/hle/service/mii/manager.cpp | 5 +- src/tests/CMakeLists.txt | 1 + src/tests/common/unique_function.cpp | 108 +++++++++++++++++++++++++++ 10 files changed, 321 insertions(+), 24 deletions(-) create mode 100755 src/common/unique_function.h create mode 100755 src/tests/common/unique_function.cpp diff --git a/README.md b/README.md index d67ca7fb4..a20ab038b 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1846. +This is the source code for early-access 1847. ## Legal Notice diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a6fa9a85d..e03fffd8d 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -180,7 +180,6 @@ add_library(common STATIC thread.cpp thread.h thread_queue_list.h - thread_worker.cpp thread_worker.h threadsafe_queue.h time_zone.cpp @@ -188,6 +187,7 @@ add_library(common STATIC tiny_mt.h tree.h uint128.h + unique_function.h uuid.cpp uuid.h vector_math.h diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index 077f34995..274f57659 100755 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp @@ -306,9 +306,9 @@ bool IOFile::Flush() const { errno = 0; #ifdef _WIN32 - const auto flush_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0; + const auto flush_result = std::fflush(file) == 0; #else - const auto flush_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0; + const auto flush_result = std::fflush(file) == 0; #endif if (!flush_result) { @@ -320,6 +320,28 @@ bool IOFile::Flush() const { return flush_result; } +bool IOFile::Commit() const { + if (!IsOpen()) { + return false; + } + + errno = 0; + +#ifdef _WIN32 + const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0; +#else + const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0; +#endif + + if (!commit_result) { + const auto ec = std::error_code{errno, std::generic_category()}; + LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}", + PathToUTF8String(file_path), ec.message()); + } + + return commit_result; +} + bool IOFile::SetSize(u64 size) const { if (!IsOpen()) { return false; @@ -347,6 +369,9 @@ u64 IOFile::GetSize() const { return 0; } + // Flush any unwritten buffered data into the file prior to retrieving the file size. + std::fflush(file); + std::error_code ec; const auto file_size = fs::file_size(file_path, ec); diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 588fe619d..2c4ab4332 100755 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -396,12 +396,21 @@ public: [[nodiscard]] size_t WriteString(std::span string) const; /** - * Attempts to flush any unwritten buffered data into the file and flush the file into the disk. + * Attempts to flush any unwritten buffered data into the file. * * @returns True if the flush was successful, false otherwise. */ bool Flush() const; + /** + * Attempts to commit the file into the disk. + * Note that this is an expensive operation as this forces the operating system to write + * the contents of the file associated with the file descriptor into the disk. + * + * @returns True if the commit was successful, false otherwise. + */ + bool Commit() const; + /** * Resizes the file to a given size. * If the file is resized to a smaller size, the remainder of the file is discarded. diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index b6fa4affb..61dddab3f 100755 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -171,19 +171,22 @@ FileBackend::FileBackend(const std::filesystem::path& filename) { FileBackend::~FileBackend() = default; void FileBackend::Write(const Entry& entry) { - using namespace Common::Literals; - // prevent logs from going over the maximum size (in case its spamming and the user doesn't - // know) - constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB; - constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB; - if (!file->IsOpen()) { return; } - if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) { - return; - } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) { + using namespace Common::Literals; + // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. + constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB; + constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB; + + const bool write_limit_exceeded = + bytes_written > MAX_BYTES_WRITTEN_EXTENDED || + (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); + + // Close the file after the write limit is exceeded. + if (write_limit_exceeded) { + file->Close(); return; } diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index f1859971f..ddd73220a 100755 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -7,24 +7,110 @@ #include #include #include +#include #include +#include +#include #include #include +#include "common/thread.h" +#include "common/unique_function.h" + namespace Common { -class ThreadWorker final { +template +class StatefulThreadWorker { + static constexpr bool with_state = !std::is_same_v; + + struct DummyCallable { + int operator()() const noexcept { + return 0; + } + }; + + using Task = + std::conditional_t, UniqueFunction>; + using StateMaker = std::conditional_t, DummyCallable>; + public: - explicit ThreadWorker(std::size_t num_workers, const std::string& name); - ~ThreadWorker(); - void QueueWork(std::function&& work); + explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {}) + : workers_queued{num_workers}, thread_name{std::move(name)} { + const auto lambda = [this, func](std::stop_token stop_token) { + Common::SetCurrentThreadName(thread_name.c_str()); + { + std::conditional_t state{func()}; + while (!stop_token.stop_requested()) { + Task task; + { + std::unique_lock lock{queue_mutex}; + if (requests.empty()) { + wait_condition.notify_all(); + } + condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + if (stop_token.stop_requested()) { + break; + } + task = std::move(requests.front()); + requests.pop(); + } + if constexpr (with_state) { + task(&state); + } else { + task(); + } + ++work_done; + } + } + ++workers_stopped; + wait_condition.notify_all(); + }; + threads.reserve(num_workers); + for (size_t i = 0; i < num_workers; ++i) { + threads.emplace_back(lambda); + } + } + + StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete; + StatefulThreadWorker(const StatefulThreadWorker&) = delete; + + StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete; + StatefulThreadWorker(StatefulThreadWorker&&) = delete; + + void QueueWork(Task work) { + { + std::unique_lock lock{queue_mutex}; + requests.emplace(std::move(work)); + ++work_scherduled; + } + condition.notify_one(); + } + + void WaitForRequests(std::stop_token stop_token = {}) { + std::stop_callback callback(stop_token, [this] { + for (auto& thread : threads) { + thread.request_stop(); + } + }); + std::unique_lock lock{queue_mutex}; + wait_condition.wait(lock, [this] { + return workers_stopped >= workers_queued || work_done >= work_scherduled; + }); + } private: - std::vector threads; - std::queue> requests; + std::queue requests; std::mutex queue_mutex; - std::condition_variable condition; - std::atomic_bool stop{}; + std::condition_variable_any condition; + std::condition_variable wait_condition; + std::atomic work_scherduled{}; + std::atomic work_done{}; + std::atomic workers_stopped{}; + std::atomic workers_queued{}; + std::string thread_name; + std::vector threads; }; +using ThreadWorker = StatefulThreadWorker<>; + } // namespace Common diff --git a/src/common/unique_function.h b/src/common/unique_function.h new file mode 100755 index 000000000..0a2ee9bb5 --- /dev/null +++ b/src/common/unique_function.h @@ -0,0 +1,64 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace Common { + +template +class UniqueFunction { + class CallableBase { + public: + virtual ~CallableBase() = default; + virtual ResultType operator()(Args...) = 0; + }; + + template + class Callable final : public CallableBase { + public: + Callable(Functor&& functor_) : functor{std::move(functor_)} {} + ~Callable() override = default; + + ResultType operator()(Args... args) override { + return functor(std::forward(args)...); + } + + private: + Functor functor; + }; + +public: + UniqueFunction() = default; + + template + UniqueFunction(Functor&& functor) + : callable{std::make_unique>(std::move(functor))} {} + + UniqueFunction& operator=(UniqueFunction&& rhs) noexcept { + callable = std::move(rhs.callable); + return *this; + } + + UniqueFunction(UniqueFunction&& rhs) noexcept + : callable{std::move(rhs.callable)} {} + + ResultType operator()(Args... args) const { + return (*callable)(std::forward(args)...); + } + + explicit operator bool() const noexcept { + return callable != nullptr; + } + + UniqueFunction& operator=(const UniqueFunction&) = delete; + UniqueFunction(const UniqueFunction&) = delete; + +private: + std::unique_ptr callable; +}; + +} // namespace Common diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp index 114aff31c..869d2763f 100755 --- a/src/core/hle/service/mii/manager.cpp +++ b/src/core/hle/service/mii/manager.cpp @@ -20,6 +20,7 @@ namespace { constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; +constexpr std::size_t BaseMiiCount{2}; constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; @@ -415,7 +416,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const { count += 0; } if ((source_flag & SourceFlag::Default) != SourceFlag::None) { - count += DefaultMiiCount; + count += (DefaultMiiCount - BaseMiiCount); } return static_cast(count); } @@ -445,7 +446,7 @@ ResultVal> MiiManager::GetDefault(SourceFlag source_ return MakeResult(std::move(result)); } - for (std::size_t index = 0; index < DefaultMiiCount; index++) { + for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) { result.emplace_back(BuildDefault(index), Source::Default); } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 96bc30cac..c4c012f3d 100755 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable(tests common/host_memory.cpp common/param_package.cpp common/ring_buffer.cpp + common/unique_function.cpp core/core_timing.cpp core/network/network.cpp tests.cpp diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp new file mode 100755 index 000000000..ac9912738 --- /dev/null +++ b/src/tests/common/unique_function.cpp @@ -0,0 +1,108 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include + +#include "common/unique_function.h" + +namespace { +struct Noisy { + Noisy() : state{"Default constructed"} {} + Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} { + rhs.state = "Moved away"; + } + Noisy& operator=(Noisy&& rhs) noexcept { + state = "Move assigned"; + rhs.state = "Moved away"; + } + Noisy(const Noisy&) : state{"Copied constructed"} {} + Noisy& operator=(const Noisy&) { + state = "Copied assigned"; + } + + std::string state; +}; +} // Anonymous namespace + +TEST_CASE("UniqueFunction", "[common]") { + SECTION("Capture reference") { + int value = 0; + Common::UniqueFunction func = [&value] { value = 5; }; + func(); + REQUIRE(value == 5); + } + SECTION("Capture pointer") { + int value = 0; + int* pointer = &value; + Common::UniqueFunction func = [pointer] { *pointer = 5; }; + func(); + REQUIRE(value == 5); + } + SECTION("Move object") { + Noisy noisy; + REQUIRE(noisy.state == "Default constructed"); + + Common::UniqueFunction func = [noisy = std::move(noisy)] { + REQUIRE(noisy.state == "Move constructed"); + }; + REQUIRE(noisy.state == "Moved away"); + func(); + } + SECTION("Move construct function") { + int value = 0; + Common::UniqueFunction func = [&value] { value = 5; }; + Common::UniqueFunction new_func = std::move(func); + new_func(); + REQUIRE(value == 5); + } + SECTION("Move assign function") { + int value = 0; + Common::UniqueFunction func = [&value] { value = 5; }; + Common::UniqueFunction new_func; + new_func = std::move(func); + new_func(); + REQUIRE(value == 5); + } + SECTION("Default construct then assign function") { + int value = 0; + Common::UniqueFunction func; + func = [&value] { value = 5; }; + func(); + REQUIRE(value == 5); + } + SECTION("Pass arguments") { + int result = 0; + Common::UniqueFunction func = [&result](int a, int b) { result = a + b; }; + func(5, 4); + REQUIRE(result == 9); + } + SECTION("Pass arguments and return value") { + Common::UniqueFunction func = [](int a, int b) { return a + b; }; + REQUIRE(func(5, 4) == 9); + } + SECTION("Destructor") { + int num_destroyed = 0; + struct Foo { + Foo(int* num_) : num{num_} {} + Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {} + Foo(const Foo&) = delete; + + ~Foo() { + if (num) { + ++*num; + } + } + + int* num = nullptr; + }; + Foo object{&num_destroyed}; + { + Common::UniqueFunction func = [object = std::move(object)] {}; + REQUIRE(num_destroyed == 0); + } + REQUIRE(num_destroyed == 1); + } +}